> ## Documentation Index
> Fetch the complete documentation index at: https://platform-docs.sarj.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Receive a POST when a call ends — completed, no answer, or failed.

When a call reaches a terminal state, Sarj.ai POSTs a JSON payload to your configured webhook URL. You don't need to poll `getCall` — webhooks push the same data the moment it's available.

## Configure

Set your webhook URL in the [Sarj.ai Dashboard](https://platform.sarj.ai) (one URL per organization). The endpoint must:

* Be reachable over HTTPS (HTTP also works for staging, not recommended for production)
* Return 2xx within 10 seconds
* Be idempotent — see [Retries](#retries) below

## Payload

Every notification has the shape:

```json theme={null}
{
  "call_id": "call_8f9b2c1e-4a5d-4f6e-8b1a-2c3d4e5f6a7b",
  "payload": { /* one of the variants below */ }
}
```

`payload.type` discriminates the variant.

### Completed call (`type: "complete"`)

Fires when the call reached the customer and finished. Includes the full recording URL, transcript, and report.

```json theme={null}
{
  "call_id": "call_8f9b2c1e-4a5d-4f6e-8b1a-2c3d4e5f6a7b",
  "payload": {
    "type": "complete",
    "call_started": "2026-04-12T10:30:05Z",
    "call_data": {
      "call_id": "call_8f9b2c1e-…",
      "direction": "outbound",
      "phone_number": "+966512345678",
      "status": "completed",
      "total_duration": 47,
      "enhanced_transcript": {
        "transcript": { "messages": [ /* user + assistant turns */ ] },
        "errors": []
      },
      "report": { /* scenario-specific structured output */ },
      "recording_url": "https://storage.googleapis.com/…?signature=…",
      "created_at": "2026-04-12T10:30:00Z",
      "started_at": "2026-04-12T10:30:05Z",
      "ended_at": "2026-04-12T10:30:52Z"
    },
    "response_body": { /* optional, scenario response */ }
  }
}
```

<Tip>
  `recording_url` is a signed, time-limited URL. Download promptly or generate a fresh one via [`GET /calls/{call_id}`](/api-reference/calls/fetch-sarjai-voice-call-details-by-call_id).
</Tip>

### Not-completed call

Fires when the call never reached the customer. `type` indicates why:

| `type`             | Meaning                                             |
| ------------------ | --------------------------------------------------- |
| `no_answer`        | Rang, no pickup                                     |
| `user_rejected`    | Customer hung up / rejected the call                |
| `user_unavailable` | Carrier returned unavailable (off, out of coverage) |
| `automation`       | Hit an IVR / voicemail / non-human                  |
| `failed`           | Telephony failure with no SIP-level reason          |

```json theme={null}
{
  "call_id": "call_8f9b2c1e-4a5d-4f6e-8b1a-2c3d4e5f6a7b",
  "payload": {
    "type": "no_answer"
  }
}
```

## Retries

Sarj.ai retries up to **3 times** on failure (network error, timeout, non-2xx response) with a **2-second delay** between attempts. Each attempt has a **10-second timeout**.

After 3 failures the delivery is marked failed and the per-call webhook state is recorded server-side. You can re-fetch the call detail via [`GET /calls/{call_id}`](/api-reference/calls/fetch-sarjai-voice-call-details-by-call_id) at any time — the data is durable.

<Warning>
  Your endpoint must be **idempotent**. The same `call_id` may arrive more than once if your server's 2xx response is delayed or dropped. Deduplicate on `call_id`.
</Warning>

## Verifying authenticity

<Note>
  Webhook signature verification (HMAC) is not yet shipped. For now, lock down your webhook endpoint by IP allow-listing (contact support for the current source IP range) or by adding a hard-to-guess secret path component to the URL (e.g. `https://your-app.com/webhooks/sarj/c8f4a2b6e1d…`).
</Note>

## Testing

The dashboard offers a "Send test webhook" button to fire a synthetic `complete` payload at your URL — use this while standing up the integration. After that, place a real test call to verify end-to-end.
