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

# Disbursement Webhooks

> Receive and verify disbursement status callbacks from LauncX.

When a disbursement reaches a final state (`SUCCESS` or `FAILED`), LauncX `POST`s a JSON callback to your registered **disbursement webhook URL** from the [outbound IPs](/prerequisites#5-configure-your-webhook-endpoint).

The disbursement webhook URL is configured separately from your payment webhook URL — disbursement callbacks are never sent to the payment URL. Both are signed with the **same Callback Secret**, so the verifier you built for [Payment Webhooks](/webhooks) works unchanged.

Intermediate states (`VERIFIED`, `REQUESTED`) do not trigger callbacks, and you receive **exactly one webhook per final transition**.

## Payload

| Field          | Type   | Notes                                                                                                                                                                                            |
| -------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `withdrawalId` | string | Disbursement UUID (the `id` from [Create a disbursement](/disbursements#3-create-a-disbursement)).                                                                                               |
| `reference`    | string | The `Idempotency-Key` you supplied when creating the disbursement. Omitted for dashboard-created disbursements.                                                                                  |
| `referenceId`  | string | The `reference_id` you supplied when creating the disbursement — your own business reference (order/payout number). Omitted when you didn't supply one, and for dashboard-created disbursements. |
| `status`       | string | `SUCCESS` \| `FAILED`.                                                                                                                                                                           |
| `amount`       | number | Amount debited from your wallet.                                                                                                                                                                 |
| `platformFee`  | number | Platform fee charged.                                                                                                                                                                            |
| `netAmount`    | number | `amount - platformFee` — what the beneficiary receives.                                                                                                                                          |
| `currency`     | string | `IDR`.                                                                                                                                                                                           |
| `bankName`     | string | Beneficiary bank.                                                                                                                                                                                |
| `accountNo`    | string | Beneficiary account number, **masked to the last 4 digits** (e.g. `******7890`).                                                                                                                 |
| `accountName`  | string | Beneficiary account holder name.                                                                                                                                                                 |
| `failedReason` | string | Only present when `status` is `FAILED`.                                                                                                                                                          |
| `paidAt`       | string | RFC3339 UTC. Only present when `status` is `SUCCESS`.                                                                                                                                            |
| `timestamp`    | string | RFC3339 UTC, e.g. `2026-06-15T08:42:11Z`.                                                                                                                                                        |
| `nonce`        | string | Unique UUID per delivery (use for replay protection).                                                                                                                                            |

```json theme={null}
{
  "withdrawalId": "9f2c1a4b-7c3e-4d9a-b8e2-3c1d5a6f0e21",
  "reference": "payout-batch-42-row-7",
  "referenceId": "ORD-2026-0042",
  "status": "SUCCESS",
  "amount": 100000,
  "platformFee": 2500,
  "netAmount": 97500,
  "currency": "IDR",
  "bankName": "Bank Central Asia",
  "accountNo": "******7890",
  "accountName": "JOHN DOE",
  "paidAt": "2026-06-15T08:42:09Z",
  "timestamp": "2026-06-15T08:42:11Z",
  "nonce": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d"
}
```

```json theme={null}
{
  "withdrawalId": "9f2c1a4b-7c3e-4d9a-b8e2-3c1d5a6f0e21",
  "reference": "payout-batch-42-row-8",
  "referenceId": "ORD-2026-0043",
  "status": "FAILED",
  "amount": 100000,
  "platformFee": 2500,
  "netAmount": 97500,
  "currency": "IDR",
  "bankName": "Bank Central Asia",
  "accountNo": "******7890",
  "accountName": "JOHN DOE",
  "failedReason": "Payout FAILED",
  "timestamp": "2026-06-15T08:42:11Z",
  "nonce": "b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e"
}
```

<Note>
  Match deliveries to your records by `withdrawalId`, or — if you supplied `reference_id` at create — by `referenceId`, which echoes your own business reference so you don't need to store our UUID. (`reference` echoes your `Idempotency-Key`.) A `FAILED` disbursement means no funds moved; the debited amount is returned to your wallet balance.
</Note>

## Verifying the signature

Each delivery includes an HMAC signature header:

| Header                 | Value                                                  |
| ---------------------- | ------------------------------------------------------ |
| `X-Callback-Signature` | `hex( HMAC_SHA256( rawRequestBody, CallbackSecret ) )` |

Compute the HMAC-SHA256 of the **raw request body bytes** (exactly as received — do not re-serialize) using your Callback Secret, hex-encode it, and compare against `X-Callback-Signature`.

This is the same secret and scheme as [Payment Webhooks](/webhooks#verifying-the-signature) — one verifier handles both.

## Delivery & retries

* Up to **4 attempts** with backoff (immediate, 1s, 2s, 3s).
* Any `2xx` response is treated as success; respond `2xx` quickly and process asynchronously.
* Delivery is best-effort: if all attempts fail, the disbursement's final status is unchanged — reconcile with [Get a disbursement](/disbursements#4-get-a-disbursement).
