# Create Orders

### Preview Order

Validate cart and calculate totals without persisting data.

### `POST /orders/preview` - Preview Order

Validate cart and calculate totals without persisting data.

#### Request Body

```json
{
  "restaurantId": 2285,
  "channelId": 2,
  "consumptionMode": "MODE_DELIVERY",
  "menuId": 24194,
  "cart": [
    {
      "productId": 1670891,
      "quantity": 1,
      "steps": []
    }
  ],
  "addressId": 12345,
  "expectedAt": "2025-12-03T19:00:00.000Z"
}
```

##### Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| restaurantId | integer | 2285 | Identifier of the restaurant. |
| channelId | integer | 2 | Identifier of the associated channel. |
| consumptionMode | string | "MODE\_DELIVERY" | The consumption mode value. |
| menuId | integer | 24194 | Identifier of the menu. |
| cart | array | \[…\] | List of cart entries. |
| cart\[\] | object | {…} | Object containing cart fields. |
| cart\[\].productId | integer | 1670891 | Identifier of the product. |
| cart\[\].quantity | integer | 1 | The quantity value. |
| cart\[\].steps | array | \[\] | List of steps entries. |
| addressId | integer | 12345 | Identifier of the associated address. |
| expectedAt | string | "2025-12-03T19:00:00.000Z" | Date or timestamp for expected. |

#### Response

