Skip to Content
Orders & Deliveries

Orders & Deliveries

An order is a tenant-scoped delivery job. POST /v1/orders persists unified_json (schema version 1). Status is stored in unified_json.status (string, normalized by workers and PATCH /v1/orders/:id).


Creating an order

Headers

HeaderRequiredDescription
AuthorizationYesBearer ilv_… (integration key) or merchant access JWT
Content-TypeYesapplication/json
Idempotency-KeyNo≤255 chars. Same tenant + key + identical JSON body replays the stored response (idempotent: true/false on 200/201). Same key with a different body → 409 idempotency_conflict. Independent of natural idempotency on (source, external_order_id).

Required body (API behaviour)

The API validates standardized.scheduled_pickup_at, standardized.delivery_window_start, and standardized.delivery_window_end (ISO-8601 datetimes). Pickup must be in the future; delivery window start must be before end; pickup must be before window end.

curl -X POST https://api.ideliver.ng/v1/orders \ -H "Authorization: Bearer ilv_test_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: create-ord-001-retry" \ -d '{ "schema_version": "1", "source": "api", "external_order_id": "store-order-8842", "raw_payload": {}, "standardized": { "pickup_address": "12 Bode Thomas Street, Surulere, Lagos", "dropoff_address": "14 Awolowo Road, Ikoyi, Lagos", "customer_phone": "+2348011122233", "customer_name": "Amara Obi", "pickup_lat": 6.4969, "pickup_lng": 3.3506, "dropoff_lat": 6.4474, "dropoff_lng": 3.4296, "scheduled_pickup_at": "2026-05-01T14:00:00.000Z", "delivery_window_start": "2026-05-01T15:00:00.000Z", "delivery_window_end": "2026-05-01T18:00:00.000Z", "notes": "Gate code 1234", "cod_amount_minor": 250000, "cod_currency": "NGN", "weight_kg": 3.5, "length_cm": 30, "width_cm": 20, "height_cm": 15, "is_fragile": false } }'
const order = await client.orders.create({ schema_version: "1", source: "api", external_order_id: "store-order-8842", raw_payload: {}, standardized: { pickup_address: "12 Bode Thomas Street, Surulere, Lagos", dropoff_address: "14 Awolowo Road, Ikoyi, Lagos", customer_phone: "+2348011122233", customer_name: "Amara Obi", pickup_lat: 6.4969, pickup_lng: 3.3506, dropoff_lat: 6.4474, dropoff_lng: 3.4296, scheduled_pickup_at: "2026-05-01T14:00:00.000Z", delivery_window_start: "2026-05-01T15:00:00.000Z", delivery_window_end: "2026-05-01T18:00:00.000Z", cod_amount_minor: 250000, cod_currency: "NGN", weight_kg: 3.5, length_cm: 30, width_cm: 20, height_cm: 15, is_fragile: false, }, });

Core fields

FieldNotes
schema_versionMust be "1"
sourceIngestion source (e.g. api, shopify)
external_order_idYour unique id — natural idempotency with source
raw_payloadOpaque object from your system (may be {})
standardized.*Addresses, phones, optional lat/lng, windows, items, notes, optional cod_amount_minor / cod_currency
standardized.weight_kgOptional parcel weight in kg (max 500). Enables automatic vehicle type suggestion
standardized.length_cmOptional parcel length in cm (max 500)
standardized.width_cmOptional parcel width in cm (max 500)
standardized.height_cmOptional parcel height in cm (max 500)
standardized.is_fragileOptional boolean — upgrades suggested vehicle (e.g. motorcycle → car)
standardized.vehicle_type_hintOptional — auto-set from dimensions when omitted: motorcycle, car, van, truck

Response

201 (new) or 200 (duplicate external_order_id) includes order with:

  • id — IDeliver UUID
  • customer_tracking_url — public tracking link when TRACKING_PUBLIC_BASE_URL is configured on the API; otherwise null
  • rider — assigned rider summary when standardized.rider_id is set

Coverage gate: When pickup_lat/lng or dropoff_lat/lng are provided, the API validates them against your merchant dispatch zones. Coordinates outside all active zones are rejected with 400 OUT_OF_COVERAGE_AREA. Call GET /v1/coverage/check first to verify. See API Reference — Coverage Check and Dispatch & Zones.


Order status (unified_json.status)

Statuses are free-form strings up to 128 chars; typical values include:

StatusMeaning (typical)
pendingAccepted / awaiting dispatch
acceptedRider accepted
picked_upCollected at pickup
in_transitEn route to customer
deliveredCompleted
cancelledCancelled

Exact transitions depend on dispatch, rider app, and PATCH /v1/orders/:id. See Order lifecycle & cancellation.


Cancelling an order (merchant API)

curl -X POST "https://api.ideliver.ng/v1/orders/ORDER_UUID/cancel" \ -H "Authorization: Bearer ilv_test_YOUR_KEY_HERE" \ -H "Content-Type: application/json" \ -d '{"reason":"Customer requested cancel","notes":"optional"}'
  • 200 — cancelled or already_cancelled: true if already cancelled
  • 409 cannot_cancel — terminal or disallowed status (same policy as admin cancel; env ADMIN_ORDER_CANCEL_PRE_PICKUP_ONLY tightens rules)
  • order.status_updated outbound webhook fires on successful cancel

Admin break-glass: POST /v1/admin/orders/:orderId/cancel (separate auth).


Idempotency (natural + header)

  1. (source, external_order_id) — resubmitting the same pair returns the existing order (200) with idempotent: true.
  2. Idempotency-Key — optional header for safe retries when the body is byte-identical; conflicts return 409 idempotency_conflict.

Listing & patching

  • GET /v1/orders — newest first; filters status, external_order_id, is_test, etc.
  • GET /v1/orders/:id — one order (merchant-scoped)
  • PATCH /v1/orders/:id — update status and/or payment_verified (commission paths env-gated)

Next steps