You do not call an endpoint to receive money — payments arrive as transfers to a customer account number. Here is the path each one takes.
Cyrus verifies the provider's webhook signature, discards duplicates, and matches the credited account number to a customer. The webhook alone is never treated as proof of payment — Cyrus independently requeries the provider to confirm the transfer before your wallet is credited and payment.succeeded fires. Amounts are always kobo (₦50,000.00 = 5,000,000 kobo) and may carry sub-kobo decimals (e.g. a computed fee of 1500.015) — parse them as decimals, not integers.
{
"event": "payment.succeeded",
"createdAt": "2026-07-10T08:13:29.663Z",
"data": {
"transactionId": "9b323304-…",
"amountKobo": 15000,
"feeKobo": 1000,
"currency": "NGN",
"status": "SUCCESSFUL",
"matchStatus": "MATCHED",
"sessionId": "1000042607…",
"providerTransactionId": "API-VACT_TRA-…",
"customerReference": "user_123",
"virtualAccountNumber": "0123456789",
"paidAt": "2026-07-07T20:39:02Z"
}
}amountKobois the gross amount the payer sent. Your wallet is credited net of two deductions: the provider's own confirmed fee, and Cyrus's platform fee (a markup on top of it) — see the Wallet page in the dashboard for the exact split on any transaction.
A transfer to an account number Cyrus does not recognize is never dropped — it is recorded and surfaced as a misdirected payment for your ops team to resolve.
Every raw inbound payment event is persisted as a payment event— even if it can't be attributed to a customer. You can list and inspect these via the Payment Events API. Each event carries a status and, when relevant, a failureReasonexplaining why it wasn't processed normally.
| Status | Meaning |
|---|---|
| RECEIVED | Event ingested but reconciliation hasn't confirmed it yet — still pending provider requery. |
| PROCESSED | Successfully reconciled. A transaction exists and the wallet was credited. |
| IGNORED | Not a credit to a known customer — see failureReason below. |
| REATTRIBUTED | Was orphaned, then manually attributed to a customer by you. |
| FAILED | Provider could not confirm the session after all retries. |
| failureReason | What happened |
|---|---|
| UNKNOWN_VIRTUAL_ACCOUNT | Account number doesn't match any of your customers — orphan/misdirected payment. Use reattribute. |
| INACTIVE_CUSTOMER | Account matched a customer who was suspended or closed at the time. Use reattribute. |
| NON_CREDIT_EVENT | Not a VA credit (e.g. POS failure notification). Nothing to do. |
| DUPLICATE | A transaction for this provider ID already exists. Nothing to do. |
| PROVIDER_UNCONFIRMED | Provider never confirmed the session. Use replay if the payer confirms they sent money. |
Re-runs the reconciliation pipeline for a previously received event. Only useful when a payment is still unresolved — status is RECEIVED or FAILED — and has been sitting without confirmation for a while. The payer should confirm they actually sent the money before you replay.
Do notreplay events that are already PROCESSED, IGNORED, or REATTRIBUTED — replaying a terminal event is a no-op. Do not use replay as a general retry mechanism: if the provider never received the transfer in the first place, replaying won't create one.
Manually assigns an orphaned payment to one of your customers. Only works on events withfailureReason of UNKNOWN_VIRTUAL_ACCOUNT or INACTIVE_CUSTOMER. You must specify thecustomerReference of the customer who should receive the payment — that customer must be ACTIVE. Cyrus then creates a transaction and runs the normal reconciliation pipeline.