# Retries and Replay

> How missed call webhooks are retried (6 attempts over about 15 minutes), how to replay failed deliveries, how to de-duplicate on call_sid, and when you are refunded.

Source: https://edesy.in/docs/missed-call/guides/retries-and-replay

## Automatic retries

Each event gets up to **6 attempts**. An attempt fails when your server returns a non-2xx status, takes longer than 5 seconds, or cannot be reached. After a failure we wait and try again, doubling the wait each time:

| Attempt | Sent about |
|---|---|
| 1 | Immediately |
| 2 | 30 seconds after attempt 1 |
| 3 | 1 minute after attempt 2 |
| 4 | 2 minutes after attempt 3 |
| 5 | 4 minutes after attempt 4 |
| 6 | 8 minutes after attempt 5 |

So the last attempt is about **15 minutes** after the call. While retries are pending the call shows `pending`; after the sixth failure it shows `failed`.

## Refund when delivery fails

If every attempt fails, the Rs 0.30 charge for that event is credited back to your wallet automatically. The call stays in your log and CSV either way.

## Replay

After you fix your server, send failed events again. Replays go to the subscription's **current** URL and are **not charged**.

- **One delivery:** press **Replay** on it in the delivery log, or call  
  `POST /webhooks/subscriptions/:id/deliveries/:delivery_id/replay`
- **All failed deliveries in a window** (up to 10,000 per call):

```bash
curl -X POST https://voice-api.edesy.in/v1/webhooks/subscriptions/SUBSCRIPTION_ID/deliveries/replay \
  -H "Authorization: Bearer vp_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "status": "failed", "since": "2026-10-18T00:00:00Z" }'
```

```json
{ "data": { "replayed": 7 } }
```

`since` and `until` are RFC 3339 timestamps; only `"failed"` is supported for `status`. See [Webhook Subscriptions](https://edesy.in/docs/missed-call/api-reference/webhook-subscriptions).

## De-duplicate on call_sid

A retry or replay carries the same `data.call_sid`. Your server may also see an event twice if it processed a request but replied too late. Store `call_sid` and skip events you have already handled:

```js
if (await db.seen(event.data.call_sid)) return; // already handled
await db.markSeen(event.data.call_sid);
```

Note that two separate calls from the same person are two events with two different `call_sid` values. If you want one lead per caller, de-duplicate on `data.from` in your own system.

## Events that were never sent

If your wallet balance was too low when the call came in, the event is `withheld` and was never queued, so there is nothing to retry or replay. Those callers are still in your log and CSV. See [Pricing and Wallet](https://edesy.in/docs/missed-call/billing/pricing-and-wallet).
