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

# Webhooks

> Receive real-time notifications when events occur on the Cadana platform

Webhooks are automated HTTP POST requests sent by Cadana when events occur on the platform. Use them to get real-time updates for transactions, payroll, KYC, and more.

***

## Webhook Structure

Every webhook follows this envelope structure:

```json JSON theme={null}
{
  "eventType": "transaction.succeeded",
  "id": "e13b9e14-c062-42ea-8563-8fc9223b29b5",
  "version": "1.0",
  "timestamp": 1681006175,
  "data": {
    ...
  }
}
```

| Field       | Description                                                                                        |
| :---------- | :------------------------------------------------------------------------------------------------- |
| `id`        | Unique webhook ID. Use this to deduplicate — retried webhooks are sent with the same `id`.         |
| `eventType` | The event type (e.g., `transaction.succeeded`). See [Events](/reference/events) for the full list. |
| `version`   | Webhook version                                                                                    |
| `timestamp` | Unix timestamp when the webhook was sent                                                           |
| `data`      | Event-specific payload. See [Events](/reference/events) for payload structures.                    |

***

## Getting Started

### Configure Your Endpoint

1. Navigate to **Settings** → **Developers** → **Webhooks** in the [Dashboard](https://app.cadanapay.com)
2. Click **Add Webhook**
3. Enter the URL where you want to receive events

<Note>
  Platform integrators configure webhooks in the [Platform Dashboard](https://platform.cadanapay.com) instead — the same **Settings** → **Developers** → **Webhooks** path. A platform endpoint receives events for all businesses on the platform; use the `tenantKey` in each payload to tell them apart.
</Note>

### Handle Events

After registering an endpoint, Cadana sends an HTTP POST request to your URL every time a subscribed event occurs. Parse the `eventType` field to determine how to handle each event.

### Respond Promptly

Your endpoint must return a `2xx` status code to acknowledge receipt. Any response outside the `2xx` range — including `3xx` redirects — is treated as a failure and will trigger retries.

<Tip>
  Respond with `200 OK` immediately, then process the payload asynchronously via a message queue. This prevents timeouts.
</Tip>

***

## Verifying Webhooks

Every webhook includes signature headers you should verify to confirm it was sent by Cadana.

### Signature Headers

| Header           | Description                                  |
| :--------------- | :------------------------------------------- |
| `svix-id`        | Unique message ID from the delivery provider |
| `svix-timestamp` | Unix timestamp of the webhook                |
| `svix-signature` | HMAC-SHA256 signature of the payload         |

### Verification

Use the [Svix libraries](https://docs.svix.com/receiving/verifying-payloads/how) to verify signatures automatically:

<Tabs>
  <Tab title="Node.js">
    ```javascript JavaScript theme={null}
    import { Webhook } from "svix";

    const wh = new Webhook("your-signing-secret");
    const payload = wh.verify(body, headers);
    ```
  </Tab>

  <Tab title="Python">
    ```python Python theme={null}
    from svix.webhooks import Webhook

    wh = Webhook("your-signing-secret")
    payload = wh.verify(body, headers)
    ```
  </Tab>

  <Tab title="Go">
    ```go Go theme={null}
    wh, _ := svix.NewWebhook("your-signing-secret")
    payload, _ := wh.Verify(body, headers)
    ```
  </Tab>
</Tabs>

Your signing secret is available in **Settings** → **Developers** → **Webhooks** in the Dashboard.

For manual verification, see the [Svix manual verification docs](https://docs.svix.com/receiving/verifying-payloads/how-manual).

***

## Retry Policy

If your endpoint fails to respond with a `2xx` status code, Cadana retries delivery with exponential backoff:

| Attempt   | Delay      |
| :-------- | :--------- |
| 1st retry | 5 seconds  |
| 2nd retry | 5 minutes  |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours    |
| 5th retry | 5 hours    |
| 6th retry | 10 hours   |
| 7th retry | 24 hours   |

After all retries are exhausted (\~3 days), the webhook is marked as failed.

***

## Best Practices

1. **Respond immediately** — Return `200 OK` first, process asynchronously. Don't do heavy work before responding.
2. **Deduplicate** — Use the `id` field to detect and skip duplicate deliveries. Retried webhooks keep the same `id`.
3. **Fetch the latest state** — Webhooks can arrive out of order. After receiving an event, fetch the resource via API (e.g., `GET /v1/payouts/{id}`) to get the latest state.
4. **Implement reconciliation** — Webhook delivery is not guaranteed. Periodically poll the API to catch any missed events.
5. **Verify signatures** — Always verify the `svix-signature` header before processing a webhook to prevent spoofing.
