Quotes and creating shipments
Last updated August 1, 2026
Creating a shipment is a two-step flow: get a rate quote, then confirm it. This guarantees the price you show your customer at checkout is exactly the price you're charged, even if rates change between the two calls.
Service types
Every quote and shipment requires a serviceType, since it determines which pricing model applies:
serviceType | What it means | Billed by | Max weight |
|---|---|---|---|
consolidated | Shared load / waybill. Your package travels alongside others on the same route. | Weight class, by trip type | 200kg |
dedicated | A vehicle chartered just for your shipment. Faster and private, at a higher cost. | Haulage class, by distance | 4500kg |
Step 1: Request a rate quote
POST /rates/estimate
| Field | Type | Required | Notes |
|---|---|---|---|
serviceType | consolidated | dedicated | yes | See above |
pickupAddress | string | yes | Street-level address |
pickupCity | string | yes | |
pickupState | string | yes | Must be a real Nigerian state |
dropoffAddress | string | yes | |
dropoffCity | string | yes | |
dropoffState | string | yes | |
packageWeightKg | number | yes | Weight in kilograms. Up to 200kg for consolidated, up to 4500kg for dedicated |
Every address is geocoded and validated to confirm it resolves to a real, deliverable location inside Nigeria. Addresses that can't be located, or resolve outside Nigeria, are rejected with invalid_request.
Response:
{
"success": true,
"sandbox": true,
"data": {
"quoteId": "QUOTE-A1B2C3D4E5F6",
"quoteExpiresAt": "2026-08-01T14:32:00.000Z",
"tripType": "interstate",
"serviceType": "consolidated",
"pickup": { "address": "12 Admiralty Way, Lekki, Lagos", "city": "Lekki", "state": "Lagos", "lat": 6.4406, "lng": 3.4739 },
"dropoff": { "address": "5 Ademola Adetokunbo St, Wuse 2, Abuja", "city": "Wuse 2", "state": "Abuja", "lat": 9.0765, "lng": 7.3986 },
"distanceKm": 462.3,
"packageWeightKg": 2.5,
"etaDays": 2,
"etaBufferedMinutes": 2880,
"etaAt": "2026-08-03T14:32:00.000Z",
"pricing": { "subtotal": 12500, "vatPercent": 7.5, "vatAmount": 900, "total": 13400 },
"currency": "NGN"
}
}
tripType is determined automatically by comparing the pickup and dropoff states: intrastate when they match, interstate otherwise. Arrival is always expressed in whole days (etaDays), never a fraction: intrastate deliveries are a flat 1-day promise, interstate deliveries scale with real driving distance and a realistic minimum for checkpoints, road conditions, and night-driving restrictions.
A quote is valid for 10 minutes. Show pricing.total and etaAt to your customer for confirmation before proceeding to step 2.
Step 2: Confirm and create the shipment
POST /shipments
Reference the quote from step 1 with quoteId, and supply the remaining shipment details:
| Field | Type | Required | Notes |
|---|---|---|---|
quoteId | string | recommended | Locks in the exact quoted price, distance, ETA, and serviceType. If omitted, the shipment is priced fresh at creation time using the same fields as /rates/estimate, including serviceType |
pickupContactName | string | yes | |
pickupContactPhone | string | yes | |
pickupContactEmail | string | yes | |
recipientName | string | yes | |
recipientPhone | string | yes | |
recipientEmail | string | yes | |
packageDescription | string | no | |
brittleTier | low | medium | high | no | Defaults to low. Affects how the package is handled in transit |
containsLiquid | boolean | no | Defaults to false |
scheduledAt | ISO 8601 date string | no | If supplied and in the future, the shipment is scheduled for that time. Otherwise it's dispatched instantly |
If you don't pass quoteId, the same serviceType/pickupAddress/pickupCity/pickupState/dropoffAddress/dropoffCity/dropoffState/packageWeightKg fields from step 1 are required directly on this call instead.
Idempotency
Pass an Idempotency-Key header (any unique string you generate, such as your own order ID) on POST /shipments. If the request is retried with the same key, the original shipment is returned instead of creating a duplicate, and your wallet is not double-charged. This protects against network retries and duplicate submissions.
Idempotency-Key: order_48213
Response
{
"success": true,
"sandbox": true,
"data": {
"_id": "6890...",
"trackingCode": "7F-20260801-4F91A2C8",
"reference": "SHIP-9F3A1B2C4D5E",
"status": "pending",
"tripType": "interstate",
"serviceType": "consolidated",
"deliveryType": "instant",
"environment": "sandbox",
"subtotal": 12500,
"vatPercent": 7.5,
"vatAmount": 900,
"price": 13400,
"currency": "NGN",
"packageWeightKg": 2.5,
"brittleTier": "low",
"containsLiquid": false,
"statusHistory": [{ "status": "pending", "at": "2026-08-01T14:22:00.000Z" }],
"createdAt": "2026-08-01T14:22:00.000Z"
}
}
Every shipment has both a trackingCode (share this with your recipient for public tracking) and a reference (your internal-facing business reference for the transaction).
Pricing, in detail
Pricing follows the same rate card model used across the Nigerian logistics industry, rather than a raw distance-and-fuel calculation:
- Consolidated (shared load / waybill) is priced from a weight class: your package's weight places it in a band, and that band has its own fare for intrastate and interstate trips.
- Dedicated (full vehicle charter) is priced from a haulage class: your package's weight determines which vehicle class is needed (bike, van, pickup, truck, and so on), and that class has its own fare covering a base distance, plus a per-kilometre rate beyond it.
Both add a fuel surcharge on top, which is adjusted independently of the base fares as fuel prices change, then VAT on top of that. Final amounts are rounded to the nearest ₦100. Rate cards vary by region and are configured by 7feastr, never hardcoded on your side, always read pricing.total from the API response rather than estimating it yourself.