Parcels
Booking a parcel adds it to the batch a rider will collect from you. Nothing is charged at this point.
Book a parcel
POST
/v1/parcelsRequires parcels:write.
| Field | Required | Notes |
|---|---|---|
recipient_name | yes | Who collects it. |
recipient_phone | yes | Gets the arrival SMS and the collection PIN. |
recipient_email | no | Emailed the PIN as well. |
description | yes | What is inside. Agents see this. |
package_size | yes | As in quotes. |
weight_kg | no | Send it where you have it. |
delivery_mode | yes | STATION or DOOR. |
dest_town_id | either | Collection town. |
dest_lat, dest_lng | either | For door delivery. |
origin_lat, origin_lng | yes | Where a rider collects. |
origin_address | no | Helps the rider find you. |
declared_value | no | Drives insurance. |
reference | no | Your own order id. Comes back on every webhook. |
json
{
"object": "parcel",
"livemode": true,
"id": "9f3a1c72-4b8e-4d1a-9c2f-7e5b0a6d3812",
"parcel_number": "PCL-A1B2C3",
"tracking_number": "SHP-8DD1D9",
"status": "AWAITING_PICKUP",
"payment_timing": "PAY_AT_PICKUP",
"total_fee": 350,
"currency": "KES",
"pickup_run_id": "cbcb7d07-...",
"reference": "order-1042",
"message": "Booked. It will be settled with the rest of your batch when a rider collects."
}Always send
X-Idempotency-Key. Derive it from your order id — order-1042, not a random value. If the connection drops after we created the parcel, your retry returns the same parcel instead of booking and charging for a second one.Parcel statuses
| Status | Means |
|---|---|
AWAITING_PICKUP | Waiting for a rider to collect it from you. |
AWAITING_DROPOFF | Waiting for you to drop it at a SendWay point. |
IN_TRANSIT | Collected, moving through the network. |
AT_PICKUP_STATION | Arrived. Your customer has a PIN. |
DELIVERED | Collected by your customer. |
RETURNED | Not collected in time; coming back. |
CANCELLED | Cancelled before collection. |
List your parcels
GET
/v1/parcels?limit=25Newest first, cursor-paginated. Requires parcels:read.
json
{
"object": "list",
"data": [{ "object": "parcel", "id": "...", "tracking_number": "SHP-8DD1D9" }],
"has_more": true,
"next_cursor": "9f3a1c72-4b8e-4d1a-9c2f-7e5b0a6d3812"
}Pass next_cursor back as starting_after for the next page.
Read one
GET
/v1/parcels/:idTrack by number
GET
/v1/tracking/:tracking_numberAccepts either the tracking number or the parcel number. Returns status and destination without the recipient's contact details, so it is safe to render on a page your customer sees.
Cancel
POST
/v1/parcels/:id/cancelPossible while the parcel is still with you. Once a rider has scanned your batch the total is locked and you get 409 run_already_scanned — at that point the parcel is in the network and cancelling is a support conversation.
A cancelled parcel drops out of your batch, so it never becomes a charge.
Next: how you pay.
