Skip to main content

What are Webhooks?

Webhooks let you receive real-time HTTP notifications when deposit, withdrawal, payout, or onramp lifecycle events occur, instead of polling the API for status changes.
Webhooks are the recommended way to track deposit settlement, merchant withdrawal progress, payout delivery, and onramp state changes.

Supported Events

Deposit Events:
EventResourceDescription
deposit.receivedDepositDeposit received (NGN or crypto)
deposit.flaggedDepositDeposit flagged for review
deposit.pending_withdrawalDepositDeposit pending on-chain withdrawal
deposit.settledDepositDeposit fully settled
deposit.failedDepositDeposit failed
Withdrawal Events:
EventResourceDescription
withdrawal.submittedWithdrawalWithdrawal submitted to chain
withdrawal.settledWithdrawalWithdrawal confirmed on-chain
withdrawal.failedWithdrawalWithdrawal failed
Payout Events:
EventResourceDescription
payout.createdPayoutPayout created and being processed
payout.settledPayoutPayout successfully settled
payout.failedPayoutPayout failed
Onramp Events:
EventResourceDescription
onramp.createdOnrampOnramp created
onramp.completedOnrampOnramp completed
onramp.failedOnrampOnramp failed

Webhook Configuration

Configure webhook endpoints in your Daya Dashboard:
  1. Navigate to Webhooks
  2. Add your webhook URL
  3. Generate or copy your webhook secret
  4. Subscribe to the events you want to receive
Use HTTPS in production. HTTP should be limited to local or sandbox development.

Webhook Payload

All merchant webhooks use the same envelope:
{
  "event": "withdrawal.settled",
  "timestamp": "2026-03-10T09:03:00Z",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "SETTLED",
    "tx_hash": "0x8f3e2d1c0b9a8e7f6d5c4b3a2e1f0d9c8b7a6e5f4d3c2b1a"
  }
}

Common Fields

event
string
Event type, such as deposit.settled or withdrawal.failed.
timestamp
string
RFC3339 timestamp for when the event was emitted.
data
object
Resource payload for the event. The shape depends on the event type — deposit events send a deposit object, withdrawal events send a withdrawal object, payout events send a payout object, and onramp events send an onramp object.
See Webhook Events for the full event list and detailed withdrawal payload fields.

Delivery Guarantees

Webhooks may be delivered more than once. Your handler must deduplicate repeated deliveries.
Events can arrive out of order. Use the timestamp field to order updates client-side.
If your endpoint returns a non-2xx response or times out, Daya retries with backoff. Build your handler to be safe for repeated delivery.
Respond quickly and offload heavy work asynchronously. Slow handlers increase duplicate deliveries.

Webhook Verification

All webhook requests include an HMAC-SHA256 signature in the X-Daya-Signature header. See Webhook Verification for implementation details.

Implementing a Webhook Endpoint

Your endpoint should:
  1. Verify the signature
  2. Build a stable deduplication key from the payload
  3. Return 2xx quickly
  4. Process the event asynchronously if work is non-trivial
const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.json());

function verifySignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

function webhookSubjectId(event) {
  return event.data.deposit_id || event.data.id;
}

function dedupeKey(event) {
  return `${event.event}:${webhookSubjectId(event)}:${event.timestamp}`;
}

app.post('/webhooks/daya', (req, res) => {
  const signature = req.headers['x-daya-signature'];
  const payload = JSON.stringify(req.body);

  if (!verifySignature(payload, signature, process.env.DAYA_WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  const key = dedupeKey(req.body);
  if (isProcessed(key)) {
    return res.status(200).send('Already processed');
  }

  queue.add('process-webhook', req.body);
  markAsProcessed(key);

  res.status(200).send('OK');
});

Best Practices

1

Verify signatures

Always verify X-Daya-Signature before trusting the payload.
2

Deduplicate deliveries

Store a stable deduplication key derived from event, timestamp, and the underlying resource ID.
CREATE TABLE processed_webhook_events (
  dedupe_key VARCHAR(255) PRIMARY KEY,
  processed_at TIMESTAMP
);
3

Return 200 quickly

Acknowledge receipt immediately and queue heavier downstream work.
4

Handle out-of-order events

Use timestamp and the underlying resource state to reconcile event order safely.
5

Monitor webhook health

Track failed deliveries and alert on sustained retries.

Testing Webhooks

For deposit webhook flows:
  1. Create a sandbox onramp
  2. Trigger a sandbox deposit with Create a sandbox deposit
  3. Observe the resulting deposit lifecycle webhooks
For withdrawal webhook flows:
  1. Fund merchant balance through a merchant-balance settlement flow
  2. Create a withdrawal
  3. Observe withdrawal.submitted, withdrawal.settled, or withdrawal.failed

Troubleshooting

Check endpoint reachability, TLS configuration, and whether your handler is returning non-2xx responses.
Duplicate delivery is expected under at-least-once semantics. Deduplicate using a stable payload-derived key.
Sort or reconcile events using timestamp instead of arrival order.
Verify you are using the correct webhook secret and the raw request body.

Next Steps

Webhook Events

Detailed deposit and withdrawal event schemas

Signature Verification

Implement HMAC verification