HMV
Courier APIv1
Get your API keys

HMV Courier API

A REST API to quote, book, track, label and get real-time webhooks for deliveries — built for storefronts, WMS integrations and shipping aggregators.

💸
Prepaid walletBookings draw from your HMV wallet balance.
📍
Coordinates inSend lat/lng — no geocoding round-trips.
🔔
WebhooksSigned status pushes, no polling.

The API is organised around predictable, resource-oriented URLs, returns JSON, and uses standard HTTP verbs and status codes. Every response shares one envelope so you branch on a single field.

🔑
Paste an API key in the bar above and every Run button on this page will fire a real request against your account.
Base URL
# All endpoints are relative to
https://api.hmvcourier.com

# Authenticate every request
Authorization: Bearer hmv_live_...
The envelope

            

          

Authentication

Authenticate with your secret API key as a bearer token. Create and revoke keys in Settings → API. A key is shown once — store it securely.

⚠️
Keep keys server-side. A key can create bookings that spend your wallet. Never expose it in a browser, mobile app, or public repo. Revoke instantly if leaked.

Rate & scope

Keys are scoped to your business — every request only ever sees and affects your own bookings and wallet.


            

Requests & errors

Send JSON bodies with Content-Type: application/json. Money is in naira (NGN) as plain numbers — not kobo. Timestamps are Africa/Lagos local time.

On failure, the HTTP status gives the class and error.code is a stable machine-readable string.

Statuserror.codeMeaning
401unauthorizedMissing or invalid API key.
402insufficient_fundsWallet balance below the booking price.
404not_foundNo such endpoint, or a booking that isn't yours.
405method_not_allowedWrong HTTP verb.
409not_cancellableBooking already picked up / cancelled.
409idempotency_conflictIdempotency-Key reused with a different body.
422invalid_requestA field is missing or malformed.
422cross_state_not_allowedsame_day/next_day across two states.

Idempotency

Safely retry a POST /v1/bookings without risk of a double booking or double wallet charge: send an Idempotency-Key header with a unique value (a UUID) per booking. If a request with that key already completed, we return the original response verbatim (with Idempotent-Replayed: true) instead of booking again.

Reusing a key with a different body returns 409 idempotency_conflict. Keys are scoped to your business; use a fresh key for each new booking.

Error example

          
Idempotent booking
curl https://api.hmvcourier.com/v1/bookings \
  -H "Authorization: Bearer hmv_live_..." \
  -H "Idempotency-Key: 3f1c9e2a-7b6d-4a11-9c02-8e5f" \
  -H "Content-Type: application/json" \
  -d '{ ... }'

# Retry the exact same call → same response, charged once.

Webhooks

Register endpoint URLs in Settings → API → Webhooks. When a booking changes status we POST a signed JSON event to every active endpoint — so you never poll.

Event

booking.status_changed
Fires on every status transition after a booking is created. data.status is the new status, data.previous_status the one before.

Delivery & retries

We expect a 2xx within 8s. Non-2xx or timeout is retried with exponential backoff (immediate, 1m, 5m, 30m, 2h, 6h) for up to 6 attempts. Make your handler idempotent using the event id.

Statuses you'll see

picked_up · at_origin_hub · in_transit · at_destination_hub · out_for_delivery · delivered · failed · returned · cancelled

Event payload

          
Request headers
POST /your/webhook  HTTP/1.1
Content-Type: application/json
HMV-Event: booking.status_changed
HMV-Delivery: 4821
HMV-Signature: t=1753329123,v1=3f9a…

Verifying signatures

Each delivery is signed so you can trust it came from HMV. The HMV-Signature header is t=<unix>,v1=<hex> where v1 = HMAC_SHA256(secret, "<t>.<raw_body>").

Recompute it with your endpoint's signing secret (whsec_…) over the raw request body and compare in constant time. Optionally reject if t is more than a few minutes old.