The Order Flow
POS Order Flow
This guide describes how orders move between YUMBI Gateway and your POS: what Gateway sends you,
what you are expected to send back, and what happens if a step fails or times out.
Overview
- You register a webhook URL with YUMBI Gateway (once).
- When a customer places an order, Gateway sends an order notification to your webhook.
- You fetch the full order details from Gateway.
- You place the order with the store and accept or reject it through the Gateway API.
- If the customer later cancels, Gateway sends a cancellation notification to your webhook.
Sequence diagram
sequenceDiagram
autonumber
participant CH as Ordering Channel
participant GW as YUMBI Gateway
participant POS as POS Integrator
participant ST as Store
Note over POS,GW: One-off setup
POS->>GW: POST /api/v1/webhooks<br/>{ url, event_type: "pos_provider.orders_notification" }
GW-->>POS: 200 { success, webhook }
Note over CH,GW: Customer places an order
CH->>GW: New order
Note over GW,POS: Order notification
GW->>POS: POST {your webhook URL}<br/>{ event_type, event_id, event_timestamp,<br/>data: { order_id, store_id, status: "created" } }
alt Notification received
POS-->>GW: 2xx
else Server error or endpoint unreachable
POS-->>GW: 5xx / no connection
GW->>POS: Retried every 10 seconds, up to 3 attempts in total
Note over GW: All attempts fail → order rejected
else Client error
POS-->>GW: 4xx
Note over GW: Order rejected, no retry
end
POS->>GW: GET /api/v1/orders/{order_id}
GW-->>POS: Full order details
POS->>ST: Place the order with the store
ST-->>POS: Store accepts or declines
alt Store accepts
POS->>GW: POST /api/v1/orders/{order_id}/accept
GW-->>POS: 200 { order_id, status: "accepted" }
else Store declines or the order can't be placed
POS->>GW: POST /api/v1/orders/{order_id}/reject<br/>{ failure_code, reason, info, ... }
GW-->>POS: 200 { order_id, status: "rejected" }
else No accept or reject within 60 seconds
Note over GW: Order rejected as a POS timeout
end
GW->>CH: Order outcome
opt Customer cancels the order
CH->>GW: Cancel order
GW->>POS: POST {your cancellation webhook URL}<br/>{ event_type: "pos_provider.channel_cancelled_order",<br/>data: { order_id } }
POS-->>GW: 2xx
POS->>ST: Void the order
end
Authentication
Every request you make to Gateway must be signed as described in Authentication.
Webhooks Gateway sends to you are signed the same way, so you can verify they came from YUMBI:
| Header | Value |
|---|---|
X-HMAC | HMAC-SHA256 of path + body + timestamp, using your API key as the secret, hex-encoded in lowercase |
X-Timestamp | Unix timestamp (seconds) used in the signature |
X-Client-ID | Your client ID |
path is the path of your webhook URL (for example /yumbi/orders). body is the raw request body.
Step 1: Register your webhook
POST /api/v1/webhooks{
"webhook": {
"url": "https://pos.example.com/yumbi/orders",
"event_type": "pos_provider.orders_notification"
}
}Your YUMBI contact will configure the cancellation webhook URL for you.
Step 2: Receive the order notification
When an order is placed at one of your stores, Gateway sends:
{
"event_id": "6f1c9a52-3b0e-4d9b-9a57-1d2f8e4c7b10",
"event_type": "pos_provider.orders_notification",
"event_timestamp": 1759406400,
"user_id": 42,
"data": {
"order_id": "b3e1f0d2-8c4a-4f5e-9d61-2a7b3c9e0f14",
"store_id": "STORE-001",
"status": "created"
}
}| Field | Description |
|---|---|
data.order_id | YUMBI order ID. Use this for every follow-up call about the order. |
data.store_id | Your own store identifier (the external_id you supplied for the store). |
data.status | created for a new order. |
Respond with a 2xx status to acknowledge the notification, then process the order. How Gateway
handles your response:
| Your response | What happens |
|---|---|
| 2xx | Gateway waits for you to accept or reject the order. |
| 4xx | The order is rejected immediately. It is not retried. |
| 5xx, or your endpoint is unreachable | Gateway retries every 10 seconds, up to 3 attempts in total. If every attempt fails, the order is rejected. |
Step 3: Fetch the order details
GET /api/v1/orders/{order_id}The response contains the full order: items and modifiers, customer, payments, discounts, delivery
details (for delivery orders), the ordering channel's own order reference (channel_order_id) and
the order's current_state. See Get Order Details for the full schema and
examples.
Step 4: Accept or reject the order
Send the store's decision within 60 seconds of the order being created. If Gateway hasn't
received an accept or reject by then, the order is rejected as a POS timeout and can no longer be
accepted.
The 60 seconds start when the order is created, not when you receive the notification. Time spent
delivering the notification, including any retries, counts towards it.
Accept
POST /api/v1/orders/{order_id}/accept{ "order_id": "b3e1f0d2-8c4a-4f5e-9d61-2a7b3c9e0f14", "status": "accepted" }Reject
POST /api/v1/orders/{order_id}/reject{
"failure_code": "item_unavailable",
"reason": "Item out of stock",
"info": "Fresh Avocado is out of stock",
"store_explicitly_declined_order": false,
"item_metadata": {
"invalid_items": [
{ "id": "1234", "error_type": "item_unavailable" }
]
},
"pos_raw_response": "<the response your POS returned, for troubleshooting>"
}All fields are optional, but the more detail you send, the better the ordering channel can explain
the outcome to the customer.
| Field | Description |
|---|---|
failure_code | Why the order was rejected. See the values below. |
reason | Short, human-readable reason. |
info | Additional detail. |
store_explicitly_declined_order | true if the store actively chose to decline the order (rather than the order failing). This stops the order from being redirected elsewhere. |
item_metadata.invalid_items | The items that caused the rejection, each with your POS's own item id (the item code in your POS menu) and an error_type. |
pos_raw_response | Your POS's raw response, as a string or JSON. Used only for troubleshooting and never shown to customers. |
failure_code values
| Code | Use when |
|---|---|
store_closed | The store is closed. |
store_too_busy | The store can't take more orders right now. |
store_declined | The store declined the order. |
item_unavailable | One or more items are out of stock. |
menu_item_issue | One or more items couldn't be matched or priced in your POS. |
order_invalid | The order is invalid for another reason. |
payment_issue | There is a problem with the payment. |
delivery_unavailable | Delivery isn't possible for this order. |
pos_offline | The store's POS is offline. |
pos_timeout | The store's POS didn't respond. |
pos_failure | The store's POS returned an error. |
unknown | None of the above. |
error_type values: invalid_price, item_not_found, item_unavailable.
Responses
| Status | Meaning |
|---|---|
| 200 | The decision was recorded. Repeating the same decision is safe and also returns 200. |
| 400 | An unrecognised failure_code or error_type was sent. |
| 404 | The order doesn't exist. |
| 409 | The order already has a different outcome: it was cancelled, or already accepted (when rejecting) or rejected (when accepting). |
Step 5: Handle cancellations
If the customer cancels an order, Gateway sends a cancellation notification to your cancellation
webhook:
{
"event_id": "0c8d7e21-5a4f-4b3c-8e9d-7f6a5b4c3d2e",
"event_type": "pos_provider.channel_cancelled_order",
"event_timestamp": 1759406700,
"user_id": 42,
"data": {
"order_id": "CH-789456"
}
}Note:
data.order_idin a cancellation is the ordering channel's order reference. It
matches thechannel_order_idfield in the order details, not the YUMBI order ID. Store
channel_order_idwhen you fetch an order so you can match cancellations to it.
Respond with a 2xx status, then void the order in the store's POS.
Updated 18 days ago
