--- sidebar_label: 'Order placement' sidebar_position: 2 --- # Order placement An order placement integration bridges Future Ordering and an external Point of Sale (POS) system. When a guest completes checkout, the integration receives an event, retrieves the order data, sends it to the POS, and reports the outcome back to Future Ordering. This article covers the full placement flow: subscribing to the trigger event, starting a placement session, reporting success or failure, and using mapping details to correlate Future Ordering entities with records in your POS. For a conceptual overview of how orders progress through their lifecycle, see [Order lifecycle](../orders/order-lifecycle.md). ## Prerequisites - An active event subscription configured to receive [order.ready_to_be_placed_in_pos](../../events/order/ready-to-be-placed) events. See [Event subscriptions](../event-subscriptions/overview.md). - API credentials with permission to read orders and call the related order state-transition endpoints. ## Handle an order ready to be placed in POS When a guest finalizes their basket and completes checkout, Future Ordering closes the order and emits an `order.ready_to_be_placed_in_pos` event. Your integration reacts to this event, retrieves the full order, sends it to the POS, and reports back whether placement succeeded or failed. ```mermaid sequenceDiagram participant fo as Future Ordering participant int as Order Placement Integration participant pos as POS note over fo : An order is ready to be placed in POS fo -->> int : `order.ready_to_be_placed_in_pos` event int ->>+ fo : POST /orders/{order-id}/place-order-session/start fo ->>- int : order data note over int : Process the order as necessary int ->> pos : Place order alt The order was successfully placed in POS pos ->> int : Order placement succeeded int ->> fo : POST /orders/{order-id}/pos/placed else The order was not able to be placed in POS pos ->> int : Order placement failed int ->> fo : POST /orders/{order-id}/pos/failed end ``` ### Start a place-order session Before sending the order to the POS, start a place-order session by calling: ```http POST /orders/{order-id}/place-order-session/start ``` Request body: | Property | Type | Required | Description | |---|---|---|---| | `placeOrderSessionId` | string (UUID) | Yes | A UUID generated by your integration to identify this session. | The API returns the full [order object](../orders/order-properties), including all items, discounts, payments, [mapping details](#mapping-details) and the time the guest requested the order to be ready. Use this data to construct the request to your POS. ### Report a successful placement When the POS confirms the order was placed, call: ```http POST /orders/{order-id}/pos/placed ``` Request body: | Property | Type | Required | Description | |---|---|---|---| | `orderName` | string | Yes | A guest-facing display name for the order, such as a check number. Does not need to be unique. Example: `"1234"` | | `datePlaced` | string (ISO 8601) | Yes | The datetime when the order was placed in the POS. | | `estimatedReadyTime` | string (ISO 8601) | Yes | The estimated datetime when the food will be ready. | | `cancelableUntil` | string (ISO 8601) | No | The datetime after which the guest can no longer cancel the order, if applicable. | | `orderRefsDict` | object | No | A map of opaque unique references to tie to the order, such as the order's ID in the POS system. Keys and values are both strings. Example: `{ "my-pos": "abc123" }` | | `receipt` | string | No | A full-text fiscal receipt string. Made available through the receipt service and included in the order confirmation email. | | `extensions` | object | No | Optional additional order-related data as a free-form key-value map. | :::note `orderRefsDict` values must be unique across all orders for your tenant. If you attempt to set a reference that is already associated with another order, the API returns `409 Conflict`. ::: ### Report a failed placement When the POS cannot place the order, call: ```http POST /orders/{order-id}/pos/failed ``` Request body: | Property | Type | Required | Description | |---|---|---|---| | `code` | string | Yes | An opaque error code identifying the failure reason. Example: `"order.pos.item-out-of-stock"` | | `message` | string | Yes | A human-readable description of the error. | | `data` | object | No | Optional additional context for the error as a free-form object. | Whether placement succeeds or fails, the order transitions to **finalized** after Future Ordering processes the callback. See [Order lifecycle](../orders/order-lifecycle.md) for more information. :::note While codes are opaque strings, consider using a structured format such as `{domain}.{subdomain}.{reason}` to make codes easier to manage. Example: `"order.pos.item-out-of-stock"`. ::: ## Placement session timeout Each placement session has a timeout of **3 minutes**. If your integration does not call `/pos/placed` or `/pos/failed` before the timeout elapses, Future Ordering marks the order as failed and transitions it to **finalized**. A timeout-triggered failure is distinct from calling `/pos/failed`. If your integration receives an `order.ready_to_be_placed_in_pos` event but the order is already finalized, calling the endpoint `/pos/placed` or `/pos/failed` returns `405 Method Not Allowed` — the order is no longer in a valid state for placement. ## Mapping details When your integration imports master data entities of stores and menu items, it can include a `mappingDetails` object with custom key-value pairs. This object is stored as-is and later included in the order data when an order is ready to be placed. This allows your integration to correlate Future Ordering entities with records in your POS when processing an order placement. :::tip `mappingDetails` is your contract between the master data integration and the order placement integration. ::: Discounts also contain `mappingDetails` that can be used to determine which POS discounts to apply, see [Loyalty integrations](../loyalty-integration/overview.md) for more information on how to setup discounts with `mappingDetails`. For charge items like delivery fees or service charges, `mappingDetails` are generated from configuration. When you retrieve the full order object, `mappingDetails` is present on each entity that carries it: - **Items** (`order.items[].mappingDetails`) for menu items and charge items - **Discounts** (`order.discounts[].mappingDetails`) for applied discounts - **Store** (`order.storeDetails.mappingDetails`) for the store the order belongs to For example, you might store a POS product ID in `mappingDetails` on each menu item so that your integration can look up the correct POS product when constructing the placement request: ```json { "id": "e646d0c0-...", "type": "menuItem", "title": "150g Burger", "quantity": 2, "mappingDetails": { "posProductId": "BURGER-150G" } } ``` The fields present in `mappingDetails` are defined when the entity is imported. See [Importing menus](../importing-master-data/importing-menus/overview.md) and [Importing stores](../importing-master-data/importing-stores.md) for how to set these values. :::tip If you need per-item discount amounts when constructing the POS request, use the `discounts[].amounts` array. Each entry includes the discounted item ID, the specific unit ID, and the discount value. See [Basket and items](../orders/basket.md) for a full explanation of the discount structure. :::