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

> ## Agent Instructions
> Bitnob API requests authenticate with an `X-Auth-Client` API key header against https://api.bitnob.com.
> Always use the sandbox environment for testing. Never put live API keys in sample code.
> Money amounts are integers in the smallest unit of the currency (satoshis for BTC, cents for fiat). Never use floats.
> Every state-changing request takes a client-supplied `reference` and must be treated as idempotent on retry.
> Quotes expire. Re-quote rather than reusing a stale quote id.

# Payout webhook events

> Payout event types and the payload fields each one carries, from initialized to withdrawal success, expiry, or failure.

Learn how to listen for transactions that happen on your account

A webhook is a URL on your server where we send payloads for transaction events. For example, if you implement webhooks, we will immediately notify your server with a deposit.success event once a Lightning payment is received. Whenever you receive a webhook notification from us, return a 200 OK to avoid resending the same event again from our server.

> [!NOTE]
>
> Signature verification, retries, source IPs, and local testing work the same way for every Bitnob Business API webhook. They're covered once, on [Webhooks](https://www.bitnob.dev/api-reference/webhooks.md).

### Payout webhooks

Payout webhooks track a payout created through the payouts API (quote → initialize → finalize) as it moves toward settlement. Every event carries the payout record itself, so the field names match the payouts API response.

### Payout initialized

Fired when a payout has been initialized against a quote and accepted for processing. Settlement has not happened yet: wait for **payouts.withdrawal.success** before treating the funds as delivered.

> [!NOTE]
>
> `data.status` on payout webhooks is the event outcome, lowercase (`initiated`, `pending_address_deposit`, `processing`, `success`, `expired`, `failed`). It is **not** the payout record's lifecycle status returned by **Get Payout**, which is uppercase and uses a different vocabulary (`QUOTE` → `INITIATED` → `PENDING` → `PROCESSING` → `SUCCESS` / `FAILED`). Check the `event` field to decide how to handle each webhook, not `data.status`.

1. `event` (string)

   Webhook event name. 'payouts.initialized' indicates the payout was accepted for processing.

2. `event_id` (uuid)

   Unique ID for this webhook delivery. Use it to de-duplicate events on retries.

3. `data.id` (uuid)

   The payout's internal identifier, the same id returned by Initialize / Finalize and accepted by GET /api/payouts/:id.

4. `data.quote_id` (string)

   The quote this payout was created from, e.g. 'QT2\_21434949'.

5. `data.company_id` (uuid)

   The UUID of the company that owns the payout.

6. `data.reference` (string)

   Your reference for the payout, echoed back for reconciliation.

7. `data.status` (string)

   Outcome carried by this event. On payouts.initialized this is 'initiated', or 'pending\_address\_deposit' for onchain funding.

8. `data.from_asset` (string)

   The crypto asset debited, lowercase. For example, 'btc', 'usdt'.

9. `data.to_currency` (string)

   The fiat currency to be paid out, lowercase. For example, 'ngn'.

10. `data.amount` (string)

Amount debited in from\_asset.

11. `data.settlement_amount` (string)

Amount to be credited to the beneficiary in to\_currency.

12. `data.fees` (string)

Fee charged for processing the payout, in `fee_currency`.

13. `data.fee_currency` (string)

Currency the fee is denominated in. For example, 'USD'.

14. `data.source_fee` (string)

The source fee for the payout.

15. `data.total_amount` (string)

Total to be debited in from\_asset.

16. `data.payment_reason` (string)

The payment reason you sent on Initialize payout.

17. `data.beneficiary` (object)

The destination the funds are headed to. Contains `country`, a masked `account_number` (e.g. '\*\*\*\*6789'), and `bank_code`, `bank_name`, and `account_name` when they were set.

18. `data.created_at` (timestamp)

When the payout record was created (ISO 8601, UTC).

19. `data.updated_at` (timestamp)

When the payout record was last updated (ISO 8601, UTC).

20. `data.customer_id` (uuid)

The customer linked to the payout. Present only when you set `customer_id` on Initialize payout.

```json theme={null}
{
  "event": "payouts.initialized",
  "event_id": "7dd98428-2a13-4ce7-8ca2-ec98ee3afcf1",
  "data": {
    "id": "01a14838-6c0c-757a-a23c-6b1cb801d771",
    "quote_id": "QT2_21052912",
    "company_id": "00000000-0000-4000-8000-000000000001",
    "reference": "payout-1777919122721-wln774",
    "status": "initiated",
    "from_asset": "usdt",
    "to_currency": "ngn",
    "amount": "0.740422",
    "settlement_amount": "1000",
    "fees": "0",
    "fee_currency": "USD",
    "source_fee": "0",
    "total_amount": "0.740422",
    "payment_reason": "vendor_payment",
    "beneficiary": {
      "account_name": "JANE DOE",
      "account_number": "****6789",
      "bank_code": "000013",
      "country": "NG"
    },
    "created_at": "2026-10-17T04:56:59.666426Z",
    "updated_at": "2026-10-17T04:57:00.611584458Z"
  }
}
```

