Webhooks & Events
Webhooks let your system receive real-time notifications when something important happens on an order. Instead of polling the API, IDeliver pushes events to your HTTPS endpoint automatically.
Setting up your webhook
- Log in to the merchant dashboard .
- Go to Settings → Webhooks.
- Enter your HTTPS endpoint URL.
- Copy the signing secret — you’ll use this to verify that events come from IDeliver.
- Click Save to activate.
You can also configure your webhook via the API:
curl -X PATCH https://api.ideliver.ng/v1/me/outbound-webhook \
-H "Authorization: Bearer ilv_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/webhooks/ideliver",
"enabled": true
}'Event types
| Event | When it fires |
|---|---|
order.created | A new order was accepted |
order.status_updated | The order status changed |
order.rider_assigned | A rider accepted the job |
order.accepted | Order confirmed by the system |
order.picked_up | Rider collected the item from pickup |
order.delivered | Delivery completed successfully |
Each delivery includes an order object (snake_case envelope: id, merchant_id, source, external_order_id, unified_json, tracking_token, timestamps) plus envelope metadata. When standardized.rider_id is present, a top-level rider object is added: id, name, phone, vehicle_type (nullable fields when unknown).
Event payload
Every webhook event is a POST request with a JSON body (shape):
{
"event": "order.status_updated",
"event_id": "c3d4e5f6-a1b2-4c3d-9e8f-0123456789ab",
"delivery_id": "550e8400-e29b-41d4-a716-446655440000",
"occurred_at": "2026-04-21T12:00:00.000Z",
"merchant_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"order": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"merchant_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"source": "api",
"external_order_id": "my-order-001",
"unified_json": {
"schema_version": "1",
"status": "delivered",
"standardized": {
"dropoff_address": "14 Awolowo Road, Ikoyi, Lagos",
"customer_phone": "+2348011122233"
}
},
"tracking_token": null,
"created_at": "2026-04-21T11:00:00.000Z",
"updated_at": "2026-04-21T12:00:00.000Z"
},
"rider": {
"id": "rider-uuid",
"name": "Jane Rider",
"phone": "+2347000000000",
"vehicle_type": "motorcycle"
},
"previous_status": "in_transit",
"status": "delivered"
}order.status_updated may include previous_status / status. order.rider_assigned may include previous_rider_id.
Verifying webhook signatures
Every event is signed so you can verify it genuinely came from IDeliver. Check the X-Ideliver-Signature header on every incoming request.
How to verify
echo -n 'RAW_REQUEST_BODY' | openssl dgst -sha256 -hmac 'YOUR_WEBHOOK_SECRET'import crypto from "node:crypto";
export function verifyWebhookSignature(secret, rawBody, signatureHeader) {
const match = /v1=([a-f0-9]+)/i.exec(signatureHeader || "");
if (!match) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody, "utf8")
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(match[1], "hex"),
Buffer.from(expected, "hex")
);
}function verifyWebhookSignature(string $secret, string $rawBody, string $header): bool {
if (!preg_match('/v1=([a-f0-9]+)/i', $header, $m)) return false;
$expected = hash_hmac('sha256', $rawBody, $secret);
return hash_equals($m[1], $expected);
}Always verify the signature before processing an event. Reject any requests that fail verification.
Idempotency & retries
IDeliver delivers each event at least once. Your endpoint may receive the same event more than once if the initial delivery fails.
Best practice: Use the event_id from the envelope body for deduplication. It is a UUID that stays constant across all retry attempts for the same business event. Store it in your database and skip processing when you see a duplicate.
const processedEvents = new Set(); // Use a database in production
app.post("/webhooks/ideliver", (req, res) => {
const { event, event_id, order } = req.body;
if (processedEvents.has(event_id)) {
return res.status(200).json({ ok: true, duplicate: true });
}
processedEvents.add(event_id);
// ... process the event
res.status(200).json({ ok: true });
});If your endpoint does not return HTTP 2xx quickly enough, the HTTP client times out after 12 seconds per attempt. IDeliver then retries persisted deliveries:
| Setting | Default | Notes |
|---|---|---|
| Max attempts | 5 | Override with env OUTBOUND_WEBHOOK_MAX_ATTEMPTS (clamped ≤ 20) |
| Backoff | 30s × 2^(n−1) after failed attempt n | Capped at 1 hour before the next attempt |
Your receiver should return HTTP 200 promptly after verifying the signature and enqueueing work.
Webhook request headers
| Header | Value |
|---|---|
Content-Type | application/json |
X-Ideliver-Signature | v1=<hmac_hex> — HMAC-SHA256 of the raw request body |
X-Ideliver-Delivery | Unique delivery ID for this specific webhook attempt |
X-Ideliver-Event | Event type string (e.g. order.status_updated) |
Next steps
- Orders & Deliveries — Create orders, idempotency, cancel
- Order lifecycle & cancellation — Status + cancel rules
- Integrator readiness — CTO checklist links
- Errors & Rate Limits — Handle errors in your webhook endpoint
- API Playground — Test your webhook interactively