Webhook Subscriptions

Register workspace webhook endpoints with POST /v1/webhooks/subscriptions, choose event types, set a signing secret, send a test event and read deliveries.

2 min read

A subscription is an HTTPS endpoint registered on your workspace that receives Voice API events. You choose which event_types it gets (or all), set an optional signing secret, and every delivery is logged with its response code so you can debug and replay. Manage subscriptions on the Webhooks page of voice-app.edesy.in or with the API below.

Endpoints#

Method Path Description
GET /v1/webhooks/subscriptions List
POST /v1/webhooks/subscriptions Create
PATCH /v1/webhooks/subscriptions/{id} Update URL, secret, event types or active flag
DELETE /v1/webhooks/subscriptions/{id} Delete
POST /v1/webhooks/subscriptions/{id}/test Send a webhook.test event and return your endpoint's status code
GET /v1/webhooks/subscriptions/{id}/deliveries Delivery log (limit, default 50)
POST /v1/webhooks/subscriptions/{id}/deliveries/{delivery_id}/replay Re-send one delivery
POST /v1/webhooks/subscriptions/{id}/deliveries/replay Re-send many. See Retries and Replay

Create#

curl -X POST https://voice-api.edesy.in/v1/webhooks/subscriptions \
  -H "Authorization: Bearer $EDESY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://crm.example.com/edesy/events",
    "secret": "a-long-random-string",
    "event_types": ["call.completed", "disposition.submitted", "recording.ready"]
  }'
Field Type Required Description
url string Required Public HTTPS endpoint
secret string Optional Signs every delivery with X-Edesy-Signature. Strongly recommended
event_types array Optional Events to receive. Empty or omitted means all
{
  "data": {
    "id": "0b9f5d2c-0000-4000-8000-000000000000",
    "url": "https://crm.example.com/edesy/events",
    "event_types": ["call.completed", "disposition.submitted", "recording.ready"],
    "has_secret": true,
    "is_active": true,
    "created_at": "2026-10-28T09:00:00Z",
    "updated_at": "2026-10-28T09:00:00Z"
  }
}

The secret is never returned; has_secret tells you one is set. To rotate it, PATCH a new secret and accept both old and new signatures on your side for a short overlap.

Valid event types: call.answered, call.completed, recording.ready, disposition.submitted, call.missed. Unknown types are rejected so a subscription cannot wait for an event that never fires.

Envelope#

Every delivery is a JSON POST:

{
  "event": "call.completed",
  "timestamp": "2026-10-28T11:19:02Z",
  "data": {
    "call_sid": "CA_6f1c2d3e-0000-4000-8000-000000000000",
    "external_id": "crm-lead-77812",
    "campaign_id": "...",
    "contact_id": "...",
    "to": "+9198XXXXXXXX",
    "from": "+9180XXXXXXXX",
    "direction": "outbound",
    "status": "completed",
    "duration_sec": 142,
    "sip_status": 200,
    "answered_by": "human",
    "custom_fields": { "city": "Pune" }
  }
}

data has one shape for every event; each event fills the fields that apply:

Field Description
call_sid The call
external_id, custom_fields Your CRM ID and fields from the lead
campaign_id, contact_id Edesy campaign and contact
to, from, direction Call details
phone_number_id, phone_number_name Your number (call.missed)
status completed, busy, no-answer, failed (call.completed), answered, or missed
duration_sec, sip_status, sip_reason Call result
answered_by human, machine or unknown when AMD ran
agent id, number, name
disposition id, name, is_terminal, notes
recording_url Playable recording (recording.ready)

Responding#

Reply with any 2xx within 5 seconds. Do the slow work (CRM writes) after responding, from a queue. Anything else, including a timeout, is retried. See Retries and Replay.

Was this page helpful?