API reference
Create and track deliveries from your own website or app. All requests use JSON over HTTPS.
Authentication
Create keys in the portal under Developers. Send them as a bearer token. dlv_test_… keys work immediately, never dispatch couriers and never charge your wallet. dlv_live_… keys need a verified business.
curl https://dlivri.auvia.business/api/v1/wallet \
-H "Authorization: Bearer dlv_test_xxxxxxxx"Errors return an HTTP status and { "error": { "code", "message" } }. Rate limit: 300 requests per minute per key.
Orders and legs
An order is one job for one customer. It has one or two legs (courier trips):
type: "single", direction: "deliver"— your facility → customer (default).type: "single", direction: "collect"— customer → your facility.type: "round_trip"— customer → facility, then back to the customer. The return leg waits until you call/release, or untilreturn.scheduledFor.
Addresses accept latitude/longitude plus an optional ghanaPostCode. Resolve a GhanaPost GPS address first with the address endpoint. Each leg has a 4-digit pin that the courier must collect at hand-over, unless you set proofMode: "photo_only".
GET/addresses/ghanapost/{code}
Resolve a GhanaPost GPS address to coordinates.
curl https://dlivri.auvia.business/api/v1/addresses/ghanapost/GA-492-6343 -H "Authorization: Bearer $KEY"
{ "object": "address", "code": "GA-492-6343", "latitude": 5.65363, "longitude": -0.19927,
"region": "Greater Accra", "district": "Ayawaso West", "area": "University Of Ghana" }GET/addresses/reverse?lat=&lng=
The GhanaPost GPS address for a point.
POST/quotes
Price an order without booking it. Same body as POST /orders.
POST/orders
Create an order (or up to 100 with { "orders": [...] }). Live orders are charged to your wallet. Send an Idempotency-Key header to make retries safe.
curl -X POST https://dlivri.auvia.business/api/v1/orders \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: order-LAU-2002" \
-d '{
"type": "round_trip",
"reference": "LAU-2002",
"facilityIndex": 0,
"customer": {
"name": "Kofi Mensah", "phone": "0201234567",
"place": { "latitude": 5.6355, "longitude": -0.1612, "address": "East Legon", "ghanaPostCode": "GA-412-3456" }
},
"package": { "size": "medium", "category": "other", "description": "Bag of laundry" },
"vehicleType": "motorbike",
"return": { "mode": "on_release" }
}'Fields: facilityIndex (a saved facility) or an inline facility; package.size is small, medium or large; vehicleType is motorbike, tricycle or car (defaults to the best fit);pickupAt (ms epoch) schedules the first leg; proofMode; notes.
{
"id": "q7Yc…", "object": "order", "livemode": true, "type": "round_trip",
"status": "in_progress", "total_charged": 37.55, "currency": "GHS",
"legs": [
{ "id": "tr_1…", "kind": "pickup", "status": "searching", "fare": 18.84, "pin": "4821", "tracking_url": "https://auvia.business/track/…" },
{ "id": "tr_2…", "kind": "return", "status": "awaiting_release", "fare": 18.71, "pin": "0937", "tracking_url": "…" }
]
}GET/orders/{id}
Order with live courier details, location and proof-of-delivery photos per leg.
GET/orders?reference={yourRef}
Look up orders by your own reference, or list recent orders (limit up to 100).
POST/orders/{id}/release
Send the return leg of a round trip once the items are ready (after the pickup leg is delivered).
POST/orders/{id}/cancel
Cancel every leg not yet picked up. Their fares are refunded to your wallet.
GET/wallet
Current wallet balance in GHS.
POST/test/orders/{id}/advance
Test keys only: move a test order’s next leg forward one step and fire the matching webhooks.
Webhooks
Set an https endpoint under Developers. Events: order.created, leg.courier_assigned, leg.picked_up,leg.delivered, leg.failed, order.awaiting_return, order.completed, order.cancelled. Failed deliveries are retried up to 5 times. Respond with any 2xx status.
Verify each request: X-Dlivri-Signature: t=<unix>,v1=<hex> where v1 is HMAC-SHA256 of <t>.<raw body> with your signing secret.
// Node.js
const crypto = require('crypto');
function verify(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected)) && Date.now() / 1000 - Number(t) < 300;
}