← Back to Tariq

Integration Guide

Connect your existing delivery, ERP or e-commerce system to Tariq's route optimization. Three API calls cover the whole loop.

ملخّص لصاحب الشركة

هذه الصفحة موجّهة لمبرمج أو مسؤول الأنظمة في شركتك. لا تحتاج لتغيير برنامجك الحالي — يبقى كما هو، ونضيف له تحسين المسارات فقط.

البرنامج عندك يرسل الطلبات إلى طريق، وطريق يرتّبها على السائقين بأفضل مسار، ثم يُرجع لك حالة كل طلب لحظة تسليمه. العمل المطلوب من المبرمج عادةً يوم إلى يومين.

How it works

Tariq does not replace your system. It sits beside it: you keep your orders, customers and invoicing where they are, and Tariq handles the part none of those systems do — deciding which driver takes which stop, in which order.

STEP 1
Push orders
Your system sends the day's orders to Tariq.
STEP 2
Optimize
Tariq assigns and sequences every stop per driver.
STEP 3
Get results back
Tariq calls your system the moment a stop is closed.

Drivers use the Tariq Android app — nothing to build on your side. Your dispatcher can keep working in your existing software; Tariq's dashboard is optional.

Before you start

Getting an API key

In the Tariq dashboard, sign in as the account owner and go to Settings → API Keys → Create key. The key is shown once and starts with tk_. Store it as a server-side secret.

Do not put the API key in a browser, a mobile app, or anything a customer can open. It grants full access to your company's order data. Calls must be made from your server.

Step 1 — Push orders into Tariq

Send orders whenever your system creates them, or in one batch each morning. Both work.

POST /api/v1/orders — one order

curl -X POST https://tariq.codeator.ai/api/v1/orders \
  -H "X-API-Key: tk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "customer": "Zainab Mohammed",
    "phone": "07701234567",
    "area": "Karrada",
    "cod": 45000,
    "lat": 33.3140,
    "lng": 44.3930,
    "priority": "normal",
    "time_window": "morning",
    "external_ref": "YOUR-ORDER-NO-8891",
    "barcode": "PKG-2026-0142"
  }'

# 200 OK
{"id": 4417, "status": "pending", "external_ref": "YOUR-ORDER-NO-8891", "duplicate": false, "message": "Created"}
Send external_ref, and send it every time. It is your own order number, and Tariq keeps it separate from barcode — which is the sticker on the parcel, scanned by the driver. A company that has both can send both.

It also makes retries safe. If your request times out you will send it again, which is correct — and without a reference Tariq would create a second order and a driver would knock twice. Post the same external_ref again and you get the order that already exists, with "duplicate": true, and nothing new is created. Every webhook carries external_ref back, so you never need a mapping table.

POST /api/v1/orders/bulk — up to 500 at once

curl -X POST https://tariq.codeator.ai/api/v1/orders/bulk \
  -H "X-API-Key: tk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '[
    {"customer":"Ali Hasan","phone":"07701111111","area":"Mansour","cod":120000,"lat":33.345,"lng":44.348},
    {"customer":"Noor Sabri","phone":"07702222222","area":"Dora","cod":70000,"lat":33.254,"lng":44.391}
  ]'

# 200 OK — partial success is normal: check "errors"
{"created": 2, "errors": [], "orders": [{"id":4418,...},{"id":4419,...}]}
A bad row does not fail the batch. Valid rows are created and the rejected ones come back in errors with their array index. Always read errors.

Order fields

FieldRequiredNotes
customeryesRecipient name
phoneyesIraqi mobile, e.g. 07701234567
areayesDistrict name, e.g. Karrada
codyesInteger IQD. Use 0 for prepaid orders
lat, lngnoStrongly recommended — routing quality depends on it
prioritynonormal (default) or urgent
time_windownoany (default), morning, afternoon, evening
external_refnoYour own order number. Returned in every webhook, and a repeat with the same value returns the existing order instead of creating a duplicate
barcodenoThe barcode printed on the parcel, scannable by the driver app. Not the same thing as external_ref
store_idnoOrigin branch, if you run more than one
secondary_phonenoAlternate number
address_descriptionnoFree text landmark — useful in Baghdad
notesnoNote shown to the driver

Step 2 — Optimize

Call this once when the day's orders are loaded. Tariq solves the routing problem and assigns every pending order to a driver with a stop number. That assignment is what the drivers' phones read.

GET /api/v1/routes

curl "https://tariq.codeator.ai/api/v1/routes?depart_hour=8" \
  -H "X-API-Key: tk_your_key_here"

# 200 OK
{
  "routes": [
    {
      "driver": 1,
      "driver_id": 12,
      "driver_name": "Hussein Kareem",
      "driver_phone": "07801234567",
      "google_maps_url": "https://www.google.com/maps/dir/...",
      "orders": [
        {"stop":1, "order_id":4417, "customer":"Zainab Mohammed", "area":"Karrada", "cod":45000},
        {"stop":2, "order_id":4419, "customer":"Noor Sabri",      "area":"Dora",    "cod":70000}
      ]
    }
  ],
  "depart_hour": 8,
  "traffic_multiplier": 1.5,
  "total_time_seconds": 7420
}
This call writes. Despite being a GET, it publishes a new plan and reassigns every pending order. Call it when you want to (re)plan — do not poll it on a timer, or drivers will see their stops change under them.

Optional parameters

