Webhook Subscriptions

Create, update, test and delete call.missed webhook subscriptions, read the delivery log and replay failed deliveries.

2 min read

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/replay returns { "data": { "replayed": 1 } }.
  • In bulk: POST /v1/webhooks/subscriptions/:id/deliveries/replay resends 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.

Was this page helpful? Suggest an edit