### Payout processing

Fired when Bitnob starts sending the payout to the settlement rail. This is an in-flight update, not a terminal state: wait for **payouts.withdrawal.success** before treating the funds as delivered.

*Same fields as Payout initialized.*

The payload carries the same fields as [Payout initialized](#payout-initialized). Refer there for the per-field explanations. One difference:

**data.status** `string`

'processing' on this event.

```json theme={null}
{
  "event": "payouts.processing",
  "event_id": "51ff21dc-9629-41b5-8771-9cbc0d670000",
  "data": {
    "id": "019f8330-71e6-7649-a382-2209fd73b56f",
    "quote_id": "QT2_21439667",
    "company_id": "00000000-0000-4000-8000-000000000001",
    "reference": "4fb1e02e-1234-467a-b91b-3a18dc000000",
    "status": "processing",
    "from_asset": "usdt",
    "to_currency": "ngn",
    "amount": "1.801875",
    "settlement_amount": "2500",
    "fees": "0",
    "fee_currency": "USD",
    "source_fee": "0",
    "total_amount": "1.801875",
    "beneficiary": {
      "country": "NG",
      "bank_code": "100004",
      "account_number": "****4000"
    },
    "created_at": "2026-07-21T05:40:18.02242Z",
    "updated_at": "2026-07-21T05:40:47.522051588Z"
  }
}
```

### Payout withdrawal successful

Fired when the payout has settled to the beneficiary. This is the event to act on when crediting an order or marking a transfer complete.

*Same fields as Payout initialized.*

The payload carries the same fields as [Payout initialized](#payout-initialized). Refer there for the per-field explanations. Two differences:

1. `data.status` (string)

   'success' on this event, rather than 'initiated'.

2. `data.completed_at` (timestamp)

When the payout reached its terminal state. Here, when settlement completed (ISO 8601, UTC). Absent while the payout is still in flight.

```json theme={null}
{
  "event": "payouts.withdrawal.success",
  "event_id": "d9c47581-5e93-4cdf-bb12-3f0a47486500",
  "data": {
    "id": "019f812d-ed2c-7f17-a2e9-12435e670813",
    "quote_id": "QT2_21434859",
    "company_id": "00000000-0000-4000-8000-000000000001",
    "reference": "76543-1234-4304-869j-3a98e85f0812",
    "status": "success",
    "from_asset": "usdt",
    "to_currency": "ngn",
    "amount": "100.988948",
    "settlement_amount": "140174.8",
    "fees": "0",
    "fee_currency": "USD",
    "source_fee": "0",
    "total_amount": "100.988948",
    "beneficiary": {
      "country": "NG",
      "bank_code": "100004",
      "account_number": "****3001"
    },
    "created_at": "2026-07-20T20:18:18.541002Z",
    "updated_at": "2026-07-20T20:18:39.515031203Z",
    "completed_at": "2026-07-20T20:18:39.515031203Z"
  }
}
```

### Payout withdrawal expired

Fired when a payout was not completed before its quote expired. No funds were settled to the beneficiary, to retry, create a fresh quote and initialize again.

*Same fields as Payout initialized.*

The payload carries the same fields as [Payout initialized](#payout-initialized). Refer there for the per-field explanations. Differences:

1. `data.status` (string)

   'expired' on this event.

2. `data.completed_at` (timestamp)

   When the payout reached its terminal state. Here, when it expired (ISO 8601, UTC).

3. `data.payment_reason` (string)

The payment\_reason you supplied on Initialize, echoed back. For example, 'Crypto offramp'. Present when one was set.

```json theme={null}
{
  "event": "payouts.withdrawal.expired",
  "event_id": "0756585e-80a1-4eb8-a9aa-859574500000",
  "data": {
    "id": "019f8128-01b9-7801-b123-123456789abc",
    "quote_id": "QT2_21434706",
    "company_id": "00000000-0000-4000-8000-000000000001",
    "reference": "12345678-c888-0987-1234-2435464844",
    "status": "expired",
    "from_asset": "usdc",
    "to_currency": "ngn",
    "amount": "25.795811",
    "settlement_amount": "35804.11",
    "fees": "0",
    "fee_currency": "USD",
    "source_fee": "0",
    "total_amount": "25.795811",
    "payment_reason": "Crypto offramp",
    "created_at": "2026-07-20T20:05:17.371329Z",
    "updated_at": "2026-07-20T20:24:01.352781396Z",
    "completed_at": "2026-07-20T20:24:01.352781396Z"
  }
}
```

### Payout withdrawal failed

Fired when a payout fails. Event name: `payouts.withdrawal.failed`.

The payload carries the same fields as [Payout initialized](#payout-initialized), with these differences:

1. `data.status` (string)

   Always `failed`.

2. `data.failure_reason` (string)

   Why the payout failed, for example `The payout could not be delivered.` The text can change, so show it to the user and don't match on it. When we held funds for the payout, it also says they were returned.


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