Register one endpoint and receive every payment and payout outcome as a clean, signed event. You integrate once and never touch the underlying provider's payload format.
| Event | Fires when |
|---|---|
| payment.succeeded | Reconciliation confirms an inbound transfer with the provider. Wallet is credited. |
| payment.reversed | A previously confirmed payment was clawed back. Wallet is debited. |
| payment.flagged | Reconciliation couldn't confirm the payment after retrying — needs manual review. |
| payout.completed | A payout you initiated settled successfully. |
| payout.failed | A payout was rejected by the provider; your wallet was refunded. |
Set your endpoint URL and copy your signing secret (whsec_…, shown once) in Dashboard → Settings.
Every delivery carries X-Cyrus-Signature, X-Cyrus-Timestamp, and X-Cyrus-Event. The signature covers timestamp + "." + payload— not the payload alone — so the timestamp is part of what's actually signed, not just a sibling header.
// pseudo-code signedContent = timestamp + "." + rawBody expected = "sha256=" + hex(hmac_sha256(secret, signedContent)) assert expected === headers["X-Cyrus-Signature"]
X-Cyrus-Delivery id; safe to receive more than once.2xx once you have stored the event; do slow work asynchronously.{
"event": "payment.succeeded",
"createdAt": "2026-07-10T08:13:29.663Z",
"data": {
"transactionId": "9a61cf0b-…",
"amountKobo": 15000,
"feeKobo": 1000,
"currency": "NGN",
"status": "SUCCESSFUL",
"matchStatus": "MATCHED",
"sessionId": "1000042607…",
"providerTransactionId": "API-VACT_TRA-…",
"customerReference": "cust_demo_001",
"virtualAccountNumber": "2134089159",
"paidAt": "2026-07-10T08:13:08Z"
}
}