# Webhooks

> How the Edesy Voice API notifies your server - per-call status callbacks and workspace webhook subscriptions - and the full event list.

Source: https://edesy.in/docs/voice-api/webhooks

The Voice API pushes events to your server in two ways. A **status callback** is a URL you set per call (or per application) and it receives every state change of that call. A **webhook subscription** is a workspace-level endpoint that receives signed events such as `call.completed`, `recording.ready`, `disposition.submitted` and `call.missed`, with a delivery log, retries and replay.

## Which one to use

| | Status callback | Webhook subscription |
|---|---|---|
| Set where | `status_callback` on `POST /v1/calls`, or on an application or number | `POST /v1/webhooks/subscriptions` |
| Covers | One call, any kind | Agent-dialer campaign calls and missed-call numbers, across the workspace |
| Payload | Twilio-style PascalCase (`CallSid`, `CallStatus`, ...) | JSON envelope `{event, timestamp, data}` with snake_case fields |
| Signature | `X-Webhook-Signature` + `X-Webhook-Timestamp` | `X-Edesy-Signature`, keyed with your own secret |
| Docs | [Status Callbacks](https://edesy.in/docs/voice-api/calls/status-callbacks) | [Subscriptions](https://edesy.in/docs/voice-api/webhooks/subscriptions) |

## Event list

**Status callback events** (labelled this way in the delivery log; the payload carries `CallStatus`):

| Event | `CallStatus` |
|---|---|
| `call.initiated` | `initiated` |
| `call.ringing` | `ringing` |
| `call.answered` | `in-progress` |
| `call.completed` | `completed`, `busy`, `no-answer`, `failed` or `canceled` |

A call that does not connect is reported as `call.completed` with a failing `CallStatus`, and its timeline at `GET /v1/calls/{CallSid}/events` shows `call.failed`.

**Subscription events:**

| Event | Fires when | Scope |
|---|---|---|
| `call.answered` | A lead picks up | Agent-dialer campaigns |
| `call.completed` | The call ends, whatever the outcome | Agent-dialer campaigns |
| `recording.ready` | The call's recording is stored and playable | Agent-dialer campaigns |
| `disposition.submitted` | An agent submits a wrap-up code | Agent-dialer campaigns |
| `call.missed` | A number routed to the missed-call target declines a call | Missed-call numbers |
| `webhook.test` | You press Test, or call `POST /v1/webhooks/subscriptions/{id}/test` | Test only |

## call.missed example

```json
{
  "event": "call.missed",
  "timestamp": "2026-10-28T09:42:18Z",
  "data": {
    "call_sid": "CA_8f3c1e0b-0000-4000-8000-000000000000",
    "from": "+9198XXXXXXXX",
    "to": "+9180XXXXXXXX",
    "direction": "inbound",
    "status": "missed",
    "sip_status": 486,
    "phone_number_id": "5e229f03-0000-4000-8000-000000000000",
    "phone_number_name": "Campaign line"
  }
}
```

`from` is the caller and `to` is your number. `call_sid` is safe to de-duplicate on. Each delivered `call.missed` event costs Rs 0.30 (excluding GST); nothing is charged while no endpoint is subscribed.

## Next

- [Subscriptions](https://edesy.in/docs/voice-api/webhooks/subscriptions): create, filter, test.
- [Verify Signatures](https://edesy.in/docs/voice-api/webhooks/verify-signatures): Node.js and Python.
- [Retries and Replay](https://edesy.in/docs/voice-api/webhooks/retries-and-replay): delivery log and re-sending.
