<!-- ZenPays documentation · https://docs.zenpayz.com/docs/guides/webhooks/payout-webhooks -->

# Payout Webhooks

Payout webhook events track single payouts, payout intents, and batch payouts.

## Single Payout Events

| Event | Description |
|-------|-------------|
| `payout.initiated` | Payout accepted by ZenPay and moved into processing (funds are reserved but the final outcome is not yet known) |
| `payout.completed` | Payout processing finished successfully and funds have been fully debited from the reserved balance |
| `payout.failed` | Payout processing did not complete successfully and any reserved funds are released or returned according to ZenPay rules |
| `payout.cancelled` | Payout was cancelled before processing completed |

### Payload

```json
{
  "event_type": "payout.completed",
  "payment_data": {
    "payoutId": "payout_1774778823937_17298d89",
    "transactionId": "po6efbc8502da51774842226226",
    "intentId": "poi_mnblh1o5_e8190e32",
    "amount": 100,
    "currency": "INR",
    "status": "completed",
    "beneficiary": "Rajesh Kumar",
    "payoutType": "BANK_TRANSFER",
    "failureReason": null,
    "metadata": {
      "order_no": "ORD-2026-001",
      "customer_ref": "CUST-789"
    }
  }
}
```

### Payload Fields

| Field | Type | Description |
|-------|------|-------------|
| `payoutId` | string | Internal payout ID |
| `transactionId` | string | External payout transaction reference — matches `externalPayoutId` from the [confirm response](/docs/rest-api/endpoints/payout-intents/confirm-payout-intent) |
| `intentId` | string \| null | Payout intent ID (if created via the two-step intent flow) |
| `amount` | number | Payout amount |
| `currency` | string | Payout currency |
| `status` | string | `initiated`, `completed`, `failed`, or `cancelled` |
| `beneficiary` | string | Beneficiary name |
| `payoutType` | string | Payout method (e.g. `BANK_TRANSFER`, `UPI`) |
| `failureReason` | string \| null | Reason for failure (only on `payout.failed`) |
| `metadata` | object \| null | Custom key-value pairs passed when creating the payout intent — returned as-is in all webhook callbacks |

:::tip Linking payouts end-to-end
When using the two-step payout intent flow:
1. **Create intent** → get `intentId`
2. **Confirm intent** → get `intentId` + `payoutId` + `externalPayoutId`
3. **Webhook callback** → get `intentId` + `payoutId` + `transactionId`

The `transactionId` in the webhook equals `externalPayoutId` from the confirm response. All three IDs are present in both the confirm response and webhook, so you can always link them.
:::

---

## Payout Intent Events

| Event | Description |
|-------|-------------|
| `payout_intent.created` | Payout intent created |
| `payout_intent.confirmed` | Payout intent confirmed and submitted for processing |
| `payout_intent.succeeded` | Payout intent completed successfully |
| `payout_intent.failed` | Payout intent failed |
| `payout_intent.cancelled` | Payout intent cancelled |
| `payout_intent.expired` | Payout intent expired without confirmation |

### Payload

Shares the same payload shape as single payout webhooks:

```json
{
  "event_type": "payout_intent.succeeded",
  "payment_data": {
    "intentId": "poi_mnblh1o5_e8190e32",
    "payoutId": "payout_1774778823937_17298d89",
    "transactionId": "po6efbc8502da51774842226226",
    "amount": 500,
    "currency": "INR",
    "status": "succeeded",
    "beneficiary": "Rajesh Kumar",
    "payoutType": "BANK_TRANSFER",
    "failureReason": null,
    "metadata": {
      "order_no": "ORD-2026-001"
    }
  }
}
```

---

## Batch Payout Events

| Event | Description |
|-------|-------------|
| `batch_payout.created` | Batch payout created |
| `batch_payout.processing` | Batch payout is being processed |
| `batch_payout.completed` | All payouts in the batch completed successfully |
| `batch_payout.partially_completed` | Some payouts in the batch completed, others failed |
| `batch_payout.failed` | Batch payout failed |
| `batch_payout.cancelled` | Batch payout was cancelled |

### Payload

```json
{
  "event_type": "batch_payout.completed",
  "payment_data": {
    "batchId": "bp_abc123",
    "totalPayouts": 50,
    "successCount": 48,
    "failedCount": 2,
    "totalAmount": 25000,
    "currency": "INR",
    "status": "completed",
    "completedAt": "2026-03-16T14:30:00.000Z"
  }
}
```

### Payload Fields

| Field | Type | Description |
|-------|------|-------------|
| `batchId` | string | Batch payout ID |
| `totalPayouts` | number | Total payouts in the batch |
| `successCount` | number | Number of successful payouts |
| `failedCount` | number | Number of failed payouts |
| `totalAmount` | number | Total amount across all payouts |
| `currency` | string | Batch currency |
| `status` | string | `created`, `processing`, `completed`, `partially_completed`, `failed`, `cancelled` |
| `completedAt` | string \| null | ISO 8601 completion timestamp |
