Eazer

Partner Delivery API

Keep your own checkout. Dispatch Eazer couriers for pickup → dropoff, settle fees from a prepaid wallet, and get signed status webhooks in real time.

Create partner accountAPI quickstart

Overview

v1 is courier-only. Your customers pay you; you pay Eazer for the delivery leg via a prepaid partner wallet.

  • Base URL: https://api.eazer.com/api
  • Partner routes live under /partner/v1
  • Jobs reuse Eazer’s package delivery engine (matching, tracking, proof of delivery)
  • Food / kitchen orders without an Eazer menu, and customer-pay-to-Eazer checkout, are out of scope for v1

Quickstart

Quote a fee, create a delivery with your order id, then poll or wait for webhooks.

  1. Receive an API key and wallet top-up from Eazer ops
  2. Call POST /partner/v1/deliveries/quote
  3. Call POST /partner/v1/deliveries with a unique externalId
  4. Share the returned trackingUrl or poll GET /deliveries/:id
  5. Verify inbound webhooks with your webhook secret
bashQuote
curl -X POST https://api.eazer.com/api/partner/v1/deliveries/quote \
  -H "Authorization: Bearer eaz_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Market-Code: NG" \
  -d '{
    "pickupLat": 6.45,
    "pickupLng": 3.39,
    "dropoffLat": 6.50,
    "dropoffLng": 3.35,
    "packageSize": "MEDIUM",
    "packageWeightKg": 2
  }'

Authentication

API keys are issued by Eazer ops. The plaintext key is shown once at creation — store it securely.

Send the key with either header:

http
Authorization: Bearer eaz_live_…
# or
X-Eazer-Api-Key: eaz_live_…
  • Keys are stored hashed; revoked keys are rejected immediately
  • Partner routes are rate-limited separately (60 requests / minute)
  • Do not embed live keys in client-side apps or public repos

Markets

Each partner has a default market and an allow-list of markets.

Override the market with X-Market-Code (for example NG or CA). The code must be allowed for your partner account; otherwise the API returns 403.

Fees and currency follow the market’s package pricing (same rules as in-app package delivery).

Wallet

Delivery fees are deducted from your prepaid partner wallet at create time.

bashBalance
curl https://api.eazer.com/api/partner/v1/wallet \
  -H "Authorization: Bearer eaz_live_YOUR_KEY"

Insufficient balance returns 402 Payment Required. Ops credits your wallet; cancelled or expired jobs that were never picked up are refunded to the wallet.

Endpoints

MethodPathPurpose
POST/partner/v1/deliveries/quoteFee and distance estimate
POST/partner/v1/deliveriesCreate courier job (debits wallet)
GET/partner/v1/deliveries/:idGet status, fee, driver, tracking URL
GET/partner/v1/deliveries/by-external/:externalIdLookup by your order id
POST/partner/v1/deliveries/:id/cancelCancel if not yet picked up
GET/partner/v1/walletPrepaid wallet balance
POST/partner/v1/webhooks/testSend a signed test webhook
GET/public/deliveries/track/:tokenPublic tracking page data (no API key)

Paths above are relative to https://api.eazer.com/api.

Quote a delivery

Estimate fee and distance before creating a job. Does not debit the wallet.

bash
curl -X POST https://api.eazer.com/api/partner/v1/deliveries/quote \
  -H "Authorization: Bearer eaz_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Market-Code: NG" \
  -d '{
    "pickupLat": 6.45,
    "pickupLng": 3.39,
    "dropoffLat": 6.50,
    "dropoffLng": 3.35,
    "packageSize": "MEDIUM",
    "packageWeightKg": 2
  }'

Body fields:

  • pickupLat, pickupLng, dropoffLat, dropoffLng (required)
  • packageSize — SMALL | MEDIUM | LARGE (default MEDIUM)
  • packageWeightKg (optional)

Create a delivery

Creates a PackageDelivery job, debits your wallet, and notifies online couriers.

bash
curl -X POST https://api.eazer.com/api/partner/v1/deliveries \
  -H "Authorization: Bearer eaz_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Market-Code: NG" \
  -H "Idempotency-Key: order-1001" \
  -d '{
    "pickupAddress": "12 Marina, Lagos",
    "pickupLat": 6.45,
    "pickupLng": 3.39,
    "dropoffAddress": "Ikeja City Mall",
    "dropoffLat": 6.60,
    "dropoffLng": 3.35,
    "receiverName": "Ada Okeke",
    "receiverPhone": "+2348012345678",
    "packageSize": "MEDIUM",
    "packageWeightKg": 2,
    "packageDescription": "Documents",
    "note": "Ring doorbell",
    "externalId": "order-1001",
    "metadata": { "channel": "web" },
    "prohibitedItemsAcknowledged": true
  }'

Idempotency: the same externalId per partner returns the existing delivery. Optional header Idempotency-Key also keys the wallet debit ledger entry.

