Manage where call.missed events are sent. For the payload and what your server must do, see Webhook Setup.
| Method | Path | What it does |
|---|---|---|
GET |
/v1/webhooks/subscriptions |
List subscriptions |
POST |
/v1/webhooks/subscriptions |
Create a subscription |
PATCH |
/v1/webhooks/subscriptions/:id |
Update URL, secret, events or active state |
DELETE |
/v1/webhooks/subscriptions/:id |
Delete a subscription (returns 204) |
POST |
/v1/webhooks/subscriptions/:id/test |
Send a test event |
GET |
/v1/webhooks/subscriptions/:id/deliveries |
Delivery log |
POST |
/v1/webhooks/subscriptions/:id/deliveries/:delivery_id/replay |
Replay one delivery |
POST |
/v1/webhooks/subscriptions/:id/deliveries/replay |
Replay failed deliveries in bulk |
Create a subscription#
| Body field | Type | Description |
|---|---|---|
url |
string, required | HTTPS URL that receives the POST. Private addresses are refused |
secret |
string | When set, each request carries X-Edesy-Signature: sha256=<HMAC-SHA256 of the raw body> |
event_types |
string[], required | Use ["call.missed"] |
curl -X POST https://voice-api.edesy.in/v1/webhooks/subscriptions \
-H "Authorization: Bearer vp_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://crm.example.com/webhooks/missed-call",
"secret": "a-long-random-string",
"event_types": ["call.missed"]
}'
201 Created:
{
"data": {
"id": "05c54484-f740-4103-8d98-0a0709569c9b",
"url": "https://crm.example.com/webhooks/missed-call",
"event_types": ["call.missed"],
"has_secret": true,
"is_active": true,
"created_at": "2026-10-18T07:00:00Z",
"updated_at": "2026-10-18T07:00:00Z"
}
}
A URL that is not HTTPS or points to a private address returns 400, for example { "error": "webhook url must use https" }.
Update a subscription#
PATCH with any of url, secret, event_types and is_active. Set is_active to false to pause deliveries without deleting the subscription.
Send a test#
POST /v1/webhooks/subscriptions/:id/test posts a webhook.test event to the URL and reports what your server answered. A failed test still returns 200, with the details:
{ "data": { "success": false, "status_code": 500, "error": "webhook returned status 500" } }
Delivery log#
GET /v1/webhooks/subscriptions/:id/deliveries?limit=50 returns recent deliveries, newest first (limit up to 200, default 50), including the exact JSON we sent:
{
"data": [
{
"id": "7b1e2c3d-4f5a-4b6c-8d7e-9f0a1b2c3d4e",
"subscription_id": "05c54484-f740-4103-8d98-0a0709569c9b",
"event_type": "call.missed",
"call_sid": "CA_e5353a30-5f34-4582-bb9c-830ab7b513ef",
"status": "failed",
"attempts": 6,
"max_attempts": 6,
"response_code": 0,
"error": "resolve webhook host: no such host",
"created_at": "2026-10-18T07:50:33Z"
}
]
}
Replay#
- One delivery:
POST /v1/webhooks/subscriptions/:id/deliveries/:delivery_id/replayreturns{ "data": { "replayed": 1 } }. - In bulk:
POST /v1/webhooks/subscriptions/:id/deliveries/replayresends every failed delivery in a window, up to 10,000 per call.
| Body field | Description |
|---|---|
status |
Only "failed" is supported |
since |
RFC 3339; only deliveries created after this |
until |
RFC 3339; only deliveries created before this |
Replays go to the subscription's current URL and are not charged. See Retries and Replay.