Darb shipping API
Send your store's orders to the shipping company automatically instead of uploading sheets, and get every delivery update back on your server.
Base URL: https://your-darb-domain/api/v1
- Ask the shipping company for an API key for your store (they create it under Stores › your store › API & webhooks).
- Create orders with
POST /orders. Each order gets a tracking number and a public tracking page. - Give the company a webhook URL to receive status changes (confirmed, out for delivery, delivered…).
Authentication
Send your key in the X-API-Key header (or Authorization: Bearer <key>). Keys start with drb_. Every call only sees your own store's orders.
curl https://HOST/api/v1/orders -H "X-API-Key: drb_xxxxxxxxxxxxxxxxxxxxxxxx"
Create orders POST /orders
One order as the body, or up to 500 as {"orders": [...]}. Your merchant_ref (your order number) must be unique: sending the same one twice returns 409, so retries are safe.
| Field | Type | Notes | |
|---|---|---|---|
merchant_ref | string | Your order number (recommended) | |
customer_name | string | required | |
customer_phone | string | required | Mobile; local format (010…) or international (+20…) |
customer_phone2 | string | ||
city | string | Governorate / city — used to route the order to a delivery zone | |
district | string | Area / district — used to route the order to a delivery zone | |
address | string | Street, building, floor… | |
lat, lng | number | Map position, if you have it | |
description | string | Contents | |
pieces | integer | Default 1 | |
weight_kg | number | ||
cod_amount | number | Cash to collect on delivery; 0 or omitted = prepaid | |
notes | string | Instructions for the driver |
curl -X POST https://HOST/api/v1/orders \
-H "X-API-Key: drb_xxx" -H "Content-Type: application/json" \
-d '{
"merchant_ref": "10045",
"customer_name": "Ahmed Mohamed",
"customer_phone": "01012345678",
"city": "Cairo", "district": "Nasr City",
"address": "12 Abbas El Akkad St, floor 3",
"description": "2 shirts", "pieces": 2,
"cod_amount": 450
}'
Answer (one order) — the order as in Get an order:
{
"tracking_no": "DRB48213907",
"merchant_ref": "10045",
"status": "new",
"customer_name": "Ahmed Mohamed",
"customer_phone": "201012345678",
"cod_amount": 450,
"tracking_url": "https://HOST/t/9xq...",
...
}
Answer (batch): {"created": 98, "failed": 2, "results": [{"ok": true, "merchant_ref": "10045", "order": {...}}, {"ok": false, "merchant_ref": "10046", "error": "Customer phone \"12\" is not a valid number", "status": 400}]}
Get an order GET /orders/{tracking_no or merchant_ref}
{
"tracking_no": "DRB48213907", "merchant_ref": "10045", "status": "out_for_delivery",
"customer_name": "Ahmed Mohamed", "customer_phone": "201012345678",
"city": "Cairo", "district": "Nasr City", "address": "...", "lat": 30.0561, "lng": 31.3301,
"description": "2 shirts", "pieces": 2, "weight_kg": null,
"cod_amount": 450, "cod_collected": null, "delivery_fee": null, "notes": null,
"slot_date": "2026-10-08", "slot_from": "14:00", "slot_to": "18:00",
"attempts": 0, "fail_reason": null,
"created_at": "...", "confirmed_at": "...", "out_at": "...", "delivered_at": null, "updated_at": "...",
"tracking_url": "https://HOST/t/9xq..."
}List orders GET /orders
Query: status (comma-separated), updated_since (ISO date-time — poll for changes), page, limit (max 200). Newest change first.
GET /api/v1/orders?status=delivered,failed&updated_since=2026-10-07T00:00:00Z&limit=100
{ "data": [ {...}, {...} ], "page": 1, "limit": 100 }Order history GET /orders/{ref}/events
{ "data": [ {"status": "new", "kind": "status", "actor": "api", "note": "Created through the API", "created_at": "..."}, ... ] }Cancel an order POST /orders/{ref}/cancel
Body (optional): {"reason": "Customer cancelled"}. Only while the order is still in the warehouse (new, awaiting_confirmation, confirmed, assigned).
Statuses
| status | Meaning |
|---|---|
new | Received by the shipping company |
awaiting_confirmation | WhatsApp sent to the customer to confirm and choose a time |
confirmed | Customer confirmed (time slot and location chosen) |
assigned | Given to a driver |
picked_up | The driver took it from the warehouse |
out_for_delivery | On the way to the customer |
delivered | Delivered (proof: customer code and/or photo, GPS). cod_collected is set |
failed | A delivery attempt failed — see fail_reason: no_answer, refused, wrong_address, postponed, phone_off, not_available, damaged, other |
returned | Returned to the store |
cancelled | Cancelled (by the store, the customer or the company) |
Webhooks
Darb sends a POST with a JSON body to your URL each time one of your orders changes status (HTTPS only). Answer with any 2xx within 10 seconds. Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h and 6 h.
Headers: X-Darb-Event: order.status, X-Darb-Delivery: <id>, X-Darb-Signature: sha256=<hex>
{
"event": "order.status",
"previous_status": "out_for_delivery",
"status": "delivered",
"order": { "tracking_no": "DRB48213907", "merchant_ref": "10045", "status": "delivered", "cod_collected": 450, ... },
"sent_at": "2026-10-08T15:42:10.123Z"
}
A test from the company's screen sends {"event": "ping", ...}. The same delivery may arrive twice: use X-Darb-Delivery to ignore duplicates.
Verifying signatures
X-Darb-Signature is the HMAC-SHA256 of the raw request body with your webhook secret (shown once when the webhook was added).
// Node.js
const crypto = require('crypto');
const expected = 'sha256=' + crypto.createHmac('sha256', SECRET).update(rawBody).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers['x-darb-signature'] || ''));
// PHP
$expected = 'sha256=' . hash_hmac('sha256', file_get_contents('php://input'), $secret);
$ok = hash_equals($expected, $_SERVER['HTTP_X_DARB_SIGNATURE'] ?? '');Errors & limits
Errors are JSON: {"statusCode": 400, "message": "Customer phone \"12\" is not a valid number"}.
| Code | When |
|---|---|
| 400 | Invalid data (the message says which field) |
| 401 | Missing, unknown or revoked API key |
| 404 | No such order for your store |
| 409 | An order with this merchant_ref already exists |
| 429 | More than 120 requests per minute for one key |