Skip to main content
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 DashboardSettings → 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:
{
  "id": "f0c3a1b2-...",
  "event": "FUNDING_NOTIFICATION",
  "created_at": "2026-06-16T10:30:00Z",
  "data": { }
}
FieldTypeDescription
idstringUnique delivery id — use it to dedupe (the same event may arrive more than once)
eventstringThe event type (see below)
created_atstringISO 8601 timestamp
dataobjectEvent-specific payload (schemas below)
The envelope and data use the API’s camelCase field names.

Headers

HeaderDescription
Content-Typeapplication/json
X-Numero-Signaturet=<timestamp>,v1=<base64 HMAC-SHA256> — see Signature Verification
X-Webhook-IdUnique delivery id (also in the body as id)
X-Webhook-EventThe event type

Event types

EventFires onSuccessFailuredata schema
FUNDING_NOTIFICATIONInbound credit lands (VA funding, settlement) and internal-transfer receiptn/a¹TransactionWebhookData
TRANSFER_NOTIFICATIONOutbound bank transfer settles and internal-transfer sendTransactionWebhookData
PAYOUT_NOTIFICATIONInternational payout settlesTransactionWebhookData
BILLS_PURCHASE_NOTIFICATIONA bill payment (airtime/data/electricity/cable/betting) settlesBillsPurchaseWebhook
VERIFICATION_NOTIFICATIONA verification settles out of PendingVerificationWebhookData
VIRTUAL_ACCOUNT_CREATEDA customer/business virtual account is mintedn/a²VirtualAccountCreatedData
CURRENCY_CONVERSIONAn FX conversion completesn/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.
{
  "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"
}
FieldTypeDescription
referencestringTransaction reference
type / servicestringTransaction type and service
directionstringCREDIT or DEBIT
statusstringSuccessful · Unsuccessful · Pending
requestState / requestStateDetailsstringProcessing state + detail
trxAmountnumberAmount (naira)
trxFeenumberThe fee charged on this transaction (naira)
stampDutyApplied / stampDutyAmountbool / numberStamp duty, when applicable
balanceBefore / balanceAfternumberWallet balance around the event
beneficiaryName / beneficiaryNumber / beneficiaryBankstringThe payer (on credits)
toAccountName / toAccountNumber / toAccountBankstringThe recipient (on debits)
merchant / businessCodestringYour business
isReversedboolWhether the transaction was reversed
tokenstringUtility token, when applicable
dateCreated / dateModifiedstringTimestamps

BillsPurchaseWebhook — VAS

{
  "businessCode": "BUS001",
  "vertical": "ELECTRICITY",
  "reference": "BILL-20260616-abc123",
  "status": "Successful",
  "amount": 5000.00,
  "customerIdentifier": "04223456789",
  "message": "Token generated",
  "token": "1234-5678-9012-3456"
}
FieldTypeDescription
verticalstringAIRTIME · DATA · ELECTRICITY · CABLETV · BETTING
statusstringSuccessful · Failed
customerIdentifierstringPhone / meter / smartcard / betting id
tokenstringUtility token (electricity), when applicable

VerificationWebhookData

{
  "businessCode": "BUS001",
  "reference": "VER-20260616-abc123",
  "type": "BVN",
  "status": "Completed",
  "businessCharge": 50.00,
  "statusCode": "00",
  "statusMessage": "Verification successful"
}
FieldTypeDescription
typestringe.g. BVN, NIN, CAC_ADVANCED
statusstringCompleted · Failed
businessChargenumberNaira charged (null if not charged)

VirtualAccountCreatedData

{
  "businessCode": "BUS001",
  "reference": "VA-20260616-abc123",
  "accountNumber": "8012345678",
  "accountName": "John Doe",
  "bankName": "...",
  "customerEmail": "[email protected]",
  "type": "customer"
}
FieldTypeDescription
typestringcustomer or business
customerEmailstringnull for business VAs

CurrencyConversionData

{
  "businessCode": "BUS001",
  "reference": "FX-20260616-abc123",
  "fromCurrency": "NGN",
  "toCurrency": "USD",
  "fromAmount": 1600000.00,
  "toAmount": 1000.00,
  "rate": 1600.00,
  "status": "Successful"
}
Field names follow the API’s camelCase, but the webhook body uses a different serializer than the request/response API — confirm exact casing against a live delivery while integrating.

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.
  • Emission is fire-and-forget on our side — a webhook problem never unwinds the underlying money movement.