Introduction
The AnchorRide API lets you fetch shipping rates and create prepaid deliveries through Okada's own network of active riders. Every order is a real delivery — the same rider pool that handles ordinary Okada rides.
The base URL for all API requests is:
https://api.anchorride.name.ng/api/anchorride/v1
All request and response bodies are JSON. All amounts are in Nigerian Naira (NGN).
Authentication
Every request to the API is authenticated with your API key, sent as a Bearer token in the Authorization header:
Authorization: Bearer ar_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Get your API key from your dashboard after signing up. Keys are only ever shown once, at the moment they're created — store it somewhere safe.
Sandbox mode
Every API key comes in two flavors: ar_live_... keys create real, billed deliveries. ar_test_... keys let you build and test your integration risk-free — orders created with a test key skip the wallet debit entirely and are never dispatched to a real driver.
You can generate a test key from your dashboard alongside your live keys.
Simulating status changes
Since a test order never reaches a real driver, use this endpoint to manually advance it through the delivery lifecycle — it fires the exact same webhook and email notifications a real status change would, so you can test your integration end to end. Only available for orders created with a test key.
Request body
| Field | Type | Description |
|---|---|---|
| status | string | Required. One of matched, driver_en_route, in_progress, completed, cancelled. |
Example request
POST /orders/7/simulate Content-Type: application/json { "status": "driver_en_route" }
Get a rate
Returns a real fare quote for a pickup and dropoff, based on actual road distance — the exact same fare an order created with these coordinates will be charged.
Request body
| Field | Type | Description |
|---|---|---|
| pickup | object | Required. { latitude, longitude } |
| dropoff | object | Required. { latitude, longitude } |
Example request
POST /rates Content-Type: application/json { "pickup": { "latitude": 7.398657, "longitude": 3.9495069 }, "dropoff": { "latitude": 7.417546, "longitude": 3.964764 } }
Example response
200 OK { "quoteId": "e41decb7-6ecb-43a7-bd01-37cacb617e47", "quoteExpiresInSeconds": 300, "fare": 1233, "currency": "NGN", "distanceKm": 4.88, "durationMin": 11 }
quoteId when you call /orders to guarantee you're charged this price, even if it changes in the meantime. Omitting it is fine too — the order will simply be priced fresh at creation time, same as before.Create an order
Creates a real delivery and debits the fare from your wallet balance immediately. There's no separate confirmation step — call /rates first to see the fare, then call this endpoint to actually book it at that price.
Headers
| Header | Description |
|---|---|
| Idempotency-Key | Optional. Any string unique to this order attempt. If a request with the same key is retried within 24 hours (e.g. after a timeout or network error), the original response is returned and no duplicate order or charge is created. |
Request body
| Field | Type | Description |
|---|---|---|
| pickup | object | Required. { latitude, longitude } |
| dropoff | object | Required. { latitude, longitude } |
| quoteId | string | Optional. A quoteId returned from a prior call to /rates. If provided and still valid (under 5 minutes old, not already used), the order is charged at that exact locked fare instead of a freshly computed one. |
| pickupLabel | string | Optional. Human-readable pickup address, shown to the driver. |
| dropoffLabel | string | Optional. Human-readable dropoff address, shown to the driver. |
| details | string | Optional. Notes about the package for the driver. |
| externalReference | string | Optional. Your own order ID, returned back to you on every lookup and webhook so you can match it to your own system. |
Example request
POST /orders Content-Type: application/json Idempotency-Key: order-1001-attempt-1 { "pickup": { "latitude": 7.398657, "longitude": 3.9495069 }, "dropoff": { "latitude": 7.417546, "longitude": 3.964764 }, "quoteId": "e41decb7-6ecb-43a7-bd01-37cacb617e47", "pickupLabel": "Warehouse, Iwo Road", "dropoffLabel": "12 Bodija Estate", "externalReference": "ORDER-1001" }
Example response
201 Created { "orderId": 2, "rideId": 66, "externalReference": "ORDER-1001", "status": "requested", "fare": 1233, "currency": "NGN", "distanceKm": 4.88, "dropoffPin": "2360" }
Insufficient balance
If your wallet balance doesn't cover the fare, the order is not created and no charge occurs:
402 Payment Required { "message": "Insufficient wallet balance.", "required": 1233, "available": 500 }
Get order status
Looks up an order by its AnchorRide order ID (the orderId returned when you created it).
Example response
200 OK { "orderId": 2, "externalReference": "ORDER-1001", "status": "in_progress", "fare": 1233, "distanceKm": 4.88, "createdAt": "2026-08-23T06:15:09.367Z" }
Possible status values
| Status | Meaning |
|---|---|
| requested | Order created, waiting for a driver. |
| matched | A driver has been assigned. |
| driver_en_route | Driver is heading to the pickup address. |
| in_progress | Package picked up, driver en route to dropoff. |
| completed | Delivered — the driver confirmed the dropoff PIN. |
| cancelled | Order was cancelled. |
List orders
Returns your orders, most recent first, with pagination and optional status filtering.
Query parameters
| Param | Type | Description |
|---|---|---|
| limit | number | Optional. Max results per page. Default 20, maximum 100. |
| offset | number | Optional. Number of orders to skip. Default 0. |
| status | string | Optional. Filter to a single status (see the status table above). |
Example request
GET /orders?limit=20&offset=0&status=completed
Example response
200 OK { "orders": [ { "orderId": 6, "externalReference": "ORDER-1006", "status": "completed", "fare": 1295, "distanceKm": 4.88, "createdAt": "2026-08-30T09:12:41.001Z" } ], "pagination": { "total": 14, "limit": 20, "offset": 0, "hasMore": false } }
Cancel an order
Cancels an order, provided a driver hasn't already picked up the package. Whether you're refunded depends on how far along the delivery is:
| Order status when cancelled | Refund |
|---|---|
| requested | Full refund to your wallet, immediately. |
| matched | Full refund to your wallet, immediately. |
| driver_en_route | No refund — the driver is already travelling to the pickup address. |
Orders that are in_progress, completed, or already cancelled can't be cancelled through this endpoint.
Example response
200 OK { "orderId": 6, "status": "cancelled", "refunded": true, "refundAmount": 1295 }
Order can't be cancelled
409 Conflict { "error": "Order cannot be cancelled", "currentStatus": "in_progress", "message": "Order has progressed past the point where cancellation is allowed" }
Order status events
Register a webhook URL from your dashboard to be notified automatically whenever one of your orders changes status, instead of polling GET /orders/:id.
Event types
| Event | Fires when |
|---|---|
| order.driver_assigned | A driver accepts the order. |
| order.driver_en_route | Driver is heading to pickup. |
| order.picked_up | Package picked up. |
| order.delivered | Package delivered and PIN confirmed. |
| order.cancelled | Order was cancelled. |
Payload
POST https://your-server.com/your-webhook-path Content-Type: application/json X-AnchorRide-Signature: 8f2a91c3... { "event": "order.picked_up", "orderId": 2, "externalReference": "ORDER-1001", "status": "in_progress", "timestamp": "2026-08-23T06:57:14.221Z" }
Your endpoint should respond with any 2xx status to acknowledge receipt. Deliveries are logged in your dashboard so you can see what was sent and whether it succeeded.
2xx (or doesn't respond at all), AnchorRide retries up to 3 times: immediately, then after 1 minute, then after 5 minutes. Each attempt is logged separately in your dashboard's delivery history. If all 3 attempts fail, the delivery is marked failed and not retried further — GET /orders/:id is always available as a fallback source of truth.Verifying signatures
Every webhook request includes an X-AnchorRide-Signature header — an HMAC-SHA256 hash of the raw request body, signed with your webhook secret (shown once when you set your webhook URL). Verify it to confirm a request genuinely came from AnchorRide.
Node.js example
const crypto = require("crypto"); function verifySignature(rawBody, signature, secret) { const expected = crypto .createHmac("sha256", secret) .update(rawBody) .digest("hex"); return signature === expected; }
Errors
Errors return a JSON body with a message field describing what went wrong.
| Status | Meaning |
|---|---|
| 400 | Missing or invalid request fields. |
| 401 | Missing, invalid, or revoked API key. |
| 402 | Insufficient wallet balance. |
| 404 | Order not found. |
| 409 | Order can't be cancelled in its current status. |
| 500 | Something went wrong on our end. Safe to retry. |