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

# How webhooks work

> We send a message to your server every time a payment changes.

Payments take time: your customer has to pay, banks have to clear, blockchains have to confirm. Instead of asking us over and over "is it done yet?", you give us a URL, and **we tell you** the moment something changes.

## The basics

**1. Something happens**

A payment moves to a new step. For example, your customer's money arrives.

**2. We send a message**

We send a `POST` request to your URL with a JSON body describing what happened. This is called an **event**.

**3. You reply**

Your server replies with any `2xx` status code, such as `200`, within 10 seconds. That tells us you got it.

**4. You act on it**

Update your database, notify your customer, and so on.

To add your URL, see [Set up webhooks](https://docs.bridge.linkio.world/control-app/webhooks.md).

## What a webhook looks like

### Headers

| Header | What it is |
| - | - |
| `Content-Type` | `application/json` |
| `X-Webhook-Signature` | Proves the message is from us. See [Verify webhooks](https://docs.bridge.linkio.world/webhooks/verify.md). |
| `X-Webhook-Delivery-Id` | A unique ID for this delivery (`wbd_…`). Use it to spot duplicates. |
| `X-Webhook-Event` | The event name, for example `payment.onramp.completed`. |
| `User-Agent` | `BridgePlatform-Webhooks/1.0` |

### Body

```json theme={null}
{
  "id": "wbd_6dE8fK2mQ9xLp3VnR7tY1wZ4cB",
  "event_id": "evt_2cB6dE8fK2mQ9xLp3VnR7tY1wZ",
  "event_type": "payment.onramp.completed",
  "api_version": "2026-10-29-atlas",
  "business_id": "bus_0190f2a1-7c3b-7d4e-9f10-2a3b4c5d6e7f",
  "created_at": "2026-10-28T10:15:17.412Z",
  "data": {
    "payment_id": "pay_8fK2mQ9xLp3VnR7tY1wZ4cB6dE",
    "reference_id": "ref_7tY1wZ4cB6dE8fK2mQ9xLp3VnR",
    "type": "payment.onramp.completed",
    "party": "1st",
    "state": "completed",
    "status": "success",
    "created_at": "2026-10-28T10:15:02.000Z",
    "updated_at": "2026-10-28T10:15:17.000Z"
  }
}
```

| Field | What it means |
| - | - |
| `id` | The delivery ID. Same as the `X-Webhook-Delivery-Id` header. |
| `event_id` | The event ID. Include it if you contact support. |
| `event_type` | What happened. See the [onramp](https://docs.bridge.linkio.world/webhooks/onramp-events.md) and [offramp](https://docs.bridge.linkio.world/webhooks/offramp-events.md) event lists. |
| `api_version` | The API version the event was created with. |
| `business_id` | Your business. |
| `created_at` | When this delivery was created. |
| `data.payment_id` | The payment this is about. |
| `data.reference_id` | The payment's reference. |
| `data.type` | The event name again. |
| `data.party` | `1st` or `3rd`, as you sent when creating the payment. |
| `data.state` | The payment's new state. |
| `data.status` | The payment's new status: `pending`, `success`, or `failed`. |
| `data.created_at`, `data.updated_at` | When the payment was created and last changed. |

> [!TIP]
>
> The webhook tells you **what changed**. For the full payment, such as amounts and the `transaction_hash`, call [Get a payment](https://docs.bridge.linkio.world/api-reference/payments/details.md) with `decode=true`.

## Order of events

For each payment, we send events **one at a time, in order**. We don't send `fiat_pending` until you've accepted `initiated`. If one delivery is being retried, the next events for that payment wait for it.

See the full [event timeline](https://docs.bridge.linkio.world/webhooks/event-timeline.md).

## Retries

If your server doesn't reply with `2xx` within 10 seconds, or can't be reached, we try again. We make up to **5 attempts**:

| Attempt | When |
| - | - |
| 1 | Straight away |
| 2 | 1 minute later |
| 3 | 5 minutes later |
| 4 | 30 minutes later |
| 5 | 1 hour later |

Each retry can be up to 15 seconds later than shown. After 5 failed attempts, we stop. You can see every attempt in the Bridge app under **Webhooks**, in the endpoint's delivery list.

> [!NOTE]
>
> Redirects (`3xx`) count as a failure. Make sure your URL is the final one.

## Best practices

> [accordion] Reply fast, work later
>
> Reply `200` as soon as you've checked the signature and saved the event. Do slow work, such as sending emails, afterwards. Otherwise you may hit the 10-second limit and get the same event again.

> [accordion] Expect duplicates
>
> You might receive the same event more than once, for example if your reply was lost. Save each `id` (`wbd_…`) you've handled and skip ones you've seen.

> [accordion] Always verify the signature
>
> Anyone can send a request to your URL. Check `X-Webhook-Signature` before trusting it. See [Verify webhooks](https://docs.bridge.linkio.world/webhooks/verify.md).

> [accordion] Use status for decisions
>
> Decide success or failure from `data.status`. Use `data.state` to show progress.

> [accordion] Have a backup
>
> If your server was down for a long time, call [List payments](https://docs.bridge.linkio.world/api-reference/payments/list.md) to catch up on anything you missed.

## Test events

From the Bridge app you can send a `test.webhook.ping` event to check your URL works. See [Send a test message](https://docs.bridge.linkio.world/control-app/webhooks.md#send-a-test-message).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.