Skip to main content

Overview

Daya sends merchant webhooks for funding account, deposit, transfer, withdrawal, customer verification, and customer bank-account verification lifecycle changes. Every webhook uses the same envelope:
event tells you what changed, id identifies the webhook event for idempotency, and data describes the resource or verification result that changed.

Common Payload Fields

string
Event type for the lifecycle transition that occurred.
string
Unique webhook event identifier. Use this value for idempotency.
object
Payload for the event. Resource lifecycle events follow the corresponding public API response, while bank-account verification events use the result shape documented below.
string
RFC3339 timestamp for when the event was emitted.

Data Shapes

Webhook events are documented here at the event-name level. Resource lifecycle events use the corresponding public API response object; bank-account verification events use the result object documented on this page.
For resource lifecycle events, fetch the latest resource by the id in data when needed. Bank-account verification events identify the customer with data.customer_id.Check data.type before refetching a deposit: GET /v1/deposits/{id} returns 404 for a USD_DEPOSIT, which use GET /v1/virtual-account-deposits/{id} instead.

Deposit Events

Credit on deposit.completed, on every rail. It is the only deposit event that means the money is final. Never credit on deposit.received, on any rail. For NGN and crypto the deposit can still move to deposit.requires_review or deposit.failed afterwards. For USD virtual account deposits the money is already settled when deposit.received is sent — so its payload reports "status": "COMPLETED" — but deposit.completed is sent for the same deposit moments later, and crediting on both pays twice. See USD Virtual Account Deposit. Deposit payloads for NGN and crypto receive flows include funding_account_id. When a developer fee is configured, deposit payloads include developer_fee and customer_amount. The developer fee is deducted before the final customer amount is calculated; it is not added as a separate charge. Track the merchant-facing settlement lifecycle with deposit.* events.

Funding Account Events

Funding account events track receive-instruction provisioning and disablement. The data object is the public funding account response.

Transfer Events

Transfer events are emitted for POST /v1/transfers and follow the merchant-created transfer lifecycle.

Withdrawal Events

Customer Verification Events

Customer Bank Account Verification Events

These events report Paystack’s asynchronous verification result for the bank details submitted through Tier 1 KYC. They do not change the customer’s Tier 1 KYC status. Both events include customer_id, provider, status, and identity_version. A failed event also includes failure_code and failure_message. When the customer submits new bank details, Daya increments identity_version; ignore an older result after you have received a result for a newer version.
All event families use the same webhook envelope and top-level id for delivery deduplication. Funding account payloads use the public funding account response shape.

Bank Account Verification Events

When you include bank_account and the required phone_number in a Tier 1 verification request, Daya resolves and stores the bank details, then starts Paystack’s asynchronous bank account verification. If Paystack is temporarily unavailable, Daya keeps the verification pending and starts it automatically after the provider recovers. The Tier 1 API response confirms that the submission was accepted; receive the Paystack result through one of these events: The data object contains: Bank detail updates increment identity_version. Daya checks Paystack’s current customer state whenever it processes a signed identification callback, so a later result can supersede an earlier result for the same version. If results arrive out of order, prefer the highest identity_version; for two results with the same version, keep the one with the later timestamp. Deduplicate repeated delivery of the same event with the top-level id. On failure, use failure_code and failure_message to tell the customer what to correct, then submit the corrected bank details with PATCH /v1/customers/{id}/tier1-verification. On success, Daya can create the Paystack instruction. If the customer already has a permanent NGN funding account, Daya sends funding_account.active when Paystack makes a pending account usable, or funding_account.updated when another instruction was already active. Use the Paystack instruction only after its status is ACTIVE. If the customer does not yet have an account, Daya retains the successful verification and uses it when you create the permanent NGN funding account later.

Lifecycle Examples

These examples show how webhook names follow the public resource being reconciled.

Funding Account Deposit

When a customer sends NGN to a funding account’s virtual account, the incoming money is a deposit. The payment detail is the funding account; the money movement is tracked with deposit.* events.

USD Virtual Account Deposit

When a customer sends USD to a virtual account, the deposit is received and settled in a single step. Both events are sent at the same moment and no deposit.processing is sent. Because the two events are delivered independently, they can arrive in either order. Treat deposit.completed as the signal to credit, and make your handler tolerant of it arriving before deposit.received.
Credit exactly once per deposit. Because a USD deposit is already settled when deposit.received is sent, every event for it — deposit.received, deposit.completed, and the legacy deposit.settled — carries the same payload with "status": "COMPLETED".A handler that credits whenever data.status == "COMPLETED", without checking the event name, will credit the same deposit three times. Either credit only on deposit.completed (treating deposit.settled as the same event), or make crediting idempotent on data.id.Deduplicate on data.id, not on the envelope id. Each event gets its own envelope id, so the three events for one deposit have three different envelope ids and the same data.id. Deduplicating on the envelope id will not prevent a double credit.This does not apply to NGN and crypto deposits, where deposit.received carries a non-terminal status and only the terminal event reports COMPLETED.
An endpoint registered with an empty event_types receives every event, so it will receive all three. USD virtual account deposits carry "type": "USD_DEPOSIT" and do not include funding_account_id. They are not served by the /v1/deposits endpoints: GET /v1/deposits/{id} returns 404 for a USD deposit id, so a handler that refetches every deposit through /v1/deposits after a webhook will treat a valid USD deposit as missing. Branch on data.type before refetching.

Internal Balance to NGN Bank Transfer

When a merchant sends withdrawal balance to an NGN bank account through POST /v1/transfers, the public resource is a transfer.

Crypto Wallet Withdrawal

When a merchant sends stablecoin or crypto from the withdrawal balance to an on-chain wallet, the public resource is a withdrawal.

Payload Examples

These examples are intentionally representative. The full data object follows the linked resource response shape for each event family.

Funding Account Active

Deposit Completed

Transfer Completed

Customer Verification Approved

Bank Account Verification Succeeded

Bank Account Verification Failed

Next Steps

Webhook Overview

Delivery guarantees, retries, and handler guidance

Webhook Verification

Verify the HMAC signature on incoming requests