```json
{
  "status": 200,
  "code": "order_parameters_validated",
  "message": "...",
  "data": {
    "totalPriceWithTaxIncluded": 1500,
    "valid": true,
    "deliveryArea": {
      "id": 1,
      "name": "Zone A"
    }
  }
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 200 | HTTP status code returned by the API. |
| code | string | "order\_parameters\_validated" | Machine-readable application code for the result. |
| message | string | "..." | Human-readable result message. Do not use this value for program logic. |
| data | object | {…} | Endpoint-specific response payload. |
| data.totalPriceWithTaxIncluded | integer | 1500 | The total price with tax included value. |
| data.valid | boolean | true | The valid value. |
| data.deliveryArea | object | {…} | Object containing delivery area fields. |
| data.deliveryArea.id | integer | 1 | The id value. |
| data.deliveryArea.name | string | "Zone A" | The name value. |

### Payload Schema

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| restaurantId | number | Yes | Target restaurant. |
| channelId | number | Yes | 1=KIOSK, 2=WEB. |
| consumptionMode | string | Yes | MODE\_DELIVERY, MODE\_TAKE\_AWAY, MODE\_SIT\_IN, MODE\_DRIVE. |
| menuId | number | Yes | Catalog context. |
| cartId | string | No | Optional. |
| cart | Product\[\] | Yes | Items. |
| customerId | number | No | Optional. |
| willPayLater | boolean | No | Payment is settled on site. `payments` must then be omitted or empty. |
| payments | Payment\[\] | No | Omit it (or send `[]`) when `willPayLater` is `true`. |
| expectedAt | ISO datetime | No | Optional. |
| expectedAtEnd | ISO datetime | No | Optional, must be &gt;= expectedAt. |
| royaltyDiscountValue | number | No | Optional. |
| promocode | string | No | Optional. |
| comment | string | No | Optional. |
| userName | string | Conditional | Min 1 if KIOSK, else min 2. |
| addressId | number | Conditional | Required for delivery. |
| applyDate | ISO datetime | No | Optional. |
| tableName | string | No | Optional. |
| formulaDetection | boolean | No | Optional. |
| loyalty | object\|null | No | Optional. |

### Detailed Schemas

#### Product Object

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| productId | string\|number | Yes | ID. |
| productCartId | string | No | Optional. |
| quantity | number | Yes | \&gt; 0. |
| imageUrl | string | No | Optional. |
| customPrice | number | No | Optional. |
| customLabel | string | No | Optional. |
| steps | Step\[\] | Yes | Options. |
| crossSelling | object | No | `{ stepId: number, productId: number }` |

#### Step Object

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| stepId | number | Yes | ID. |
| products | Product\[\] | Yes | Selected options. |
| type | number | No | Optional. |

#### Payment Object

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| paymentMethodId | number | Yes | ID. |
| amount | number | No | \&gt;= 0. |
| quantity | number | Yes | \&gt; 0. |
| currency | string | No | Optional. |
| metadata | string | No | JSON-encoded string. Required for Stripe payments: `{"stripePaymentMethodId":"pm_..."}` or `{"cardId":42}` - see the [Stripe Payments guide](https://developers.innovorder.io/docs/guides/guide-stripe-payments.md). |
| isExternal | boolean | No | Optional. |
| paymentMethodCode | string | No | Optional. |

### Business Rules

#### MODE\_DELIVERY

-   **addressId** is required.
-   **area** is NOT expected in the body. It is calculated server-side from the address.
-   If no active zone covers the address: `no_area_for_address` (403).
-   If zone minimum order amount is not reached: `price_too_low_for_delivery` (403).

#### MODE\_TAKE\_AWAY / MODE\_SIT\_IN / MODE\_DRIVE

**addressId** must be absent.

#### Payments

-   Mixing payments with and without defined amounts is forbidden.
-   If the sum of payments does not match the total: `wrong_payments_amount`.

#### willPayLater

-   Nothing is paid at preview time: `payments` must be either omitted or an empty array. Both spellings are accepted and mean the same thing.
-   Sending `willPayLater: true` together with a non-empty `payments` array is refused with `invalid_order_payload` (400); the offending `willPayLater` / `payments` values are echoed in `extraData`.

#### Deligo

If `expectedAt` is &gt; 3 days in the past and outside the current month: `expected_at_too_old` (400).

### Error Codes

 missing\_parameters (400)  invalid\_parameters (400)  no\_area\_for\_address (403)  price\_too\_low\_for\_delivery (403)  incompatible\_address\_and\_customer (400)  wrong\_payments\_amount (403)  payment\_presence\_inconsistency (403)  expected\_at\_too\_old (400)  consumption\_mode\_not\_activated (403)  permission\_denied (403)  unauthorized (401) 

### Examples

#### Error: No Delivery Area

```json
{
  "status": 403,
  "code": "no_area_for_address",
  "message": "Aucune zone de livraison ne couvre cette adresse."
}
```

### Create Order

Once the cart has been validated via the `/orders/preview` endpoint, use this endpoint to finalize and persist the transaction.

Workflow: The body for `Create Order` is largely identical to `Preview Order`, but introduces mandatory fields for payment and timestamps.

## Create Endpoint

 Idempotency Required: You must provide a unique `idempotency-key` in the header to prevent duplicate orders in case of network retries.

### `POST /orders` - Create Order

Creates an order. Supports recursive product steps, multiple payment methods, and loyalty rewards.

#### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| idempotency-key | string (header) | Yes | Unique UUID to prevent duplicate creations. |
| io-locale | string (header) | No | Language code (e.g. fr-FR). |

#### Request Body

```json
{
  "restaurantId": 2285,
  "channelId": 2,
  "consumptionMode": "MODE_TAKE_AWAY",
  "menuId": 24194,
  "cart": [
    {
      "productId": 1670891,
      "quantity": 1,
      "steps": [
        {
          "stepId": 501,
          "products": [
            {
              "productId": 9901,
              "quantity": 1,
              "steps": []
            }
          ]
        }
      ]
    }
  ],
  "customerId": 1008881,
  "expectedAt": "2025-12-03T15:53:44.311Z",
  "payments": [
    {
      "paymentMethodId": 22521,
      "amount": 273,
      "quantity": 1,
      "paymentMethodCode": "EWallet"
    }
  ],
  "comment": "No cutlery please"
}
```

##### Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| restaurantId | integer | 2285 | Identifier of the restaurant. |
| channelId | integer | 2 | Identifier of the associated channel. |
| consumptionMode | string | "MODE\_TAKE\_AWAY" | The consumption mode value. |
| menuId | integer | 24194 | Identifier of the menu. |
| cart | array | \[…\] | List of cart entries. |
| cart\[\] | object | {…} | Object containing cart fields. |
| cart\[\].productId | integer | 1670891 | Identifier of the product. |
| cart\[\].quantity | integer | 1 | The quantity value. |
| cart\[\].steps | array | \[…\] | List of steps entries. |
| cart\[\].steps\[\] | object | {…} | Object containing steps fields. |
| cart\[\].steps\[\].stepId | integer | 501 | Identifier of the associated step. |
| cart\[\].steps\[\].products | array | \[…\] | List of products entries. |
| cart\[\].steps\[\].products\[\] | object | {…} | Object containing products fields. |
| cart\[\].steps\[\].products\[\].productId | integer | 9901 | Identifier of the product. |
| cart\[\].steps\[\].products\[\].quantity | integer | 1 | The quantity value. |
| cart\[\].steps\[\].products\[\].steps | array | \[\] | List of steps entries. |
| customerId | integer | 1008881 | Identifier of the customer. |
| expectedAt | string | "2025-12-03T15:53:44.311Z" | Date or timestamp for expected. |
| payments | array | \[…\] | List of payments entries. |
| payments\[\] | object | {…} | Object containing payments fields. |
| payments\[\].paymentMethodId | integer | 22521 | Identifier of the associated payment method. |
| payments\[\].amount | integer | 273 | The amount value. |
| payments\[\].quantity | integer | 1 | The quantity value. |
| payments\[\].paymentMethodCode | string | "EWallet" | The payment method code value. |
| comment | string | "No cutlery please" | The comment value. |

#### Response

```json
{
  "status": 200,
  "code": "order_created",
  "message": "Your order was successfully created.",
  "data": {
    "orderId": 1927055,
    "brandId": 2285,
    "restaurantId": 2285,
    "channelId": 2,
    "menuId": 24194,
    "ticketNumber": "1747_2285_2025-12-03_2",
    "mainStatus": "VALIDATED",
    "paymentStatus": "PAID",
    "consommationMode": 2,
    "ttcTotalPrice": 273,
    "vatTotalPrice": 25,
    "currency": "EUR",
    "expectedDate": "2025-12-03T15:53:44.000Z",
    "created_at": "2025-12-03T15:50:00.000Z",
    "updated_at": "2025-12-03T15:50:00.000Z",
    "userName": "Alice",
    "metadata": null,
    "orderUuid": "c4d2261e-2779-4eb6-beb0-cb41235c751e"
  }
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 200 | HTTP status code returned by the API. |
| code | string | "order\_created" | Machine-readable application code for the result. |
| message | string | "Your order was successfully created." | Human-readable result message. Do not use this value for program logic. |
| data | object | {…} | Endpoint-specific response payload. |
| data.orderId | integer | 1927055 | Identifier of the order. |
| data.brandId | integer | 2285 | Identifier of the brand. |
| data.restaurantId | integer | 2285 | Identifier of the restaurant. |
| data.channelId | integer | 2 | Identifier of the associated channel. |
| data.menuId | integer | 24194 | Identifier of the menu. |
| data.ticketNumber | string | "1747\_2285\_2025-12-03\_2" | The ticket number value. |
| data.mainStatus | string | "VALIDATED" | The main status value. |
| data.paymentStatus | string | "PAID" | The payment status value. |
| data.consommationMode | integer | 2 | The consommation mode value. |
| data.ttcTotalPrice | integer | 273 | The ttc total price value. |
| data.vatTotalPrice | integer | 25 | The vat total price value. |
| data.currency | string | "EUR" | ISO 4217 currency code. |
| data.expectedDate | string | "2025-12-03T15:53:44.000Z" | Date or timestamp for expected. |
| data.created\_at | string | "2025-12-03T15:50:00.000Z" | Timestamp when this resource was created. |
| data.updated\_at | string | "2025-12-03T15:50:00.000Z" | Timestamp when this resource was last updated. |
| data.userName | string | "Alice" | The user name value. |
| data.metadata | null | null | Additional metadata supplied with the response. |
| data.orderUuid | string | "c4d2261e-2779-4eb6-beb0-cb41235c751e" | The order uuid value. |

### Sit-in orders on a table

An order is added to the shared bill of a physical table when the three conditions below hold together. This is the only combination that triggers the table injection; a Sit-in order without `tableId`, or with a payment attached, is a regular order.

-   `consumptionMode` is `MODE_SIT_IN`;
-   `tableId` is the UUID of the table;
-   `willPayLater` is `true` (the bill is settled at the point of sale later, so `payments` is sent as an empty array, since it is a required field on this endpoint).

The table must belong to the restaurant. `tableId` is checked against the table layout attached to the restaurant's master POS device, the layout that is published to the point of sale. A table that does not exist, or that belongs to another layout or another restaurant, is refused with `table_not_found` (404) and **no order is created**. Retrying the same payload cannot succeed: correct the `tableId` first.

A restaurant with no master POS device does not operate table service, so no `tableId` is orderable there.

```json
{
  "status": 404,
  "code": "table_not_found",
  "message": "No table was found for this identifier in this restaurant.",
  "extraData": {
    "restaurantId": 2285,
    "tableId": "11112222-3333-4444-5555-666677778888"
  }
}
```

### Error responses

A failure raised while the order is being persisted is returned with its own `status` and `code`; `table_not_found` (404) above is one such case. A `4xx` means the order was rolled back and does not exist. The one exception is `restaurant_offline` (503) on a table order, described below.

-   The generic `create_order_max_retries_reached` (409, "please try again") is no longer returned. Failures that used to be reported under that code now surface with their real code, so a deterministic refusal is no longer indistinguishable from a transient fault. Do not branch on `create_order_max_retries_reached`; branch on the returned `code`.
-   Retry only on `5xx` responses and network timeouts, reusing the same `idempotency-key`. A `4xx` is a refusal of the payload and will be refused again identically.
-   A failure to notify the point of sale never costs a regular order: it is committed and reconciled with the POS afterwards, and the call still answers `200` with the created order.
-   A **table order** is different, because it is confirmed by the restaurant point of sale inside the request. When that point of sale does not answer, the call returns `restaurant_offline` (503): nothing was added to the table bill and nothing will be, so the order is not served. An order row is written and left in a non servable state for audit; it is never fulfilled and never charged. Present this to the guest as "the restaurant is unreachable, try again", not as a failed order they might already have been served.
-   After a `restaurant_offline` on a table order, retry with a **new** `idempotency-key`. Reusing the previous one returns the earlier, non servable order as a success instead of placing a new one.

### Request Payload

| Field | Validation | Description |
| --- | --- | --- |
| restaurantId | Required | Integer &gt; 0. Target restaurant. |
| channelId | Required | `1` (KIOSK), `2` (WEB). |
| consumptionMode | Required | Enum (e.g. MODE\_TAKE\_AWAY). |
| menuId | Required | Integer &gt; 0. Catalog context. |
| cart | Required | Array of Products (see Preview). |
| payments | Required | Array of `PaymentSchema` (see below). |
| expectedAt | Required | ISO 8601 Date. Pickup time. |
| customerId | Optional | Integer &gt; 0. Linked customer account. |
| cartId | Optional | String. Reference to a saved cart. |
| willPayLater | Optional | Boolean. If true, payment is on site. |
| expectedAtEnd | Optional | ISO Date. Must be &gt;= `expectedAt`. |
| royaltyDiscountValue | Optional | Number. Amount covered by loyalty points. |
| promocode | Optional | String. Coupon code. |
| comment | Optional | String (allows empty). Kitchen notes. |
| userName | Conditional | If KIOSK: Opt, min 1 char. Else: Opt, min 2 chars. |
| addressId | Optional | Integer &gt; 0. Delivery address ID. |
| applyDate | Optional | ISO Date. Logic application date. |
| tableName | Optional | String. For Sit-in mode. Free text, not unique per room. |
| tableId | Optional | String (UUID). Identifier of the physical table, for Sit-in mode. Required to have the order injected into the shared bill of that table (see **Sit-in orders on a table** above). |
| metadata | Optional | JSON-encoded string. Arbitrary integration data, persisted as-is on the order and returned by the order read endpoints. It must parse as valid JSON, otherwise the order is rejected with `400` / `order_metadata_invalid`; send a JSON **object**, not an array or a scalar. `kioskPaymentState` is a **reserved key owned by the platform**: on KIOSK orders (`channelId: 1`) any value you send under that key is overwritten or removed - see the **Kiosk payment state** section below. All your other keys are preserved untouched. |
| formulaDetection | Optional | Boolean. |
| loyalty | Optional | Object. See Loyalty Schema below. |

### Detailed Schemas

#### Loyalty Object

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| memberId | String | Yes | External ID of the loyalty member. |
| selectedRewards | Array&lt;Object&gt; | No | `{ id: string, selectedProductId?: number }` |
| usedPromocodes | Array&lt;Object&gt; | No | `{ id: string, code: string }` |
| usedPointsValue | Integer | No | Amount of points to burn. |
| shouldApplyDeals | Boolean | No | Auto-apply best deals. |

#### Payment Object

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| paymentMethodId | Integer | Yes | ID of the payment method (from configuration). |
| amount | Integer | Yes | Amount in cents. |
| quantity | Integer | Yes | Usually 1. |
| paymentMethodCode | String | No | See Enum below. |
| isExternal | Boolean | No | Flag for external payment processors. |

### PaymentMethodCode Enum

Cash

CreditCard

EWallet

LuncheonVoucher

ELuncheonVoucher

Transfer

Check

UberEats

Deliveroo

JustEat

Stripe

Paygreen

Yavin

Ingenico

Lydia

Edenred

RestoFlash

AmericanExpress

Izly

ExternalPayment

#### Paying with Stripe

-   The payment `metadata` must be a JSON-encoded **string** containing `{"stripePaymentMethodId":"pm_..."}` (one-shot card) or `{"cardId":42}` (saved card, authenticated customer only); otherwise the API returns `stripe_payment_metadata_invalid` (400).
-   If 3-D Secure is required, the response is a `200` with code `required_action` and a `requiredAction` object carrying the `payment_intent_client_secret`; complete the challenge client-side, then call `POST /orders/confirm` below.
-   See the [Stripe Payments guide](https://developers.innovorder.io/docs/guides/guide-stripe-payments.md) for the full flow (platform publishable key, tokenization, saved cards, SCA).

### Kiosk payment state (reserved metadata)

A kiosk order placed with `willPayLater: true` is a "pay at the counter" order: the guest orders on the kiosk but settles at the counter. For those orders the API writes the reserved key `kioskPaymentState` inside the order `metadata`, so a consumer can tell that the guest has **not** paid at the kiosk even when the sale is already represented as paid for reporting.

| Order | Resulting `metadata.kioskPaymentState` |
| --- | --- |
| `channelId: 1` (KIOSK), `willPayLater: true` and an amount left to pay &gt; 0 | Set by the API to `"NOT_PAID_AT_KIOSK"`, replacing any value sent by the client. |
| Any other KIOSK order (paid at the kiosk, free order, negative/deposit-refund order) | Absent. A value sent by the client under that key is removed. |
| Non-KIOSK order (`channelId: 2` WEB, vending machine) | The `metadata` string is stored exactly as sent, this key included. |

Which creation paths enforce the key: `POST /orders` and `POST /orders/bulk` share the same order-creation code, so a KIOSK-channel order sent in a bulk batch also has a client-provided `kioskPaymentState` removed from its `metadata` object. Bulk payloads carry no `willPayLater` field, so the key is never set on that path. Multi-restaurant orders (`POST /multi-orders`) use a different creation path and their `metadata` is left untouched.

Only that one key is platform-owned: every other property of your `metadata` object is preserved. That merge needs an object: when the `metadata` of an eligible kiosk order is valid JSON of another shape (an array, a number, a bare string), the API replaces the whole value with `{"kioskPaymentState":"NOT_PAID_AT_KIOSK"}` instead of merging into it. Response variant for a kiosk pay-at-counter order - same `200` / `order_created` envelope as the main example above, with the reserved key added to `metadata`:

```json
{
  "status": 200,
  "code": "order_created",
  "message": "Your order was successfully created.",
  "data": {
    "orderId": 1927056,
    "restaurantId": 2285,
    "channelId": 1,
    "ticketNumber": "1748_2285_2025-12-03_1",
    "paymentStatus": null,
    "ttcTotalPrice": 1350,
    "metadata": "{\"kioskPaymentState\":\"NOT_PAID_AT_KIOSK\"}",
    "orderUuid": "9f3b0f0e-6f0a-4c0b-9a4c-9e4a1d3f8b21"
  }
}
```

Do not read the payment status alone. At creation, a pay-at-counter order has a `paymentStatus` of `null` when settlement is deferred to a POS receiving kiosk orders, as in the example above. Without such a POS, the counter sale is recorded immediately through a synthetic `ExternalPayment`: the `POST /orders` response contains the numeric value `1` (internally `PAYMENT_OK`), while order read endpoints and webhooks report `status: "PAID"`. A deferred order can also become paid when the POS later settles it. In every case, `metadata.kioskPaymentState` keeps the original payment intent and still reads `"NOT_PAID_AT_KIOSK"`. Use the reserved key, not the accounting status, to know whether the guest actually paid at the kiosk.

Two properties of the key matter when you store or replay orders:

-   **It is written once, at order creation, and never updated.** It records where the guest was expected to pay, not whether the money has been collected. An order settled at the counter afterwards still reads `"NOT_PAID_AT_KIOSK"`.
-   **`"NOT_PAID_AT_KIOSK"` is the only value the API ever writes.** Treat the key as absent-or-`"NOT_PAID_AT_KIOSK"`, and do not read its absence as a payment confirmation: it is also absent on free orders, on deposit-refund orders and on every non-kiosk channel.

Where to read it afterwards - the key lives inside `metadata`, so only the endpoints that load that field expose it:

| Endpoint | `metadata` |
| --- | --- |
| POST /orders | Returned, already carrying the reserved key. |
| GET /v1/orders | Returned on each listed order. |
| GET /v1/orders/{orderId} | Returned. |
| GET /v1/orders/uuid/{orderUuid} | Returned. Anonymous lookup is supported for WEB orders only; authenticated callers can use it for kiosk orders. |
| GET /v1/orders/search | **Always `null`.** The lightweight search payload does not load the field, so an order carrying the reserved key still reads `null` here. Do not conclude from that search result that the guest paid at the kiosk. |
| orders.paid / orders.cancelled webhooks | Returned in the OmnichannelOrder payload. |

### Confirm Payment

After creating an order that requires a payment action (e.g. Stripe 3D-Secure), use these endpoints to confirm the payment and finalize the order.

### `POST /orders/confirm` - Confirm Stripe Payment

Confirms a Stripe payment intent for a previously created order. This finalizes the transaction after 3D-Secure or other Stripe authentication flows. paymentIntentId is the intentId returned in the requiredAction object of the create response. If the intent still requires an action, the API returns a 400 stripe\_still\_requires\_action with the client\_secret in extraData so the challenge can be retried.

#### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| idempotency-key | string (header) | Yes | Same idempotency key used during order creation. |

#### Request Body

```json
{
  "orderId": 1927055,
  "paymentIntentId": "pi_3Abc123def456ghi",
  "cartId": "cart_abc123"
}
```

##### Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| orderId | integer | 1927055 | Identifier of the order. |
| paymentIntentId | string | "pi\_3Abc123def456ghi" | Identifier of the associated payment intent. |
| cartId | string | "cart\_abc123" | Identifier of the associated cart. |

#### Response

```json
{
  "status": 200,
  "code": "order_created",
  "message": "Your order was successfully created.",
  "data": {
    "orderId": 1927055,
    "mainStatus": "VALIDATED",
    "paymentStatus": "PAID"
  }
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 200 | HTTP status code returned by the API. |
| code | string | "order\_created" | Machine-readable application code for the result. |
| message | string | "Your order was successfully created." | Human-readable result message. Do not use this value for program logic. |
| data | object | {…} | Endpoint-specific response payload. |
| data.orderId | integer | 1927055 | Identifier of the order. |
| data.mainStatus | string | "VALIDATED" | The main status value. |
| data.paymentStatus | string | "PAID" | The payment status value. |

### `POST /orders/confirm/adyen` - Confirm Adyen Payment

Confirms an Adyen payment for a previously created order. Used for Adyen 3D-Secure authentication flows.

#### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| idempotency-key | string (header) | Yes | Same idempotency key used during order creation. |

#### Request Body

```json
{
  "orderId": 1927055,
  "ioPayTransactionId": 98765,
  "adyenAuthenticationResult": "eyJhbGciOiJIUzI1NiJ9..."
}
```

##### Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| orderId | integer | 1927055 | Identifier of the order. |
| ioPayTransactionId | integer | 98765 | Identifier of the associated io pay transaction. |
| adyenAuthenticationResult | string | "eyJhbGciOiJIUzI1NiJ9..." | The adyen authentication result value. |

#### Response

```json
{
  "status": 200,
  "code": "order_created",
  "message": "Your order was successfully created.",
  "data": {
    "orderId": 1927055,
    "mainStatus": "VALIDATED",
    "paymentStatus": "PAID"
  }
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 200 | HTTP status code returned by the API. |
| code | string | "order\_created" | Machine-readable application code for the result. |
| message | string | "Your order was successfully created." | Human-readable result message. Do not use this value for program logic. |
| data | object | {…} | Endpoint-specific response payload. |
| data.orderId | integer | 1927055 | Identifier of the order. |
| data.mainStatus | string | "VALIDATED" | The main status value. |
| data.paymentStatus | string | "PAID" | The payment status value. |

### Send Receipt

Send an order receipt by email. Primarily used for anonymous orders placed via web ordering.

### `POST /orders/receipt` - Send Anonymous Order Receipt

Sends a receipt email for an anonymous order identified by its UUID. Only supports Web channel orders.

#### Request Body

```json
{
  "email": "customer@example.com",
  "orderUuid": "c4d2261e-2779-4eb6-beb0-cb41235c751e",
  "channelId": 2
}
```

##### Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| email | string | "customer@example.com" | Email address. |
| orderUuid | string | "c4d2261e-2779-4eb6-beb0-cb41235c751e" | The order uuid value. |
| channelId | integer | 2 | Identifier of the associated channel. |

#### Response

```json
{
  "status": 200,
  "code": "order_receipt_sent",
  "message": "The receipt has been sent successfully."
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 200 | HTTP status code returned by the API. |
| code | string | "order\_receipt\_sent" | Machine-readable application code for the result. |
| message | string | "The receipt has been sent successfully." | Human-readable result message. Do not use this value for program logic. |

### Bulk Order Creation

Create multiple orders in a single request. This endpoint is designed for system integrations (e.g. importing orders from external platforms). Each order is processed independently -- individual failures do not affect other orders in the batch.

Idempotency: Provide a comma-separated list of UUIDs in the `idempotency-key` header, one per order in the array.

### `POST /orders/bulk` - Create Bulk Orders

Creates multiple orders at once. Requires Brand role. Each order in the array is matched to its corresponding idempotency key by position.

#### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| idempotency-key | string (header) | Yes | Comma-separated list of UUIDs, one per order. |

#### Request Body

```json
[
  {
    "restaurantId": 2285,
    "channelId": 2,
    "consumptionMode": "MODE_TAKE_AWAY",
    "customerId": 1008881,
    "expectedAt": "2025-12-03T15:53:44.311Z",
    "cart": [
      {
        "productId": 1670891,
        "quantity": 1,
        "customPrice": 500
      }
    ],
    "payments": [
      {
        "paymentMethodId": 22521,
        "amount": 500,
        "quantity": 1
      }
    ],
    "metadata": {
      "externalOrderId": "EXT-001",
      "productsMetadata": [
        {
          "cartLine": 0,
          "cegidLine": "L1",
          "unit": "1"
        }
      ],
      "paymentsMetadata": [
        {
          "paymentLine": 0,
          "paymentMethodId": "22521"
        }
      ]
    }
  }
]
```

##### Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| \[\] | object | {…} | Object containing fields. |
| \[\].restaurantId | integer | 2285 | Identifier of the restaurant. |
| \[\].channelId | integer | 2 | Identifier of the associated channel. |
| \[\].consumptionMode | string | "MODE\_TAKE\_AWAY" | The consumption mode value. |
| \[\].customerId | integer | 1008881 | Identifier of the customer. |
| \[\].expectedAt | string | "2025-12-03T15:53:44.311Z" | Date or timestamp for expected. |
| \[\].cart | array | \[…\] | List of cart entries. |
| \[\].cart\[\] | object | {…} | Object containing cart fields. |
| \[\].cart\[\].productId | integer | 1670891 | Identifier of the product. |
| \[\].cart\[\].quantity | integer | 1 | The quantity value. |
| \[\].cart\[\].customPrice | integer | 500 | The custom price value. |
| \[\].payments | array | \[…\] | List of payments entries. |
| \[\].payments\[\] | object | {…} | Object containing payments fields. |
| \[\].payments\[\].paymentMethodId | integer | 22521 | Identifier of the associated payment method. |
| \[\].payments\[\].amount | integer | 500 | The amount value. |
| \[\].payments\[\].quantity | integer | 1 | The quantity value. |
| \[\].metadata | object | {…} | Additional metadata supplied with the response. |
| \[\].metadata.externalOrderId | string | "EXT-001" | Identifier of the associated external order. |
| \[\].metadata.productsMetadata | array | \[…\] | List of products metadata entries. |
| \[\].metadata.productsMetadata\[\] | object | {…} | Object containing products metadata fields. |
| \[\].metadata.productsMetadata\[\].cartLine | integer | 0 | The cart line value. |
| \[\].metadata.productsMetadata\[\].cegidLine | string | "L1" | The cegid line value. |
| \[\].metadata.productsMetadata\[\].unit | string | "1" | The unit value. |
| \[\].metadata.paymentsMetadata | array | \[…\] | List of payments metadata entries. |
| \[\].metadata.paymentsMetadata\[\] | object | {…} | Object containing payments metadata fields. |
| \[\].metadata.paymentsMetadata\[\].paymentLine | integer | 0 | The payment line value. |
| \[\].metadata.paymentsMetadata\[\].paymentMethodId | string | "22521" | Identifier of the associated payment method. |

#### Response

```json
{
  "status": 200,
  "code": "bulk_order_created",
  "data": [
    {
      "status": 200,
      "code": "order_created",
      "message": "Order created successfully",
      "data": {
        "omnichannelOrderId": "12345678",
        "restaurantId": 2285,
        "customerId": 1008881,
        "channelId": 2,
        "consumptionModeId": 2,
        "status": "VALIDATED"
      }
    }
  ]
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 200 | HTTP status code returned by the API. |
| code | string | "bulk\_order\_created" | Machine-readable application code for the result. |
| data | array | \[…\] | Endpoint-specific response payload. |
| data\[\] | object | {…} | Endpoint-specific response payload. |
| data\[\].status | integer | 200 | HTTP status code returned by the API. |
| data\[\].code | string | "order\_created" | Machine-readable application code for the result. |
| data\[\].message | string | "Order created successfully" | Human-readable result message. Do not use this value for program logic. |
| data\[\].data | object | {…} | Endpoint-specific response payload. |
| data\[\].data.omnichannelOrderId | string | "12345678" | Identifier of the associated omnichannel order. |
| data\[\].data.restaurantId | integer | 2285 | Identifier of the restaurant. |
| data\[\].data.customerId | integer | 1008881 | Identifier of the customer. |
| data\[\].data.channelId | integer | 2 | Identifier of the associated channel. |
| data\[\].data.consumptionModeId | integer | 2 | Identifier of the associated consumption mode. |
| data\[\].data.status | string | "VALIDATED" | HTTP status code returned by the API. |

### Per-Order Response Codes

| Code | Meaning |
| --- | --- |
| order\_created | Order was successfully created. |
| order\_already\_created | Idempotent duplicate -- returns the existing order. |
| invalid\_parameters | Validation failure for this specific order. |

### Multi-Restaurant Orders

Multi-orders allow customers to place a single order spanning multiple restaurants within the same brand. The workflow mirrors the single-order flow: **Preview**, then **Create**, with an optional **Confirm** step for Stripe payments.

### Preview Multi-Order

### `POST /multi-orders/preview` - Preview Multi-Order

Validate a multi-restaurant cart and calculate totals without persisting. Each restaurantCart contains its own cart array, menuId, and expectedAt.

#### Request Body

```json
{
  "brandId": 100,
  "channelId": 2,
  "consumptionMode": "MODE_TAKE_AWAY",
  "customerId": 1008881,
  "restaurantCarts": [
    {
      "restaurantId": 2285,
      "menuId": 24194,
      "expectedAt": "2025-12-03T15:00:00.000Z",
      "cart": [
        {
          "productId": 1670891,
          "quantity": 1,
          "steps": []
        }
      ]
    },
    {
      "restaurantId": 2290,
      "menuId": 24200,
      "expectedAt": "2025-12-03T15:30:00.000Z",
      "cart": [
        {
          "productId": 1670950,
          "quantity": 2,
          "steps": []
        }
      ]
    }
  ]
}
```

##### Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| brandId | integer | 100 | Identifier of the brand. |
| channelId | integer | 2 | Identifier of the associated channel. |
| consumptionMode | string | "MODE\_TAKE\_AWAY" | The consumption mode value. |
| customerId | integer | 1008881 | Identifier of the customer. |
| restaurantCarts | array | \[…\] | List of restaurant carts entries. |
| restaurantCarts\[\] | object | {…} | Object containing restaurant carts fields. |
| restaurantCarts\[\].restaurantId | integer | 2285 | Identifier of the restaurant. |
| restaurantCarts\[\].menuId | integer | 24194 | Identifier of the menu. |
| restaurantCarts\[\].expectedAt | string | "2025-12-03T15:00:00.000Z" | Date or timestamp for expected. |
| restaurantCarts\[\].cart | array | \[…\] | List of cart entries. |
| restaurantCarts\[\].cart\[\] | object | {…} | Object containing cart fields. |
| restaurantCarts\[\].cart\[\].productId | integer | 1670891 | Identifier of the product. |
| restaurantCarts\[\].cart\[\].quantity | integer | 1 | The quantity value. |
| restaurantCarts\[\].cart\[\].steps | array | \[\] | List of steps entries. |

#### Response

```json
{
  "status": 200,
  "code": "multi_order_verified",
  "message": "Your multi order was successfully verified.",
  "data": {
    "totalPriceWithTaxIncluded": 2500,
    "valid": true,
    "restaurantCarts": [
      {
        "restaurantId": 2285,
        "totalPriceWithTaxIncluded": 1000
      },
      {
        "restaurantId": 2290,
        "totalPriceWithTaxIncluded": 1500
      }
    ]
  }
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 200 | HTTP status code returned by the API. |
| code | string | "multi\_order\_verified" | Machine-readable application code for the result. |
| message | string | "Your multi order was successfully verified." | Human-readable result message. Do not use this value for program logic. |
| data | object | {…} | Endpoint-specific response payload. |
| data.totalPriceWithTaxIncluded | integer | 2500 | The total price with tax included value. |
| data.valid | boolean | true | The valid value. |
| data.restaurantCarts | array | \[…\] | List of restaurant carts entries. |
| data.restaurantCarts\[\] | object | {…} | Object containing restaurant carts fields. |
| data.restaurantCarts\[\].restaurantId | integer | 2285 | Identifier of the restaurant. |
| data.restaurantCarts\[\].totalPriceWithTaxIncluded | integer | 1000 | The total price with tax included value. |

### Create Multi-Order

Idempotency Required: You must provide a unique `idempotency-key` header.

### `POST /multi-orders` - Create Multi-Order

Creates a multi-restaurant order. Includes payment information shared across all restaurant carts. Each restaurant cart must include its own cart, menuId, expectedAt, and payments array.

#### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| idempotency-key | string (header) | Yes | Unique UUID to prevent duplicate creations. |

#### Request Body

```json
{
  "brandId": 100,
  "channelId": 2,
  "consumptionMode": "MODE_TAKE_AWAY",
  "customerId": 1008881,
  "payment": {
    "currency": "EUR",
    "metadata": "{}"
  },
  "restaurantCarts": [
    {
      "restaurantId": 2285,
      "menuId": 24194,
      "expectedAt": "2025-12-03T15:00:00.000Z",
      "cart": [
        {
          "productId": 1670891,
          "quantity": 1,
          "steps": []
        }
      ],
      "payments": [
        {
          "paymentMethodId": 22521,
          "amount": 1000,
          "quantity": 1
        }
      ]
    }
  ]
}
```

##### Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| brandId | integer | 100 | Identifier of the brand. |
| channelId | integer | 2 | Identifier of the associated channel. |
| consumptionMode | string | "MODE\_TAKE\_AWAY" | The consumption mode value. |
| customerId | integer | 1008881 | Identifier of the customer. |
| payment | object | {…} | Object containing payment fields. |
| payment.currency | string | "EUR" | ISO 4217 currency code. |
| payment.metadata | string | "{}" | Additional metadata supplied with the response. |
| restaurantCarts | array | \[…\] | List of restaurant carts entries. |
| restaurantCarts\[\] | object | {…} | Object containing restaurant carts fields. |
| restaurantCarts\[\].restaurantId | integer | 2285 | Identifier of the restaurant. |
| restaurantCarts\[\].menuId | integer | 24194 | Identifier of the menu. |
| restaurantCarts\[\].expectedAt | string | "2025-12-03T15:00:00.000Z" | Date or timestamp for expected. |
| restaurantCarts\[\].cart | array | \[…\] | List of cart entries. |
| restaurantCarts\[\].cart\[\] | object | {…} | Object containing cart fields. |
| restaurantCarts\[\].cart\[\].productId | integer | 1670891 | Identifier of the product. |
| restaurantCarts\[\].cart\[\].quantity | integer | 1 | The quantity value. |
| restaurantCarts\[\].cart\[\].steps | array | \[\] | List of steps entries. |
| restaurantCarts\[\].payments | array | \[…\] | List of payments entries. |
| restaurantCarts\[\].payments\[\] | object | {…} | Object containing payments fields. |
| restaurantCarts\[\].payments\[\].paymentMethodId | integer | 22521 | Identifier of the associated payment method. |
| restaurantCarts\[\].payments\[\].amount | integer | 1000 | The amount value. |
| restaurantCarts\[\].payments\[\].quantity | integer | 1 | The quantity value. |

#### Response

```json
{
  "status": 200,
  "code": "multi_order_created",
  "data": {
    "multiOrderId": "mo_abc123",
    "orders": [
      {
        "orderId": 1927060,
        "restaurantId": 2285,
        "mainStatus": "VALIDATED"
      }
    ]
  }
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 200 | HTTP status code returned by the API. |
| code | string | "multi\_order\_created" | Machine-readable application code for the result. |
| data | object | {…} | Endpoint-specific response payload. |
| data.multiOrderId | string | "mo\_abc123" | Identifier of the associated multi order. |
| data.orders | array | \[…\] | List of orders entries. |
| data.orders\[\] | object | {…} | Object containing orders fields. |
| data.orders\[\].orderId | integer | 1927060 | Identifier of the order. |
| data.orders\[\].restaurantId | integer | 2285 | Identifier of the restaurant. |
| data.orders\[\].mainStatus | string | "VALIDATED" | The main status value. |

### Confirm Multi-Order

### `POST /multi-orders/confirm` - Confirm Multi-Order Stripe Payment

Confirms a Stripe payment intent for a previously created multi-order. Similar to single-order confirm but for multi-restaurant orders.

#### Request Body

```json
{
  "multiOrderId": "mo_abc123",
  "paymentIntentId": "pi_3Abc123def456ghi"
}
```

##### Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| multiOrderId | string | "mo\_abc123" | Identifier of the associated multi order. |
| paymentIntentId | string | "pi\_3Abc123def456ghi" | Identifier of the associated payment intent. |

#### Response

```json
{
  "status": 200,
  "code": "multi_order_created",
  "data": {
    "multiOrderId": "mo_abc123",
    "orders": [
      {
        "orderId": 1927060,
        "restaurantId": 2285,
        "mainStatus": "VALIDATED",
        "paymentStatus": "PAID"
      }
    ]
  }
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 200 | HTTP status code returned by the API. |
| code | string | "multi\_order\_created" | Machine-readable application code for the result. |
| data | object | {…} | Endpoint-specific response payload. |
| data.multiOrderId | string | "mo\_abc123" | Identifier of the associated multi order. |
| data.orders | array | \[…\] | List of orders entries. |
| data.orders\[\] | object | {…} | Object containing orders fields. |
| data.orders\[\].orderId | integer | 1927060 | Identifier of the order. |
| data.orders\[\].restaurantId | integer | 2285 | Identifier of the restaurant. |
| data.orders\[\].mainStatus | string | "VALIDATED" | The main status value. |
| data.orders\[\].paymentStatus | string | "PAID" | The payment status value. |

### Get Multi-Order

### `GET /multi-orders/{multiOrderId}` - Get Multi-Order by ID

Retrieve a multi-order and all its constituent orders. No authentication required.

#### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| multiOrderId | string | Yes | The multi-order ID. |

#### Response

```json
{
  "status": 200,
  "code": "multi_order_found",
  "message": "Your multi order was successfully fetched.",
  "data": {
    "multiOrderId": "mo_abc123",
    "orders": [
      {
        "orderId": 1927060,
        "restaurantId": 2285,
        "mainStatus": "VALIDATED"
      }
    ]
  }
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 200 | HTTP status code returned by the API. |
| code | string | "multi\_order\_found" | Machine-readable application code for the result. |
| message | string | "Your multi order was successfully fetched." | Human-readable result message. Do not use this value for program logic. |
| data | object | {…} | Endpoint-specific response payload. |
| data.multiOrderId | string | "mo\_abc123" | Identifier of the associated multi order. |
| data.orders | array | \[…\] | List of orders entries. |
| data.orders\[\] | object | {…} | Object containing orders fields. |
| data.orders\[\].orderId | integer | 1927060 | Identifier of the order. |
| data.orders\[\].restaurantId | integer | 2285 | Identifier of the restaurant. |
| data.orders\[\].mainStatus | string | "VALIDATED" | The main status value. |

### Send Multi-Order Receipt

### `POST /multi-orders/receipt` - Send Multi-Order Receipt Email

Sends a receipt email for a multi-order. Supports anonymous orders. Only Web channel.

#### Request Body

```json
{
  "email": "customer@example.com",
  "multiOrderId": "mo_abc123",
  "channelId": 2
}
```

##### Request Body Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| email | string | "customer@example.com" | Email address. |
| multiOrderId | string | "mo\_abc123" | Identifier of the associated multi order. |
| channelId | integer | 2 | Identifier of the associated channel. |

#### Response

```json
{
  "status": 200,
  "code": "order_receipt_sent",
  "message": "The receipt has been sent successfully."
}
```

##### Response Properties

Every field in the example is listed below. Explicit requiredness is shown when the endpoint contract defines it.

| Property | Type | Example | Description |
| --- | --- | --- | --- |
| status | integer | 200 | HTTP status code returned by the API. |
| code | string | "order\_receipt\_sent" | Machine-readable application code for the result. |
| message | string | "The receipt has been sent successfully." | Human-readable result message. Do not use this value for program logic. |
