> ## Documentation Index
> Fetch the complete documentation index at: https://developers.phonovation.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Receive and process Phonovation delivery receipts.

A webhook is an HTTPS endpoint in your application. Phonovation sends a delivery receipt (DLR) to this endpoint when a message's delivery status is available.

Configure the endpoint URL and signing secret in [Phonovation developer settings](https://app.phonovation.com/developer?tab=webhook).

```mermaid title="Webhook at a glance" actions={false} theme={"dark"}
%%{init: {"theme": "base", "themeVariables": {"fontFamily": "Outfit", "fontSize": "13px", "primaryColor": "#fff1ef", "primaryTextColor": "#0f1926", "primaryBorderColor": "#d23a25", "lineColor": "#4b69a0", "secondaryColor": "#eaf0fa", "tertiaryColor": "#edf8f0"}, "flowchart": {"nodeSpacing": 18, "rankSpacing": 24, "diagramPadding": 4}}}%%
flowchart LR
  Send["Phonovation sends a DLR"] --> Verify["Verify signature"]
  Verify --> Save["Save delivery status"]
  Save --> Reply["Return 2xx"]

  classDef phono fill:#fff1ef,stroke:#d23a25,color:#0f1926,stroke-width:2px
  classDef app fill:#eaf0fa,stroke:#4b69a0,color:#0f1926,stroke-width:2px
  classDef done fill:#edf8f0,stroke:#247a4b,color:#0f1926,stroke-width:2px
  class Send phono
  class Verify,Save app
  class Reply done
```

## Delivery receipt

Phonovation sends a JSON payload with four fields:

```json title="Delivered DLR" theme={"dark"}
{
  "To": "353871234567",
  "From": "CompanyName",
  "Status": "DELIVERED",
  "ClientReference": "appointment-88421"
}
```

| Field             | Meaning                                              |
| ----------------- | ---------------------------------------------------- |
| `To`              | Recipient mobile number                              |
| `From`            | Sender ID                                            |
| `Status`          | Delivery status from the mobile network              |
| `ClientReference` | The reference supplied for this recipient, or `null` |

Treat `Status` as a string rather than a fixed list of values. Its casing is preserved.

## Handle the webhook

<Steps>
  <Step title="Read the raw body">
    Keep the exact request body before parsing the JSON. You need those original bytes to verify the signature.
  </Step>

  <Step title="Verify the signature">
    If you configured a signing secret, compare `X-Signature` with an HMAC-SHA256 digest of the raw body. Reject the request if it does not match.
  </Step>

  <Step title="Update your record">
    Use `ClientReference` to find the matching record, then save the new `Status`.
  </Step>

  <Step title="Acknowledge the webhook">
    Return any `2xx` response after you have stored or safely queued the event.
  </Step>
</Steps>

## Signature format

The signature header looks like this:

```http title="Signature header" theme={"dark"}
X-Signature: sha256=LOWERCASE_HEX_HMAC
```

Calculate the HMAC with SHA-256, using your webhook secret as the key and the exact raw UTF-8 body as the input. Compare signatures using a constant-time comparison.

<Warning>
  Do not rebuild the JSON before checking the signature. Changes to whitespace or property order will produce a different HMAC.
</Warning>

If no signing secret is configured, `X-Signature` is omitted. Configure a secret whenever possible.

## Retries and duplicates

Phonovation retries webhook delivery after these failures:

* A network failure prevents the HTTP request from completing.
* Your endpoint returns `404 Not Found`.
* Your endpoint returns `408 Request Timeout`.
* Your endpoint returns a `5xx` response.

Other `4xx` responses are not retried. This includes `400 Bad Request`, `401 Unauthorized`, and `403 Forbidden`.

|  Retry | Approximate delay after the previous failure |
| -----: | -------------------------------------------: |
|  First |                                    2 seconds |
| Second |                                    4 seconds |
|  Third |                                    8 seconds |

<Warning>
  A circuit breaker opens for 30 seconds after two transient failures. While it is open, a configured retry may fail fast without making an HTTP request to your endpoint. Because of this, the circuit breaker may prevent all configured retry attempts from becoming actual HTTP requests.
</Warning>

Return a `2xx` response only after you have safely stored or queued the event. Retries can deliver the same event more than once, and delivery order is not guaranteed. Make your handler idempotent so processing a duplicate does not repeat a business action or corrupt status.

<Card title="Webhook API Reference" icon="webhook" color="#4B69A0" href="/phonovation-api/api-reference/webhooks/receive-webhook" cta="View the payload schema" arrow="true">
  Review the complete webhook payload and signature contract.
</Card>
