Webhook Event Reference
Every PayKore webhook shares the same envelope:
{
"event": "transaction.completed",
"data": { },
"created_at": "2025-06-01T15:00:00Z"
}
| Field | Description |
|---|---|
event | The event type — one of the events documented below. |
data | Event-specific fields. Structure varies by event type; see each section. |
created_at | ISO 8601 timestamp of when PayKore generated the event (not necessarily when your server received it). |
transaction.completed
Fires when any transaction — P2P transfer, bank transfer, USSD payment, QR payment, or split payment — reaches completed status.
Triggered by: P2P Transfer, Bank Transfer, USSD Payment, QR Payment, Split Payment.
{
"event": "transaction.completed",
"data": {
"id": "tx_4qYzA1bCdE",
"reference": "order_789",
"type": "p2p_transfer",
"amount_kobo": 500000,
"currency": "NGN",
"fee_breakdown": {
"customer_fee_kobo": 2500,
"platform_fee_kobo": 2500
},
"source_wallet": {
"id": "wlt_9f3kA2mXpQ",
"balance_after_kobo": 497500
},
"dest_wallet": {
"id": "wlt_3pRsT7uVwX",
"balance_after_kobo": 500000
},
"completed_at": "2025-06-01T10:03:00Z"
},
"created_at": "2025-06-01T10:03:01Z"
}
Key fields: id, reference, type, amount_kobo, fee_breakdown, completed_at, source_wallet, dest_wallet.
Recommended action: Look up your order or payout record by reference. Mark it as paid/settled. If dest_wallet matches a wallet you're displaying a balance for in your UI, use balance_after_kobo to update it without an extra GET call.
transaction.failed
Fires when a transaction fails after being accepted — most commonly for bank transfers rejected by the receiving bank, or USSD/QR sessions that fail mid-flow.
Triggered by: Bank Transfer, USSD Payment, QR Payment.
{
"event": "transaction.failed",
"data": {
"id": "tx_8wXyZ1aBcD",
"reference": "payout_456",
"type": "bank_transfer",
"amount_kobo": 1000000,
"failed_reason": "Beneficiary bank rejected: account dormant",
"reversal_status": "reversed",
"reversal_tx_id": "tx_9xYzA2bCdE"
},
"created_at": "2025-06-01T11:01:10Z"
}
Key fields: id, reference, failed_reason, reversal_status (whether the originating wallet was credited back).
Recommended action: Mark the corresponding order/payout as failed in your system. Check reversal_status — if "reversed", the source wallet was already credited back automatically and you don't need to issue a manual refund. Surface failed_reason to your user or support team so they understand why (e.g. "account dormant" vs "insufficient funds").
transaction.reversed
Fires when a previously completed transaction is later reversed — for example, a bank flags a transfer as fraudulent after settlement, or PayKore support processes a manual reversal.
Triggered by: Any completed transaction type, via manual reversal or downstream bank action.
{
"event": "transaction.reversed",
"data": {
"original_transaction_id": "tx_4qYzA1bCdE",
"reversal_transaction_id": "tx_2hJkL3mNpQ",
"reference": "order_789",
"amount_kobo": 500000,
"reason": "Bank-initiated reversal: suspected fraud"
},
"created_at": "2025-06-02T09:15:00Z"
}
Key fields: original_transaction_id, reversal_transaction_id, reason.
Recommended action: This is rare and requires manual review. Look up your order by matching original_transaction_id against the id you stored from the original transaction.completed event. Flag the order for manual review — do not assume the original delivery/service can simply be undone automatically. Notify your operations or support team.
kyc.verified
Fires when a BVN or NIN verification check succeeds.
Triggered by: BVN Verification, NIN Verification.
{
"event": "kyc.verified",
"data": {
"kyc_id": "kyc_8dEfG9hIjK",
"user_ref": "user_123",
"type": "bvn",
"verified_at": "2025-06-01T14:00:08Z"
},
"created_at": "2025-06-01T14:00:09Z"
}
Key fields: user_ref, kyc_id, type (bvn or nin), verified_at.
Recommended action: Update the verification status on your user's record (user_ref), keyed by type if you support multiple verification methods. Unlock any features or transaction limits that were gated behind verification — for example, raising their wallet transaction cap above the Tier 1 unverified threshold.
kyc.failed
Fires when a BVN or NIN verification check fails.
Triggered by: BVN Verification, NIN Verification.
{
"event": "kyc.failed",
"data": {
"kyc_id": "kyc_8dEfG9hIjK",
"user_ref": "user_123",
"type": "bvn",
"failure_reason": "BVN_DOB_MISMATCH"
},
"created_at": "2025-06-01T14:00:09Z"
}
Key fields: user_ref, kyc_id, failure_reason.
Recommended action: Notify the user with a specific, actionable message based on failure_reason — for example, BVN_DOB_MISMATCH should prompt "double check the date of birth you entered," not a generic "verification failed." Do not silently retry; most failure reasons require the user to correct their input. See the KYC Verification guide for the full list of failure codes.
settlement.completed
Fires when a scheduled settlement run pays out your accumulated wallet balance to your registered bank account.
Triggered by: Scheduled settlement runs (T+1 by default; see Dashboard: Settlements).
{
"event": "settlement.completed",
"data": {
"settlement_id": "stl_3kLmN4oPqR",
"amount_kobo": 12500000,
"currency": "NGN",
"period_start": "2025-06-01T00:00:00Z",
"period_end": "2025-06-01T23:59:59Z",
"bank_account": {
"bank_name": "Zenith Bank",
"account_number": "1234567890"
},
"psp_reference": "ZEN-STL-88213400"
},
"created_at": "2025-06-02T06:00:00Z"
}
Key fields: settlement_id, amount_kobo, period_start, period_end, bank_account, psp_reference.
Recommended action: Record the settlement in your own accounting/reconciliation system, matched against the transactions that occurred within period_start–period_end. Use psp_reference when reconciling against your bank statement — it's the reference the bank will show for the inbound credit.
wallet.frozen
Fires when a wallet's status changes to frozen — either via your own API call or a compliance action initiated by PayKore.
Triggered by: POST /v1/wallets/{id}/freeze, or automated compliance holds.
{
"event": "wallet.frozen",
"data": {
"wallet_id": "wlt_9f3kA2mXpQ",
"user_ref": "user_123",
"reason": "Compliance hold: unusual transaction pattern",
"frozen_at": "2025-06-02T08:30:00Z"
},
"created_at": "2025-06-02T08:30:01Z"
}
Key fields: wallet_id, user_ref, reason, frozen_at.
Recommended action: Immediately disable outbound payment actions in your app's UI for this user_ref — outbound transfers will fail at the API level regardless, but a clear UI state prevents confusing error messages. If reason indicates a compliance hold rather than your own API call, surface a support contact path to the user rather than a generic error.
Next steps
- Verifying Signatures → — Confirm these payloads genuinely came from PayKore.
- Webhooks Overview → — Delivery, retries, and idempotent handling.