# Webhook Setup

> Receive every missed call in real time as a call.missed JSON POST - payload, headers, HTTPS rules, the 5-second reply window and the test button.

Source: https://edesy.in/docs/missed-call/guides/webhook-setup

A webhook sends each missed call to your URL as it happens, as one JSON `POST` per call.

## 1. Add your URL

In the portal open **Webhook**, paste your URL, optionally add a secret, and save. You can also do this with the [webhook subscriptions API](https://edesy.in/docs/missed-call/api-reference/webhook-subscriptions):

```bash
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"]
  }'
```

Rules for the URL:

- It must use **HTTPS**.
- It must be publicly reachable. Private and internal addresses are refused, and redirects are not followed, except the one a Google Apps Script web app makes to `script.googleusercontent.com` (see [Send Leads to Google Sheets](https://edesy.in/docs/missed-call/guides/send-leads-to-google-sheets)).

## 2. Handle the request

```
POST https://crm.example.com/webhooks/missed-call
Content-Type: application/json
X-Edesy-Signature: sha256=3b9f...   (only when a secret is set)
```

```json
{
  "event": "call.missed",
  "timestamp": "2026-10-18T07:50:33Z",
  "data": {
    "call_sid": "CA_e5353a30-5f34-4582-bb9c-830ab7b513ef",
    "from": "+918273237781",
    "to": "+919484958085",
    "direction": "inbound",
    "status": "missed",
    "sip_status": 486,
    "phone_number_id": "5e229f03-af3c-47f5-be45-5b11583a7c55",
    "phone_number_name": "Campaign"
  }
}
```

| Field | Meaning |
|---|---|
| `event` | Always `call.missed` for a missed call |
| `timestamp` | When the event was sent, UTC, RFC 3339 |
| `data.call_sid` | Unique id of the call. Retries and replays carry the same value; use it to drop duplicates |
| `data.from` | The caller's number in E.164 (`+91...`) |
| `data.to` | Your number that was called |
| `data.direction` | `inbound` |
| `data.status` | `missed` |
| `data.sip_status` | `486`, the busy signal the caller received |
| `data.phone_number_id` | Id of your number, as used in the API |
| `data.phone_number_name` | The name you gave the number; handy as the campaign or lead source |

## 3. Reply fast

Reply with **any 2xx status within 5 seconds**. Anything else (a non-2xx code, a timeout, a connection error) counts as a failure and is retried; see [Retries and Replay](https://edesy.in/docs/missed-call/guides/retries-and-replay).

Do slow work (CRM calls, emails, callbacks) after you reply, for example by putting the event on a queue.

## 4. Send a test

Press **Send test** on the Webhook page (or call `POST /webhooks/subscriptions/:id/test`). We post this to your URL and show the status code your server returned:

```json
{
  "event": "webhook.test",
  "timestamp": "2026-10-18T07:00:00Z",
  "data": { "message": "Test event from Edesy." }
}
```

Your handler should answer 2xx for `webhook.test` too, and ignore it.

## 5. Watch the delivery log

The Webhook page lists recent deliveries with their status, attempt count, the response code and error, and the exact JSON we sent. In the missed-call log every call also shows a delivery status: `delivered`, `pending` (still retrying), `failed` (all attempts used), `withheld` (wallet too low when the call came in) or `not_sent` (no webhook was set).

## Cost

On pay as you go each event delivered to your webhook costs Rs 0.30 (+GST). Events that fail on every attempt are refunded, and replays are not charged. See [Pricing and Wallet](https://edesy.in/docs/missed-call/billing/pricing-and-wallet).

## Next

- [Verify the signature](https://edesy.in/docs/missed-call/guides/verify-webhook-signature)
- [Send leads to Edesy CRM](https://edesy.in/docs/missed-call/guides/send-leads-to-edesy-crm) with no code
