
# Delivery & tracking

Every order Kibanda delivers has **one tracking picture**, whatever is carrying it. The same picture drives
the customer's tracking page, what the shop assistant says when a customer asks "where is it?", the
messages the customer gets, and [`GET /v1/orders/:id/tracking`](#retrieve-tracking).

## Ways an order can travel

| Method | Who reports progress | What the customer sees |
|---|---|---|
| `rider` | The shop's rider in WhatsApp (picked up → arrived → delivered), plus their location link | Live map, ETA, "almost there", delivery code |
| `courier` | The shop, or your system through the API (carrier + tracking number) | Carrier, tracking number, the carrier's own tracking page, ETA |
| `meetup` | The shop, or your system (a matatu, a bus parcel, a stage or agent where the customer collects) | Where to collect, when, a collection code, an "I've collected it" button |
| `partner` | A logistics partner through the API | Partner status, ETA, position if they share it |
| `pickup` | The shop (ready → collected) | "Ready to collect" and a pickup code |

## The tracking link

Each order has a private link, `https://api.kibanda.africa/t/<token>`. It is sent to the customer with the
payment confirmation and again when the order is on its way. The page shows the status, a live map when
there is a position, the ETA, the handover code once it's needed, and a timeline. It refreshes itself.

The link is unguessable and shows no phone numbers. Anyone holding it can see the order's items and status,
so share it only with the customer.

## Riders and live location

Riders work in WhatsApp. Their delivery card includes a **location link**: one tap on *Share my location*
and, while the page is open, the rider's phone reports its position every few seconds (WhatsApp's own
live-location sharing doesn't reach businesses). From that Kibanda:

- keeps the ETA current (road distance at boda speeds, from the rider's latest position);
- tells the customer when the rider is about 800 m away, once ("Your rider is almost there…");
- keeps a position trail for the delivery, useful when a customer says the rider never came.

Before any position arrives, the ETA is estimated from the shop's location to the customer's pin.

## Sending an order another way

From WhatsApp, the shop taps **Send another way** on an order and describes it in one line, e.g.
*"Super Metro KBX 123A to Rongai stage, around 4pm"* or *"Pickup Mtaani PM-88213, Kahawa agent"*. The
assistant reads it into carrier, reference, collection point and time, and the customer gets the details
and their tracking link. When it gets there the shop taps **It's arrived**; the customer gets a collection
code and confirms with **I've collected it**, which completes the order.

From your system, use the API below.

### Hand an order to a courier, partner or meet-up point

`POST /v1/orders/:id/fulfillment`

| Field | |
|---|---|
| `method` | **Required.** `courier`, `meetup` or `partner`. |
| `carrier` | Who carries it: `"Pickup Mtaani"`, `"G4S"`, `"Super Metro"`. |
| `tracking_ref` | Tracking number, waybill, or vehicle registration. |
| `tracking_url` | The carrier's own tracking page (https). |
| `meet_point` | Where the customer collects: a stage, agent, office. |
| `meet_location` | `{ "lat": -1.29, "lng": 36.82 }` of the collection point, for the map. |
| `eta` | When it's expected, ISO 8601 (`2026-10-24T16:00:00+03:00`). |
| `note` | Anything the customer should know (who to ask for). |
| `external_id` | Your or the partner's id for the shipment. |

```bash
curl https://api.kibanda.africa/v1/orders/ord_9f2c…/fulfillment \
  -H "Authorization: Bearer $KIBANDA_KEY" -H "Content-Type: application/json" \
  -d '{"method":"courier","carrier":"Pickup Mtaani","tracking_ref":"PM-88213","meet_point":"Kahawa Sukari agent","eta":"2026-10-24T14:00:00+03:00"}'
```

The order must be confirmed (paid, or pay-on-delivery placed) and not already with a rider. The customer is
told straight away. Returns the [tracking object](#the-tracking-object).

### Report progress

`POST /v1/orders/:id/fulfillment/updates`

| Field | |
|---|---|
| `status` | `in_transit`, `arrived` (at the collection point or door), `delivered`, or `failed`. |
| `eta` | A new expected time. The customer is told when it changes. |
| `location` | `{ "lat", "lng" }` of the parcel or vehicle, shown on the map. |
| `note`, `tracking_url` | Updated details. |

`arrived` sends the customer their collection code (meet-up) and the *I've collected it* button.
`delivered` completes the order exactly like a rider's delivery: stock is consumed, the customer is asked
whether everything arrived, and `order.delivered` is sent to your [webhooks](https://kibanda.africa/docs/webhooks).

A logistics partner without a native integration can be connected this way with an API key limited to
orders, or through a bridge such as Zapier.

## Retrieve tracking

`GET /v1/orders/:id/tracking`

### The tracking object

```json
{
  "object": "tracking",
  "order": "ord_564aecc5ad9c437a9ff957ac42c6efb4",
  "method": "rider",
  "stage": "on_the_way",
  "headline": "Order #2 is on the way 🛵",
  "eta": "2026-10-23T16:11:56.000Z",
  "url": "https://api.kibanda.africa/t/8f92a4ec097243608cd873",
  "rider": { "name": "Otieno", "location": { "lat": -0.285, "lng": 36.0275 }, "seen_at": "2026-10-23T15:59:02.000Z" },
  "consignment": null,
  "timeline": [
    { "at": "2026-10-23T15:12:06.926Z", "text": "Order placed" },
    { "at": "2026-10-23T15:12:40.965Z", "text": "Paid" },
    { "at": "2026-10-23T15:21:57.784Z", "text": "Handed to the rider" },
    { "at": "2026-10-23T15:22:18.216Z", "text": "On the way" }
  ]
}
```

`stage` is one of `awaiting_payment`, `preparing`, `on_the_way`, `arrived`, `ready` (pickup),
`delivered`, `failed`, `cancelled`. `consignment` holds `carrier`, `tracking_ref`, `tracking_url`,
`meet_point` and `note` for courier, meet-up and partner deliveries. A rider's `location` is included only
while it's fresh (the last five minutes).