Example response:

json
{
  "id": "clx…",
  "externalId": "order-1001",
  "status": "REQUESTED",
  "fee": 1200,
  "estimatedFee": 1200,
  "finalFee": null,
  "distanceKm": 5.6,
  "trackingToken": "…",
  "trackingUrl": "https://api.eazer.com/api/public/deliveries/track/…",
  "pickupAddress": "12 Marina, Lagos",
  "dropoffAddress": "Ikeja City Mall",
  "receiverName": "Ada Okeke",
  "receiverPhone": "+2348012345678",
  "packageSize": "MEDIUM",
  "paymentMethod": "partner_wallet",
  "driver": null,
  "createdAt": "2026-10-14T16:00:00.000Z"
}

Retrieve & cancel

Fetch by Eazer id or your external id. Cancel is allowed until the package is picked up.

bashGet by id
curl https://api.eazer.com/api/partner/v1/deliveries/DELIVERY_ID \
  -H "Authorization: Bearer eaz_live_YOUR_KEY" \
  -H "X-Market-Code: NG"
bashGet by external id
curl https://api.eazer.com/api/partner/v1/deliveries/by-external/order-1001 \
  -H "Authorization: Bearer eaz_live_YOUR_KEY"
bashCancel
curl -X POST https://api.eazer.com/api/partner/v1/deliveries/DELIVERY_ID/cancel \
  -H "Authorization: Bearer eaz_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Customer cancelled" }'

Cancel is blocked once status is PICKED_UP or IN_TRANSIT. Eligible cancels refund the delivery fee to your partner wallet.

Public tracking

Share a tracking URL with your customer. No API key is required.

bash
curl https://api.eazer.com/api/public/deliveries/track/TRACKING_TOKEN

Response includes status, addresses, receiver first name, coarse location label, and a limited driver name — not full phone/PII. Prefer the trackingUrl returned at create time.

Webhooks

Configure a webhook URL with Eazer ops. Events are signed and delivered at-least-once with retries.

Headers

  • X-Eazer-Timestamp — Unix seconds
  • X-Eazer-Signature — hex HMAC-SHA256 of {timestamp}.{rawBody} using your webhook secret

Event types

  • delivery.requested
  • delivery.assigned
  • delivery.picked_up
  • delivery.in_transit
  • delivery.delivered
  • delivery.cancelled
  • delivery.test — from POST /partner/v1/webhooks/test
jsonExample payload
{
  "id": "evt_clx…_REQUESTED",
  "type": "delivery.requested",
  "createdAt": "2026-10-14T16:00:00.000Z",
  "data": {
    "deliveryId": "clx…",
    "externalId": "order-1001",
    "status": "REQUESTED",
    "trackingToken": "…",
    "fee": 1200,
    "receiverName": "Ada Okeke",
    "pickupAddress": "12 Marina, Lagos",
    "dropoffAddress": "Ikeja City Mall",
    "driver": null
  }
}
javascriptVerify signature (Node)
const crypto = require('crypto');

function verifyEazerWebhook(rawBody, timestamp, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected, 'utf8'),
    Buffer.from(signature, 'utf8'),
  );
}

// Express example
app.post('/eazer-hooks', express.raw({ type: 'application/json' }), (req, res) => {
  const timestamp = req.headers['x-eazer-timestamp'];
  const signature = req.headers['x-eazer-signature'];
  const rawBody = req.body.toString('utf8');

  if (!verifyEazerWebhook(rawBody, timestamp, signature, process.env.EAZER_WEBHOOK_SECRET)) {
    return res.status(401).send('invalid signature');
  }

  const event = JSON.parse(rawBody);
  // Handle event.type: delivery.assigned, delivery.delivered, …
  res.status(200).send('ok');
});

Errors

StatusMeaning
401Missing or invalid API key
402Insufficient partner wallet balance
403Partner inactive or market not allowed for this key
404Delivery not found
409Duplicate create race (safe to retry / fetch by externalId)
429Rate limited (partner routes: 60 req/min)

Status lifecycle

Each transition emits a matching webhook when a webhook URL is configured.

StatusWebhookNotes
REQUESTEDdelivery.requestedJob broadcast to available couriers
DRIVER_ASSIGNEDdelivery.assignedCourier accepted the job
PICKED_UPdelivery.picked_upPackage collected from pickup
IN_TRANSITdelivery.in_transitEn route to dropoff
DELIVEREDdelivery.deliveredCompleted with proof of delivery
CANCELLEDdelivery.cancelledCancelled or expired; wallet refunded when applicable

Partner portal

Create an account, link a card (Stripe outside NG, Paystack in NG), generate API keys, and view deliveries and invoices. You are charged when each delivery completes.

Need help from ops first? Use the form below or email info@eazer.com.

Or email info@eazer.com