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

  1. You register a webhook URL with YUMBI Gateway (once).
  2. When a customer places an order, Gateway sends an order notification to your webhook.
  3. You fetch the full order details from Gateway.
  4. You place the order with the store and accept or reject it through the Gateway API.
  5. 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:

HeaderValue
X-HMACHMAC-SHA256 of path + body + timestamp, using your API key as the secret, hex-encoded in lowercase
X-TimestampUnix timestamp (seconds) used in the signature
X-Client-IDYour 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"
    }
}
FieldDescription
data.order_idYUMBI order ID. Use this for every follow-up call about the order.
data.store_idYour own store identifier (the external_id you supplied for the store).
data.statuscreated for a new order.

Respond with a 2xx status to acknowledge the notification, then process the order. How Gateway
handles your response:

Your responseWhat happens
2xxGateway waits for you to accept or reject the order.
4xxThe order is rejected immediately. It is not retried.
5xx, or your endpoint is unreachableGateway 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.

FieldDescription
failure_codeWhy the order was rejected. See the values below.
reasonShort, human-readable reason.
infoAdditional detail.
store_explicitly_declined_ordertrue 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_itemsThe 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_responseYour POS's raw response, as a string or JSON. Used only for troubleshooting and never shown to customers.

failure_code values

CodeUse when
store_closedThe store is closed.
store_too_busyThe store can't take more orders right now.
store_declinedThe store declined the order.
item_unavailableOne or more items are out of stock.
menu_item_issueOne or more items couldn't be matched or priced in your POS.
order_invalidThe order is invalid for another reason.
payment_issueThere is a problem with the payment.
delivery_unavailableDelivery isn't possible for this order.
pos_offlineThe store's POS is offline.
pos_timeoutThe store's POS didn't respond.
pos_failureThe store's POS returned an error.
unknownNone of the above.

error_type values: invalid_price, item_not_found, item_unavailable.

Responses

StatusMeaning
200The decision was recorded. Repeating the same decision is safe and also returns 200.
400An unrecognised failure_code or error_type was sent.
404The order doesn't exist.
409The 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_id in a cancellation is the ordering channel's order reference. It
matches the channel_order_id field in the order details, not the YUMBI order ID. Store
channel_order_id when 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.



Did this page help you?