Deliveries
A delivery is the core resource of the v2 API: an order that moves a package from a pickup location to a drop-off location, performed by an Armada driver. This page walks through the full lifecycle, the address formats, and every endpoint on the resource.
Lifecycle
Every delivery moves through a fixed sequence of statuses. A partner never writes directly to status — it's always Armada's dispatcher and driver actions that advance it.
Terminal states: completed, canceled, failed. From any
non-terminal status you can call cancel. From failed (dispatcher couldn't find a driver, or driver gave up) you can call retry.
What webhooks fire
See Webhooks for configuration. In lifecycle order:
delivery.accepted— a driver is assigned; driver details become available.delivery.en_route— driver left the pickup. Driver location webhooks start firing on the interval you configure.delivery.completed— delivered. Wallet is debited fordelivery_fee.delivery.canceled— you or the merchant canceled before terminal state. No fee.delivery.failed— dispatcher exhausted retries or driver aborted. No fee.
Origin & destination formats
The same request body supports several address shapes, so you don't have to normalize customer data yourself. Pick the format that matches what you have — Armada handles the geocoding.
Origin formats
branch_format— reference an existing branch by id. Use this when the pickup is one of the merchant's registered locations. Required field:branch_id.location_format— an ad-hoc pickup anywhere. Useful for same-day errand-style orders. Required fields:contact_name,contact_phone,latitude,longitude. Optional:first_line,floor,apartment,instructions.
Destination formats
location_format— lat/lng anywhere. Fields:contact_name,contact_phone,latitude,longitude,first_line/floor/apartment/instructions(optional).kuwait_format— structured Kuwait address. Fields:area,block,street,building. Optional floor / apartment.bahrain_format— same shape askuwait_format.ksa_format— structured Saudi address. Fields:city,district,street,building.ksa_short_format— Saudi short address code. Fields:short_address(8-char alphanumeric).
/v2/deliveriesCreate a delivery
Creates a delivery. Returns the new order with status: "pending"; the
dispatcher will assign a driver and advance the status via webhooks.
Permission: delivery:write.
Request
reference requiredstringpayment.amount requirednumberpayment.type requiredstringpaidcashorigin_format requiredstringorigin object. Use branch_format for a registered branch, location_format for an ad-hoc pickup point. branch_formatlocation_formatdestination_format requiredstringdestination object. location_formatkuwait_formatbahrain_formatksa_formatksa_short_formatscheduled_date optionalISO-8601 date-timeTS=$(date +%s%3N)
BODY='{"reference":"order-100245","payment":{"amount":4.5,"type":"paid"},"origin_format":"branch_format","origin":{"branch_id":"66af6f6c2f85f4b4c36f2031"},"destination_format":"location_format","destination":{"contact_name":"John Doe","contact_phone":"+96590000000","latitude":29.3759,"longitude":47.9774,"first_line":"Salmiya, Block 5, Street 3"}}'
SIG=$(printf "%s.POST./v2/deliveries.%s" "$TS" "$BODY" \
| openssl dgst -sha256 -hmac "$ARMADA_API_SECRET" -hex | awk '{print $NF}')
curl -sS -X POST https://api.armadadelivery.com/v2/deliveries \
-H "Authorization: Key $ARMADA_API_KEY" \
-H "x-armada-timestamp: $TS" \
-H "x-armada-signature: $SIG" \
-H "Content-Type: application/json" \
-d "$BODY"Response
status requiredstringpendingaccepteddispatcheden_routecompletedcanceledfailedcode requiredstringamount requirednumberpayment.amount). created_at requiredISO-8601 date-timecurrency requiredstringKWD, SAR. delivery_fee requirednumbercompleted. customer requiredobjectcustomer.name requiredstringcustomer.phone requiredstringcustomer.address requiredstringcustomer.latitude optionalnumber | nullcustomer.longitude optionalnumber | nulldriver requiredobjectdriver.name requiredstringdriver.phone requiredstringdriver.latitude optionalnumber | nulldriver.longitude optionalnumber | nulllogistics requiredobjectlogistics.estimated_distance requirednumberlogistics.estimated_duration requirednumberlogistics.tracking_url requiredstringlogistics.pickup_qr_url requiredstring{
"code": "A1B2",
"status": "pending",
"test_mode": true,
"amount": 4.5,
"created_at": "2026-04-16T18:12:03.117Z",
"currency": "KWD",
"delivery_fee": 2,
"customer": {
"name": "John Doe",
"phone": "+96590000000",
"address": "Salmiya, Block 5, Street 3",
"latitude": 29.3759,
"longitude": 47.9774
},
"driver": {
"name": "",
"phone": "",
"latitude": null,
"longitude": null
},
"logistics": {
"estimated_distance": 4820,
"estimated_duration": 743,
"tracking_url": "https://tracking.armadadelivery.com/A1B2",
"pickup_qr_url": "https://tracking.armadadelivery.com/A1B2?qr=pickup"
}
}Errors
400— validation failure. Response body carrieserrorandmessagenaming the offending field (e.g.destination.latitude: is required for location_format).401— missing or invalid HMAC signature, or timestamp drift > 30 s. See Authentication.403— key lacksdelivery:write.422— wallet balance insufficient for the estimated fee (only when the merchant hasforceWalletPaymenton).
/v2/deliveries/:idRetrieve a delivery
Fetch the current state of an order. Use this for reconciliation or when you need the live status without waiting for the next webhook.
Permission: delivery:read.
curl -sS https://api.armadadelivery.com/v2/deliveries/670f2a4e1f6b3c0012abcd12 \ -H "Authorization: Key $ARMADA_API_KEY" \ -H "x-armada-timestamp: $TS" \ -H "x-armada-signature: $SIG"
The returned object has the same shape as the create
response. Fields like driver.name, driver.phone, and driver.latitude/driver.longitude fill in as the order progresses.
/v2/deliveries/:id/cancelCancel a delivery
Cancels an order before it reaches a terminal state. Safe to call from any status except completed, canceled, or failed.
Permission: delivery:cancel.
Request
reason optionalstringcurl -sS -X POST https://api.armadadelivery.com/v2/deliveries/670f2a4e.../cancel \
-H "Authorization: Key $ARMADA_API_KEY" \
-H "x-armada-timestamp: $TS" \
-H "x-armada-signature: $SIG" \
-H "Content-Type: application/json" \
-d '{"reason":"customer canceled"}'/v2/deliveries/:id/retryRetry a failed delivery
Puts a failed order back in the dispatch queue. Resets the driver-search
counter to zero and moves the status to pending.
Permission: delivery:retry.
curl -sS -X POST https://api.armadadelivery.com/v2/deliveries/670f2a4e.../retry \ -H "Authorization: Key $ARMADA_API_KEY" \ -H "x-armada-timestamp: $TS" \ -H "x-armada-signature: $SIG"
Errors
409— order is not infailedstatus. You can't retry a completed or canceled order; create a new one instead.404— order not found or not owned by this merchant.
/v2/deliveries/estimateEstimate a delivery fee
Preview the delivery fee before creating the order. Same address-format options
as create; you don't need to send reference or payment.
Permission: delivery:write.
curl -sS -X POST https://api.armadadelivery.com/v2/deliveries/estimate \
-H "Authorization: Key $ARMADA_API_KEY" \
-H "x-armada-timestamp: $TS" \
-H "x-armada-signature: $SIG" \
-H "Content-Type: application/json" \
-d '{"origin_format":"branch_format","origin":{"branch_id":"..."},"destination_format":"location_format","destination":{"contact_name":"x","contact_phone":"x","latitude":29.37,"longitude":47.98}}'The /estimate/static variant uses fixed pricing (no live traffic); useful when
you want a predictable quote.
Idempotency
Every create request requires a reference. If the network fails and you retry
with the same reference, Armada returns the original order rather than creating a
duplicate. Treat reference as your source-of-truth id — something like a
database primary key, a UUID, or your own order number.