Skip to Content
Webhooks & Events

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

  1. Log in to the merchant dashboard .
  2. Go to Settings → Webhooks.
  3. Enter your HTTPS endpoint URL.
  4. Copy the signing secret — you’ll use this to verify that events come from IDeliver.
  5. 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

EventWhen it fires
order.createdA new order was accepted
order.status_updatedThe order status changed
order.rider_assignedA rider accepted the job
order.acceptedOrder confirmed by the system
order.picked_upRider collected the item from pickup
order.deliveredDelivery 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:

SettingDefaultNotes
Max attempts5Override with env OUTBOUND_WEBHOOK_MAX_ATTEMPTS (clamped ≤ 20)
Backoff30s × 2^(n−1) after failed attempt nCapped at 1 hour before the next attempt

Your receiver should return HTTP 200 promptly after verifying the signature and enqueueing work.


Webhook request headers

HeaderValue
Content-Typeapplication/json
X-Ideliver-Signaturev1=<hmac_hex> — HMAC-SHA256 of the raw request body
X-Ideliver-DeliveryUnique delivery ID for this specific webhook attempt
X-Ideliver-EventEvent type string (e.g. order.status_updated)

Next steps