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

# Webhooks

> Receive real-time notifications when events occur on your account — funding, transfers, payouts, bills, verifications, and more.

Numero **originates** every webhook: we decide which events fire, when (including on failure), the exact payload, and the signature. Your integration is a pure receiver — verify the signature, read the fields, branch on `status`.

## Setting up webhooks

Configure your webhook URL in the **[Merchant Dashboard](https://merchant.usenumero.com)** → **Settings → Developer → Webhooks**:

1. Add your webhook endpoint URL
2. Select the events you want (or subscribe to all with `"*"`)
3. Your signing secret is generated — save it securely (you can view it again anytime via a verification code)

> Your signing secret verifies that incoming webhooks are genuinely from Numero. Keep it private and never expose it in client-side code.

## How webhooks work

```
1. An event occurs       →  e.g., a customer funds your virtual account
2. Numero sends a POST   →  We POST a JSON payload to your webhook URL
3. You verify & process  →  Verify the signature, process the event, return 2xx
```

## Envelope

Every delivery is a `POST` of this structure:

```json theme={null}
{
  "id": "f0c3a1b2-...",
  "event": "FUNDING_NOTIFICATION",
  "created_at": "2026-06-16T10:30:00Z",
  "data": { }
}
```

| Field        | Type   | Description                                                                      |
| ------------ | ------ | -------------------------------------------------------------------------------- |
| `id`         | string | Unique delivery id — use it to dedupe (the same event may arrive more than once) |
| `event`      | string | The event type (see below)                                                       |
| `created_at` | string | ISO 8601 timestamp                                                               |
| `data`       | object | Event-specific payload (schemas below)                                           |

The envelope and `data` use the API's **camelCase** field names.

## Headers

| Header               | Description                                                                                                  |
| -------------------- | ------------------------------------------------------------------------------------------------------------ |
| `Content-Type`       | `application/json`                                                                                           |
| `X-Numero-Signature` | `t=<timestamp>,v1=<base64 HMAC-SHA256>` — see [Signature Verification](/api-reference/webhooks-verification) |
| `X-Webhook-Id`       | Unique delivery id (also in the body as `id`)                                                                |
| `X-Webhook-Event`    | The event type                                                                                               |

## Event types

| Event                         | Fires on                                                                    | Success | Failure | `data` schema               |
| ----------------------------- | --------------------------------------------------------------------------- | :-----: | :-----: | --------------------------- |
| `FUNDING_NOTIFICATION`        | Inbound credit lands (VA funding, settlement) and internal-transfer receipt |    ✓    |   n/a¹  | `TransactionWebhookData`    |
| `TRANSFER_NOTIFICATION`       | Outbound bank transfer settles and internal-transfer send                   |    ✓    |    ✓    | `TransactionWebhookData`    |
| `PAYOUT_NOTIFICATION`         | International payout settles                                                |    ✓    |    ✓    | `TransactionWebhookData`    |
| `BILLS_PURCHASE_NOTIFICATION` | A bill payment (airtime/data/electricity/cable/betting) settles             |    ✓    |    ✓    | `BillsPurchaseWebhook`      |
| `VERIFICATION_NOTIFICATION`   | A verification settles out of Pending                                       |    ✓    |    ✓    | `VerificationWebhookData`   |
| `VIRTUAL_ACCOUNT_CREATED`     | A customer/business virtual account is minted                               |    ✓    |   n/a²  | `VirtualAccountCreatedData` |
| `CURRENCY_CONVERSION`         | An FX conversion completes                                                  |    ✓    |   n/a²  | `CurrencyConversionData`    |

¹ An inbound credit has no "merchant failure" to report. ² FX conversion and VA creation are **synchronous** — a failure is returned in the API response (`status: false` + a 4xx/503), so there's nothing to webhook. Branch on the call's own response for those.

> **Determine the outcome from `data.status`** (`Successful` / `Unsuccessful` / `Pending`) and `data.requestState` / `data.requestStateDetails` — don't infer it from the event type. A failed outbound transfer/payout fires with `status: "Unsuccessful"` and `requestState: "Failed"`; the debit is returned to your wallet through the reversal workflow.

## Payloads

### `TransactionWebhookData` — money movement (transfer / funding / payout / internal)

All amounts are in **naira**.

```json theme={null}
{
  "reference": "TRF-20260616-abc123",
  "type": "Transfer",
  "service": "SingleTransfer",
  "direction": "DEBIT",
  "status": "Successful",
  "requestState": "Completed",
  "requestStateDetails": "Transfer completed successfully",
  "currency": "NGN",
  "trxAmount": 50000.00,
  "trxFee": 25.00,
  "stampDutyApplied": true,
  "stampDutyAmount": 50.00,
  "balanceBefore": 120000.00,
  "balanceAfter": 69925.00,
  "toAccountName": "Jane Smith",
  "toAccountNumber": "0123456789",
  "toAccountBank": "GT Bank",
  "merchant": "Your Business Name",
  "businessCode": "BUS001",
  "isReversed": false,
  "token": null,
  "dateCreated": "2026-06-16T10:30:00Z",
  "dateModified": "2026-06-16T10:30:05Z"
}
```

| Field                                                       | Type          | Description                                 |
| ----------------------------------------------------------- | ------------- | ------------------------------------------- |
| `reference`                                                 | string        | Transaction reference                       |
| `type` / `service`                                          | string        | Transaction type and service                |
| `direction`                                                 | string        | `CREDIT` or `DEBIT`                         |
| `status`                                                    | string        | `Successful` · `Unsuccessful` · `Pending`   |
| `requestState` / `requestStateDetails`                      | string        | Processing state + detail                   |
| `trxAmount`                                                 | number        | Amount (naira)                              |
| `trxFee`                                                    | number        | The fee charged on this transaction (naira) |
| `stampDutyApplied` / `stampDutyAmount`                      | bool / number | Stamp duty, when applicable                 |
| `balanceBefore` / `balanceAfter`                            | number        | Wallet balance around the event             |
| `beneficiaryName` / `beneficiaryNumber` / `beneficiaryBank` | string        | The payer (on credits)                      |
| `toAccountName` / `toAccountNumber` / `toAccountBank`       | string        | The recipient (on debits)                   |
| `merchant` / `businessCode`                                 | string        | Your business                               |
| `isReversed`                                                | bool          | Whether the transaction was reversed        |
| `token`                                                     | string        | Utility token, when applicable              |
| `dateCreated` / `dateModified`                              | string        | Timestamps                                  |

### `BillsPurchaseWebhook` — VAS

```json theme={null}
{
  "businessCode": "BUS001",
  "vertical": "ELECTRICITY",
  "reference": "BILL-20260616-abc123",
  "status": "Successful",
  "amount": 5000.00,
  "customerIdentifier": "04223456789",
  "message": "Token generated",
  "token": "1234-5678-9012-3456"
}
```

| Field                | Type   | Description                                                |
| -------------------- | ------ | ---------------------------------------------------------- |
| `vertical`           | string | `AIRTIME` · `DATA` · `ELECTRICITY` · `CABLETV` · `BETTING` |
| `status`             | string | `Successful` · `Failed`                                    |
| `customerIdentifier` | string | Phone / meter / smartcard / betting id                     |
| `token`              | string | Utility token (electricity), when applicable               |

### `VerificationWebhookData`

```json theme={null}
{
  "businessCode": "BUS001",
  "reference": "VER-20260616-abc123",
  "type": "BVN",
  "status": "Completed",
  "businessCharge": 50.00,
  "statusCode": "00",
  "statusMessage": "Verification successful"
}
```

| Field            | Type   | Description                         |
| ---------------- | ------ | ----------------------------------- |
| `type`           | string | e.g. `BVN`, `NIN`, `CAC_ADVANCED`   |
| `status`         | string | `Completed` · `Failed`              |
| `businessCharge` | number | Naira charged (null if not charged) |

### `VirtualAccountCreatedData`

```json theme={null}
{
  "businessCode": "BUS001",
  "reference": "VA-20260616-abc123",
  "accountNumber": "8012345678",
  "accountName": "John Doe",
  "bankName": "...",
  "customerEmail": "john@example.com",
  "type": "customer"
}
```

| Field           | Type   | Description              |
| --------------- | ------ | ------------------------ |
| `type`          | string | `customer` or `business` |
| `customerEmail` | string | null for business VAs    |

### `CurrencyConversionData`

```json theme={null}
{
  "businessCode": "BUS001",
  "reference": "FX-20260616-abc123",
  "fromCurrency": "NGN",
  "toCurrency": "USD",
  "fromAmount": 1600000.00,
  "toAmount": 1000.00,
  "rate": 1600.00,
  "status": "Successful"
}
```

> **Field casing.** The four envelope fields are exactly `id`, `event`, `created_at` and `data` — note
> `created_at` is snake\_case. Everything **inside** `data` is camelCase, matching the rest of the API.

## Retries

* Respond **`2xx`** to acknowledge. Any non-2xx (or a timeout) is a failed delivery.
* Every webhook is **persisted before delivery**, so events are never lost if your endpoint is down.
* Failed deliveries are **retried automatically** with exponential backoff for up to **24 hours**, and can be **re-sent manually** from the dashboard. After **10 consecutive failures** the subscription is disabled.
* **Webhook delivery never affects the transaction it describes.** A failed or refused delivery does not
  reverse, hold or retry the underlying money movement — treat the webhook as notification, and the
  transaction status endpoint as the source of truth.
