Receiving orders
How an online order reaches your POS, what it contains, and how to tell us you recorded it.
When an order reaches you
An order reaches your POS only after the customer has accepted the shop's quote (status confirmed). Never earlier, while the shop is still deciding what it can supply.
The three events
| Event | Means | Your POS should |
|---|---|---|
order.confirmed | A customer committed to an order. | Record the sale, send new stock, then acknowledge. |
order.cancelled | It was cancelled afterwards. was is pending or accepted; pos_ref is your sale number. | If you had recorded it (was: accepted), void the sale. |
order.completed | Delivered or collected. | Nothing; it is for your records. |
Events can arrive more than once. De-duplicate on event_id.
The order
{
"event_id": 1042, "type": "order.confirmed", "created_at": "2026-11-03T10:15:22+03:00",
"order": {
"id": 5521, "order_number": "SHP-KQ2P9X", "status": "confirmed", "type": "delivery", "currency": "TZS",
"customer": { "name": "Asha", "phone": "+255712345678" },
"delivery": { "address": "Sinza Mori", "instructions": "Blue gate", "latitude": -6.78, "longitude": 39.22 },
"lines": [
{ "line_id": 9001, "external_id": "SKU-1", "variant_external_id": null, "name": "Sukari Kilombero 1kg",
"quantity": 2, "unit_price": 3500, "addons": [], "line_total": 7000 },
{ "line_id": 9002, "external_id": "PZ-1", "variant_external_id": "PZ-1-L", "name": "Margherita (Large)",
"quantity": 1, "unit_price": 18000, "addons": [{ "name": "Extra cheese", "price": 1500 }], "line_total": 19500 }
],
"subtotal": 26500, "delivery_fee": 2000, "total": 28500,
"payment": { "method": "cash", "status": "unpaid" },
"pos": { "state": "pending", "pos_ref": null }
}
}
- Lines are matched by your
external_idandvariant_external_id. Lines the shop couldn't supply are left out. line_totalincludes the add-ons.typeisdelivery,pickuporparcel.
Getting events
Polling (works everywhere)
GET /pos/v1/events?after=<last event_id> returns events in order, with next_after for the next call. Persist the last id you processed. While a shop has paused the link you get 403; when it resumes, the feed catches up.
GET /pos/v1/orders?state=pending lists the orders still waiting for you, which is useful after downtime.
Webhooks (cloud POS)
The owner sets your HTTPS address in the Business app and we POST each event to it. The feed always has the same events, so a missed webhook is never lost. See webhooks.
Acknowledging
Tell us whether you recorded the sale with POST /pos/v1/orders/{id}/ack.
// Recorded
{ "status": "accepted", "pos_ref": "SALE-8841" }
// Couldn't record it
{ "status": "rejected", "reason": "out_of_stock", "message": "Last bag sold in store", "unavailable": ["SKU-1"] }
Send updated stock before you acknowledge. From confirmed until you answer, we hold the order's units back so two customers can't buy the last item while your POS catches up. Acknowledging releases that hold, so your new, lower stock must already have arrived.
- Repeating the same answer is safe (
replay: true). A different answer afterwards is409. - A rejection never cancels the order, because the customer may already have paid. The shop is told and decides whether to fulfil or cancel. Products named in
unavailableare taken off sale.