DarbAPI v1

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

  1. Ask the shipping company for an API key for your store (they create it under Stores › your store › API & webhooks).
  2. Create orders with POST /orders. Each order gets a tracking number and a public tracking page.
  3. 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.

FieldTypeNotes
merchant_refstringYour order number (recommended)
customer_namestringrequired
customer_phonestringrequiredMobile; local format (010…) or international (+20…)
customer_phone2string
citystringGovernorate / city — used to route the order to a delivery zone
districtstringArea / district — used to route the order to a delivery zone
addressstringStreet, building, floor…
lat, lngnumberMap position, if you have it
descriptionstringContents
piecesintegerDefault 1
weight_kgnumber
cod_amountnumberCash to collect on delivery; 0 or omitted = prepaid
notesstringInstructions 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

statusMeaning
newReceived by the shipping company
awaiting_confirmationWhatsApp sent to the customer to confirm and choose a time
confirmedCustomer confirmed (time slot and location chosen)
assignedGiven to a driver
picked_upThe driver took it from the warehouse
out_for_deliveryOn the way to the customer
deliveredDelivered (proof: customer code and/or photo, GPS). cod_collected is set
failedA delivery attempt failed — see fail_reason: no_answer, refused, wrong_address, postponed, phone_off, not_available, damaged, other
returnedReturned to the store
cancelledCancelled (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"}.

CodeWhen
400Invalid data (the message says which field)
401Missing, unknown or revoked API key
404No such order for your store
409An order with this merchant_ref already exists
429More than 120 requests per minute for one key