> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.crisscross.money/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.crisscross.money/_mcp/server.

# Exchange Webhooks

> Subscribe to Exchange webhooks to track order, withdrawal, deposit, and beneficiary status changes in real time instead of polling.

### Overview

Exchange emits webhooks whenever one of your resources changes state, so you can react to events as they happen instead of polling the API. Each event is delivered as an HTTP POST to the endpoint you configure, with the body in a consistent envelope:

```json
{
  "eventType": "order.filled",
  "timestamp": "2026-06-09T08:15:30Z",
  "payload": { }
}
```

| Field       | Type               | Description                                                                                                              |
| ----------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------ |
| `eventType` | string             | The event that fired, e.g. `order.filled`.                                                                               |
| `timestamp` | string (date-time) | When the event was generated.                                                                                            |
| `payload`   | object             | The resource in its state at the time of the event. The shape depends on the resource (see [Payloads](#payloads) below). |

Webhooks are delivered via [Svix](https://www.svix.com), and the signing, retry, and recovery mechanics are identical to those described on the [Webhooks](/core-concepts-webhooks) page. This guide covers the Exchange-specific event types and payloads; see [Securing and delivery](#securing-and-delivery) for the shared mechanics.

### Event Types

Exchange emits events across four resources. The `payload` for an event matches the resource named in its `eventType`.

**Order events** — payload is an `Order`:

| Event Type        | Description                                                                  |
| ----------------- | ---------------------------------------------------------------------------- |
| `order.filled`    | The order reached `filled` — the trade is complete and balances are updated. |
| `order.cancelled` | The order was cancelled before trading and balances were reverted.           |

**Withdrawal events** — payload is a `Withdrawal`:

| Event Type              | Description                                                                  |
| ----------------------- | ---------------------------------------------------------------------------- |
| `withdrawal.pending`    | A withdrawal was created and is awaiting dispatch.                           |
| `withdrawal.processing` | The withdrawal is being dispatched to the payment rail.                      |
| `withdrawal.completed`  | The withdrawal completed and the funds were sent.                            |
| `withdrawal.failed`     | The withdrawal failed before dispatch; the reservation was released.         |
| `withdrawal.cancelled`  | The withdrawal was cancelled while pending.                                  |
| `withdrawal.returned`   | The withdrawal was returned by the receiving bank; the balance was restored. |

**Deposit events** — payload is a `Deposit`:

| Event Type         | Description                                         |
| ------------------ | --------------------------------------------------- |
| `deposit.pending`  | A deposit was detected and is awaiting crediting.   |
| `deposit.credited` | A deposit was credited and the funds are available. |

**Beneficiary events** — payload is a `Beneficiary`:

| Event Type             | Description                                                          |
| ---------------------- | -------------------------------------------------------------------- |
| `beneficiary.pending`  | A beneficiary was created and is awaiting activation.                |
| `beneficiary.active`   | A beneficiary reached `active` and can be used in withdrawals.       |
| `beneficiary.rejected` | A beneficiary was rejected and cannot be used in withdrawals.        |
| `beneficiary.disabled` | A beneficiary was disabled and can no longer be used in withdrawals. |

### Payloads

Each `payload` is the same object you receive from the corresponding Exchange API endpoint, so you can reuse the same parser for both. The full field-level schemas live in the [Exchange Webhooks API Reference](/api-reference/exchange/webhooks). The key fields and status values for each resource are summarised below.

#### Order

| Field                               | Type               | Description                                                   |
| ----------------------------------- | ------------------ | ------------------------------------------------------------- |
| `orderId`                           | string             | Unique order identifier.                                      |
| `quoteId`                           | string             | The originating quote.                                        |
| `status`                            | string             | One of `pending`, `filling`, `filled`, `cancelled`, `failed`. |
| `sellCurrency` / `buyCurrency`      | string             | Currencies traded.                                            |
| `sellAmount` / `buyAmount` / `rate` | string             | Locked at quote acceptance.                                   |
| `createdAt` / `updatedAt`           | string (date-time) | Creation and last-update timestamps.                          |

#### Withdrawal

| Field                     | Type               | Description                                                                                                                                                                                                       |
| ------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `withdrawalId`            | string             | Unique withdrawal identifier.                                                                                                                                                                                     |
| `status`                  | string             | One of `pending`, `processing`, `completed`, `cancelled`, `failed`, `returned`.                                                                                                                                   |
| `statusReason`            | string             | Machine-readable context (e.g. `compliance_review`, `returned_by_bank`). Present when available.                                                                                                                  |
| `beneficiaryId`           | string             | The destination beneficiary.                                                                                                                                                                                      |
| `currency` / `amount`     | string             | Withdrawal currency and amount.                                                                                                                                                                                   |
| `reference`               | string             | Reference shown on the rail.                                                                                                                                                                                      |
| `clientReference`         | string             | Your own reference, if supplied.                                                                                                                                                                                  |
| `paymentType`             | string             | Rail inherited from the beneficiary (e.g. `ach`, `international_wire`, `domestic_wire`, `crypto`). `swift` appears only on USD beneficiaries created before wire schemes and is deprecated for new beneficiaries. |
| `createdAt` / `updatedAt` | string (date-time) | Creation and last-update timestamps.                                                                                                                                                                              |

#### Deposit

| Field                 | Type               | Description                                                                  |
| --------------------- | ------------------ | ---------------------------------------------------------------------------- |
| `depositId`           | string             | Unique deposit identifier.                                                   |
| `status`              | string             | One of `pending`, `credited`, `rejected`.                                    |
| `statusReason`        | string             | Machine-readable context (e.g. `compliance_review`). Present when available. |
| `currency` / `amount` | string             | Deposit currency and amount received.                                        |
| `reference`           | string             | The reference that attributed the incoming wire.                             |
| `receivedAt`          | string (date-time) | When the incoming wire was detected.                                         |
| `creditedAt`          | string (date-time) | When funds were credited. Absent while pending.                              |

#### Beneficiary

| Field                               | Type               | Description                                                                  |
| ----------------------------------- | ------------------ | ---------------------------------------------------------------------------- |
| `beneficiaryId`                     | string             | Unique beneficiary identifier.                                               |
| `status`                            | string             | One of `pending`, `active`, `rejected`, `disabled`.                          |
| `statusReason`                      | string             | Machine-readable context (e.g. `compliance_review`). Present when available. |
| `currency`                          | string             | One currency per beneficiary.                                                |
| `name` / `entityType`               | string             | Legal name and `business` or `individual`.                                   |
| `accountDetails` / `routingDetails` | array              | Account identifiers and rail routing.                                        |
| `createdAt` / `activatedAt`         | string (date-time) | Creation and activation timestamps (`activatedAt` absent until active).      |

### Example Webhook Payloads

A filled order:

```json
{
  "eventType": "order.filled",
  "timestamp": "2026-06-09T08:15:30Z",
  "payload": {
    "orderId": "ord_6ba7b810-9dad-11d1-80b4-00c04fd430c8",
    "quoteId": "qut_550e8400-e29b-41d4-a716-446655440000",
    "status": "filled",
    "sellCurrency": "USD",
    "buyCurrency": "NGN",
    "sellAmount": "10000.00",
    "buyAmount": "16750000.00",
    "rate": "1675.00",
    "createdAt": "2026-06-09T08:15:25Z",
    "updatedAt": "2026-06-09T08:15:30Z"
  }
}
```

A completed withdrawal:

```json
{
  "eventType": "withdrawal.completed",
  "timestamp": "2026-04-21T14:30:00Z",
  "payload": {
    "withdrawalId": "wth_6ba7b810-9dad-11d1-80b4-00c04fd430c8",
    "status": "completed",
    "beneficiaryId": "ben_f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "currency": "NGN",
    "amount": "16750000.00",
    "reference": "Invoice 8821 Acme",
    "clientReference": "wd-2026-04-21-0001",
    "paymentType": "local",
    "createdAt": "2026-04-21T14:20:00.000Z",
    "updatedAt": "2026-04-21T14:30:00.000Z"
  }
}
```

A credited deposit:

```json
{
  "eventType": "deposit.credited",
  "timestamp": "2026-04-21T14:25:00Z",
  "payload": {
    "depositId": "dep_550e8400-e29b-41d4-a716-446655440000",
    "status": "credited",
    "currency": "USD",
    "amount": "10000.00",
    "reference": "CC-ACME-USD-7Q3K",
    "receivedAt": "2026-04-21T14:20:00.000Z",
    "creditedAt": "2026-04-21T14:25:00.000Z"
  }
}
```

An activated beneficiary:

```json
{
  "eventType": "beneficiary.active",
  "timestamp": "2026-04-21T09:00:00Z",
  "payload": {
    "beneficiaryId": "ben_f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "status": "active",
    "currency": "NGN",
    "name": "Acme Industries Ltd",
    "entityType": "business",
    "accountDetails": [
      { "type": "account_number", "value": "0123456789" }
    ],
    "routingDetails": [
      { "rail": "fiat", "paymentType": "local", "routingNumberType": "other", "routingNumber": "058" }
    ],
    "createdAt": "2026-04-21T08:55:00Z",
    "activatedAt": "2026-04-21T09:00:00Z"
  }
}
```

### Handling events

Switch on `eventType` to route each event to the right handler. The `payload` already matches the resource you would fetch from the API, so you can reuse your existing models:

```javascript
const event = JSON.parse(payload);

switch (event.eventType) {
  case "order.filled":
    await handleOrderFilled(event.payload);
    break;
  case "withdrawal.completed":
    await handleWithdrawalCompleted(event.payload);
    break;
  case "withdrawal.returned":
    await handleWithdrawalReturned(event.payload);
    break;
  case "deposit.credited":
    await handleDepositCredited(event.payload);
    break;
  case "beneficiary.active":
    await handleBeneficiaryActive(event.payload);
    break;
  // Handle other event types
}
```

### Securing and delivery

Exchange webhooks use the same delivery infrastructure as the rest of CrissCross, so the following all work exactly as documented on the [Webhooks](/core-concepts-webhooks) page:

* **Subscribing** — create and configure endpoints from the webhooks section of your [CrissCross dashboard](https://dashboard.crisscross.money/dashboard/profile), selecting the Exchange events you want to receive.
* **Signature verification** — every delivery is signed with Svix `svix-id`, `svix-timestamp`, and `svix-signature` headers. Verify them before processing. See [Securing Webhooks](/core-concepts-webhooks#securing-webhooks).
* **Idempotency** — deduplicate on the Svix message ID, since retries and manual replays can deliver the same event more than once. See [Best Practices](/core-concepts-webhooks#best-practices).
* **Retries** — failed deliveries are retried on an exponential backoff schedule. See [Webhook Retries](/core-concepts-webhooks#webhook-retries).
* **Recovery** — resend, replay, and recover past messages from the dashboard. See [Resending and Replaying Webhooks](/core-concepts-webhooks#resending-and-replaying-webhooks).

Acknowledge every delivery by returning any `2xx` status. Anything else (including `3xx` redirects and timeouts) is treated as a failure and retried.