Webhooks Overview
Webhooks notify your server in real time when payment events occur. Instead of polling the API, ZevPay sends an HTTP POST request to your configured webhook URL.
How webhooks work
- A payment event occurs (e.g., customer completes a transfer)
- ZevPay sends a POST request to the webhook URL configured on your API key
- Your server processes the event and responds with a
2xxstatus code
Customer pays → ZevPay confirms → POST to your webhook URL → You fulfill the orderSetting up webhooks
Configure your webhook URL in the ZevPay Dashboard under Settings → API Keys. Each API key pair (secret + public) shares a webhook URL and webhook secret.
Requirements
- Your endpoint must be publicly accessible (HTTPS recommended for production)
- Your endpoint must respond with a
2xxstatus code within 30 seconds - Your endpoint should accept
POSTrequests withContent-Type: application/json
Webhook payload
All webhook payloads follow this structure:
{
"event": "charge.success",
"data": {
"reference": "ZVP-CKO-S-abc123",
"amount": 500000,
"currency": "NGN",
"status": "completed",
// ... event-specific fields
}
}| Field | Type | Description |
|---|---|---|
event | string | The event type (e.g., "charge.success") |
data | object | Event-specific payload |
Security
Every webhook request includes an HMAC-SHA256 signature in the x-zevpay-signature header. Always verify this signature before processing the event.
x-zevpay-signature: a1b2c3d4e5f6...The signature is computed using your webhook secret (available in the dashboard). See Verifying Signatures for implementation details.
Webhook delivery
| Property | Value |
|---|---|
| Method | POST |
| Content-Type | application/json |
| Timeout | 30 seconds |
| Signature header | x-zevpay-signature |
| Max retries | 5 |
| Deduplication | Per event + reference |
Automatic retries
If your server fails to respond with a 2xx status code (or doesn't respond within 30 seconds), ZevPay will automatically retry the delivery with exponential backoff:
| Retry | Delay after previous attempt |
|---|---|
| 1st retry | ~4 minutes |
| 2nd retry | ~9 minutes |
| 3rd retry | ~16 minutes |
| 4th retry | ~25 minutes |
| 5th retry | ~36 minutes |
After 5 failed retries, the webhook is marked as permanently failed. You can manually retry failed webhooks from the dashboard.
WARNING
Make sure your webhook endpoint is reliable and responds quickly. If your server is consistently unresponsive, webhook deliveries will be lost after the retry window expires.
Deduplication
ZevPay prevents duplicate webhook deliveries. If a webhook for the same event and transaction reference has already been successfully delivered to your endpoint, it will not be sent again. This protects against accidental double-processing on our end.
However, you should still implement idempotent handlers on your side — see Handle duplicates below.
Delivery logging
All webhook deliveries are logged with:
- Delivery status (
delivered,failed, orpending) - HTTP status code from your server
- Response time in milliseconds
- Retry count and last attempt timestamp
- Request and response body
You can view webhook delivery logs in the dashboard.
Best practices
Respond quickly
Return a 2xx response as fast as possible. Process the event asynchronously if needed:
app.post('/webhooks/zevpay', (req, res) => {
// Respond immediately
res.status(200).send('OK');
// Process asynchronously
processEvent(req.body).catch(console.error);
});Handle duplicates
Design your webhook handler to be idempotent. Use the reference field to detect and skip duplicate events:
async function processEvent(payload) {
const existing = await db.findByReference(payload.data.reference);
if (existing) return; // Already processed
// Process the event
await fulfillOrder(payload.data);
}Verify signatures
Always verify the x-zevpay-signature header before processing events. See Verifying Signatures.
Use HTTPS
Always use HTTPS for your webhook endpoint in production to prevent payload interception.
Events reference
| Event | Description |
|---|---|
charge.success | Payment completed successfully |
See Events Reference for detailed payload documentation.