ParameterDefaultMeaning
depart_hourcurrent hourHour of day (0–23) drivers leave. Baghdad traffic is modelled per hour, so this changes the result
store_idallPlan one branch only. The branch's location becomes the start point
depot_lat, depot_lngBaghdad centreExplicit start point, if you do not use branches
Orders may be left out of the result if the solver cannot fit them (too far, impossible time window, too few drivers). The response has no "dropped" list — compare the order_ids you pushed against those returned, and anything missing stays pending for the next run.

Step 3 — Get results back

Two options. Webhooks are strongly preferred: your system learns about a delivery within seconds instead of minutes, and you make no wasted calls.

Option A — Webhooks (recommended)

In the Tariq dashboard: Settings → Webhooks → Add webhook. Enter your HTTPS endpoint and a shared secret you generate. Tariq POSTs JSON to that URL whenever an order changes state.

// Tariq POSTs this to your endpoint
{
  "event": "order.delivered",
  "company_id": 7,
  "timestamp": "2026-09-22T11:04:19.223401",
  "data": {
    "order_id": 4417,
    "status": "delivered",
    "customer": "Zainab Mohammed",
    "phone": "07701234567",
    "area": "Karrada",
    "cod": 45000,
    "cod_collected": 45000,
    "barcode": "YOUR-SYSTEM-REF-8891",
    "assigned_driver_id": 12,
    "received_by": "Zainab",
    "fail_reason": null,
    "fail_reason_code": null,
    "postpone_reason": null,
    "postpone_reason_code": null,
    "postponed_to": null,
    "delivered_at": "2026-09-22T11:04:18",
    "completed_at": "2026-09-22T11:04:18"
  }
}

Events

EventFires when
order.createdA single order is created via the API
order.bulk_createdA bulk import finishes. Payload is {count, order_ids}, not a full order
order.deliveredDriver marks the stop delivered
order.failedDelivery failed — see fail_reason_code
order.postponedRescheduled — see postponed_to
order.returnedReturned to warehouse
order.exchangedExchanged at the door

Leave the event list empty to receive all of them, or subscribe to only the ones you need.

Verifying the signature

Every request carries X-Tariq-Signature: sha256=<hex> — an HMAC-SHA256 of the raw request body, keyed with your shared secret. Verify it before trusting the payload.

Hash the raw bytes. Parsing the JSON and re-serializing it changes the whitespace and the signature will not match. Read the body as text/bytes first, verify, then parse.
# Python / Flask
import hmac, hashlib

@app.route("/tariq-webhook", methods=["POST"])
def tariq_webhook():
    raw = request.get_data()                      # raw bytes, not request.json
    expected = "sha256=" + hmac.new(SECRET.encode(), raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, request.headers.get("X-Tariq-Signature", "")):
        return "", 401
    event = request.get_json()
    # ... update your own order record here ...
    return "", 200
// Node / Express — note express.raw, not express.json
app.post("/tariq-webhook", express.raw({type: "application/json"}), (req, res) => {
  const expected = "sha256=" + require("crypto")
    .createHmac("sha256", SECRET).update(req.body).digest("hex");
  if (expected !== req.headers["x-tariq-signature"]) return res.sendStatus(401);
  const event = JSON.parse(req.body.toString());
  // ... update your own order record here ...
  res.sendStatus(200);
});
// PHP
$raw = file_get_contents("php://input");
$expected = "sha256=" . hash_hmac("sha256", $raw, $SECRET);
if (!hash_equals($expected, $_SERVER["HTTP_X_TARIQ_SIGNATURE"] ?? "")) { http_response_code(401); exit; }
$event = json_decode($raw, true);
// ... update your own order record here ...

Delivery rules

Option B — Polling

If you cannot expose a public endpoint, poll instead. Every 5–10 minutes is enough; the data does not change faster than drivers move.

# Everything closed out — poll each status you care about
curl "https://tariq.codeator.ai/api/v1/orders?status=delivered" -H "X-API-Key: tk_..."
curl "https://tariq.codeator.ai/api/v1/orders?status=failed"    -H "X-API-Key: tk_..."

# Full detail for one order, including POD and signature flags
curl "https://tariq.codeator.ai/api/v1/orders/4417" -H "X-API-Key: tk_..."

Returns the 200 most recent orders, newest first.

Other endpoints

EndpointPurpose
GET /api/v1/ordersList orders. Optional ?status=
GET /api/v1/orders/{id}One order, full detail including driver notes and POD flags
PATCH /api/v1/orders/{id}Update status from your side. Body: {"status":"delivered"}
GET /api/v1/driversDrivers with their last known GPS position
PATCH fires the same webhooks as the driver app, so if your system is the one closing orders, guard against reacting to your own update.

Status values

StatusMeaning
pendingWaiting to be delivered. Only these are routed
deliveredDelivered. cod_collected holds the cash the driver actually took — it is null when the order was closed through the API rather than the app
failedAttempted, not delivered
postponedRescheduled to postponed_to
returned / exchangedReturned to warehouse / swapped at the door

Errors

CodeMeaning
400Missing or invalid fields — the message says which
401Missing, wrong or deactivated X-API-Key
403Subscription expired or suspended
404Order or branch not found, or belongs to another company

A realistic first integration

  1. Create a key and push one test order. Confirm it appears in the Tariq dashboard
  2. Add a webhook pointing at your staging endpoint and press Test until the signature verifies
  3. Push a real morning's orders with /orders/bulk, then call /routes
  4. Hand the drivers the app and run one day in parallel with your current process
  5. Once the callbacks line up with reality, switch over

Most teams finish this in one to two days.

Questions

Email tariq@codeator.ai with your company name and what you are connecting, and we will help directly.