Skip to content

Events and payloads

A webhook subscription is an HTTPS endpoint you register with the bank, a list of event types you want, and a signing secret. When something happens on an account you can reach, BankCore writes a delivery row and a poller posts it to you within about ten seconds. This page is about what arrives. Verifying signatures covers proving it came from us, and Delivery and retries covers what happens when your endpoint does not answer.

Subscriptions are created by the bank against your partner API client. You tell your relationship manager the URL, the event types and any filters; the secret is handed to you once, out of band.

Every event is matched against every active subscription in the tenant, then filtered. For a partner client the filter that matters most is the account scope: the event must name an account, and that account must be one you can reach. That is the same test the API applies on every call:

  • your settlement account,
  • any account of the corporate customer your client acts for,
  • any account of a sub-agent you onboarded, provided sub_agent_access_enabled is on for your client.

An event that carries no account at all is dropped for a partner subscription before anything else is looked at. In practice that means only these types can ever reach you:

Event What it is Account on the event
transaction.completed A posted transaction on an account you reach: wallet funding, transfers in or out, NIP in or out, virtual account credits, bill and airtime debits, fees. One event per leg, so a wallet-to-wallet transfer between two of your accounts arrives twice (TRANSFER_OUT, TRANSFER_IN). Primary account of the leg
transaction.reversed A posted transaction was reversed, for example a NIP transfer the receiving bank rejected. Carries the original reference and the reversal reference. Primary account of the leg
bill.payment.completed A bill or airtime payment reached the provider. Carries the vend receipt (token, units, receipt). Payer account
bill.payment.failed The provider refused the payment. Payer account
campaign.award.paid A promotional award the bank paid to a customer you created. Addressed to the client that created the customer, nobody else. Award account
card.authorization, card.reversed Card activity on an account you reach, if you hold virtual cards. Cardholder account
webhook.test Fired by the bank to prove your endpoint. Bypasses every filter. None (exempt)

Types such as customer.created, agent.profile.created, account.created, kyc.* and nip.transfer.* exist in the catalogue and can be listed on a subscription, but they do not carry an account, so they are filtered out for partner clients. Subscribe to them if you like; do not wait for them.

Two further filters can be set on your subscription by the bank: a customer type list (INDIVIDUAL, CORPORATE, AGENT) and a source channel list. Ask for them if you only want, say, sub-agent events.

The body is compact JSON, UTF-8, no whitespace. Every payload carries type and eventId. BankCore’s own identifiers (tenantId, customerId, accountId) are stripped before sending; reconcile on account numbers and references.

Field Present on Meaning
type all The event type, same value as the X-Webhook-Event header.
eventId all The bank’s id for the event. Your idempotency key.
timestamp all When the event was raised inside BankCore, before queueing.
reference transaction, bill BankCore’s transaction reference.
clientReference transaction The transactionRef / external_ref you supplied on the originating call, when there was one.
txType transaction See Field dictionaries.
amount, currency transaction, bill, campaign Amount of this leg. For a virtual account credit this is the net amount posted; grossAmount and feeAmount carry the split.
sourceAccountNumber, destinationAccountNumber transaction Either may be null: a NIP inward credit has no source account, a NIP outward debit has no destination account.
accountNumber, accountName, customerName, customerPhone transaction, campaign The account this event is about and who owns it.
customerType, sourceChannel transaction, campaign Owner type and the channel that originated the transaction.
reversalReference, reversalReason transaction.reversed The reversing transaction and why.

Then, for every transaction event, the transaction’s metadata is merged into the top level. The keys depend on the kind of money movement and are listed at the end of this page.

{"type":"transaction.completed","eventId":"9f3b2c14-7a6e-4d51-b0c8-2e5a91d47f60","reference":"TXN20260901084512001","clientReference":"INV-8842190","txType":"NIP_OUTWARD","amount":50000.00,"currency":"NGN","sourceAccountNumber":"6020012345","destinationAccountNumber":null,"customerType":"AGENT","sourceChannel":"API","accountNumber":"6020012345","accountName":"ADEOLA STORES","customerName":"ADEOLA STORES","customerPhone":"08012345678","beneficiaryName":"CHINEDU OKAFOR","beneficiaryAccount":"0123456789","beneficiaryBank":"000013","beneficiaryBankCbnCode":"058","beneficiaryBankName":"GTBank","nipSessionId":"000013260901084512123456789012","senderName":"ADEOLA STORES","senderAccount":"6020012345","feeAmount":10.75,"vatAmount":0.81,"stampDutyAmount":50.00}

The fee, VAT and stamp duty are separate transactions on the same account (txType FEE, TAX, STAMP_DUTY) and arrive as their own transaction.completed events, with references <reference>-FEE, -VAT and -SD.

Only the keys that apply are present. Empty strings are used where a value was unknown at posting time; nulls are not written.

txType Keys
NIP_OUTWARD beneficiaryName, beneficiaryAccount, beneficiaryBank (6-digit NIBSS code), beneficiaryBankCbnCode (3-digit), beneficiaryBankName, nipSessionId, senderName, senderAccount, feeAmount, vatAmount, stampDutyAmount, clientReference
NIP_INWARD senderName, senderAccount, senderBank, senderBankName, nipSessionId, narration; through a virtual account add virtualAccountNumber, virtualAccountName, externalRef, beneficiaryAccount, beneficiaryName, grossAmount, feeAmount, netAmount (a remote-validated band adds beneficiarySource instead of virtualAccountName and externalRef)
TRANSFER_OUT, TRANSFER_IN beneficiaryName, beneficiaryAccount, senderName, senderAccount, channel; initiatedByPartner, initiatedByPartnerCustomerId when a partner debited a sub-agent
VA_CREDIT_OUT, VA_CREDIT_IN virtualAccountNumber, virtualAccountName, virtualAccountExternalRef, virtualAccountPaymentRef, vaMode, beneficiaryAccount, beneficiaryName, senderName, senderAccount, grossAmount, netAmount, feeAmount, channel, clientReference
BILL_PAYMENT, AIRTIME_PURCHASE billPaymentRef, billerName, billerCode, billType, productName, subscriberId, subscriberName, subscriberAddress, amount, feeAmount, totalDebit, clientReference
CASH_IN, CASH_OUT agentAccountNumber, agentName, customerAccountNumber, customerName, operationType
POS legs terminalId, merchantName, cardMasked, rrn, authCode, posTransactionType
Card legs cardMasked, merchantName, merchantId, terminalId, authCode, rrn, responseCode
REVERSAL reversalOf, reversalReason, originalSessionId, originalAmount, feeAmount, netReversed

Treat the key set as open: new money-movement features add keys, and your parser should ignore what it does not know.

Webhook endpoints lists every subscription on your client with its event types and filters; opening one shows Deliveries, and a delivery shows the message that was sent with some keys withheld (the count of withheld keys is stated).