# Create an order `POST https://management.horeca-24.de/api/partner/v1/orders` Checked in this order; the first problem is answered: 1. Key (`401`/`403`), rate limit (`429`). 2. JSON (`400 MALFORMED_REQUEST`), schema (`422 INVALID_FIELD` with `path`). 3. Known `externalId`: the same payload answers `200` with `duplicate: true`; another payload `409 ORDER_CONFLICT`. 4. Store offline (`409 STORE_OFFLINE`), no webhook URL (`409 WEBHOOK_NOT_CONFIGURED`). 5. Currency not the restaurant's (`422 CURRENCY_MISMATCH`); not checked before the restaurant's first menu sync. 6. Sums with your prices (`422 INVALID_AMOUNTS`): line total = `quantity × (unitPrice + Σ modifier.unitPrice × modifier.quantity)`; `itemsSubtotal` = Σ line totals; `0 ≤ discount ≤ itemsSubtotal`; `total = itemsSubtotal − discount + deliveryFee + serviceFee + tip`; `deliveryFee = 0` for a pickup. 7. Payment: `cash`/`unpaid`/`amountPaid 0`, or `online`/`paid`/`amountPaid = total` with a `reference` (`422 INVALID_FIELD`, a wrong `amountPaid` `422 INVALID_AMOUNTS`). Names, prices and tax rates are taken as sent. Prices are not compared with the menu, and availability is not checked: a product switched off since your copy of the menu is matched and accepted, and a line that cannot be matched (deleted since) reaches the till with your name, price and `taxRate`. ## Headers - `Authorization` (string, required): Bearer $PARTNER_API_KEY ## Request `application/json` - `schemaVersion` (string, required, Value: `1`) - `externalId` (string, required, pattern: `^[A-Za-z0-9._~-]{1,64}$`) Your order id, unique per connection - `currency` (string, required, pattern: `^[A-Z]{3}$`) ISO 4217; the menu file's currency - `type` (string, required, `pickup` | `delivery`) - `createdAt` (string, required, format: date-time) ISO 8601 with Z or an offset - `fulfillment` (object, required) - `mode` (string, required, `asap` | `scheduled`) - `requestedAt` (string, optional, format: date-time) Pickup time for a pickup, arrival time for a delivery - `courier` (string, optional, `store` | `partner`) store: the restaurant delivers; partner: your courier (enables courier updates) - `customer` (object, required) - `name` (string, required, 1-200 chars) - `phone` (string, required, 1-50 chars) - `email` (string, optional, 0-254 chars, format: email) - `address` (object, optional) - `street` (string, required, 1-200 chars) - `streetNumber` (string, required, 1-50 chars) - `zipCode` (string, required, 1-20 chars) - `city` (string, required, 1-100 chars) - `countryCode` (string, required, pattern: `^[A-Z]{2}$`) ISO 3166-1 alpha-2 - `extraAddressInfo` (string, optional, 0-500 chars) - `lat` (number, optional, -90-90) - `lng` (number, optional, -180-180) Without lat/lng the till shows no map or distance - `items` (array of object, required, 1-200 items) - `lineId` (string, required, 1-128 chars) Unique within the order - `productId` (string, required, 1-128 chars) The menu file's products[].id - `name` (string, required, 1-200 chars) As the customer saw it - `quantity` (integer, required, 1-999) - `unitPrice` (integer, required, 0-10000000) Gross - `taxRate` (number, required, 0-100) The line's tax rate (the menu file's taxRate); it applies to the line's modifiers too - `note` (string, optional, 0-500 chars) - `modifiers` (array of object, required, 0-100 items) [] for none - `modifierGroupId` (string, required, 1-128 chars) The menu file's modifierGroups[].id - `modifierOptionId` (string, required, 1-128 chars) The menu file's options[].id - `name` (string, required, 1-200 chars) As the customer saw it - `quantity` (integer, required, 1-999) Per piece of the line - `unitPrice` (integer, required, 0-10000000) Gross - `amounts` (object, required) total = itemsSubtotal − discount + deliveryFee + serviceFee + tip - `itemsSubtotal` (integer, required, 0-10000000) Minor units (cents) - `discount` (integer, required, 0-10000000) Minor units (cents) - `deliveryFee` (integer, required, 0-10000000) Minor units (cents) - `serviceFee` (integer, required, 0-10000000) Minor units (cents) - `tip` (integer, required, 0-10000000) Minor units (cents) - `total` (integer, required, 0-10000000) Minor units (cents) - `payment` (object, required) cash / unpaid / amountPaid 0 (the restaurant collects), or online / paid / amountPaid = total with a reference - `method` (string, required, `cash` | `online`) - `status` (string, required, `unpaid` | `paid`) - `amountPaid` (integer, required, 0-10000000) Minor units (cents) - `reference` (string, optional, 1-200 chars) Required for online - `note` (string, optional, 0-1000 chars) - `deliveryInstructions` (string, optional, 0-1000 chars) ## Response ### 200 — The same order again (same `externalId`, identical payload); `duplicate` is `true` - `orderId` (string, required, pattern: `^oo_[0-9a-f]{64}$`) Our order id - `externalId` (string, required, pattern: `^[A-Za-z0-9._~-]{1,64}$`) Your order id, unique per connection - `status` (string, required, Value: `inProgress`) Always inProgress; read the current status with GET /orders/{externalId} - `receivedAt` (string, required, format: date-time) - `duplicate` (boolean, required) ### 201 — Received - `orderId` (string, required, pattern: `^oo_[0-9a-f]{64}$`) Our order id - `externalId` (string, required, pattern: `^[A-Za-z0-9._~-]{1,64}$`) Your order id, unique per connection - `status` (string, required, Value: `inProgress`) Always inProgress; read the current status with GET /orders/{externalId} - `receivedAt` (string, required, format: date-time) - `duplicate` (boolean, required) ## Errors - `400`: An error; see `ErrorCode` - `401`: `UNAUTHORIZED`: the key is missing, malformed, unknown, wrong, expired or revoked - `403`: `CONNECTION_REVOKED`: the restaurant deleted the connection; stop sending - `409`: `STORE_OFFLINE`, `WEBHOOK_NOT_CONFIGURED`, `ORDER_CONFLICT`, `INVALID_TRANSITION` or `REQUEST_IN_PROGRESS`. `REQUEST_IN_PROGRESS` carries `Retry-After` (seconds); retry with the same body. - `422`: An error; see `ErrorCode` - `429`: `RATE_LIMITED`: 60 per minute and connection, shared by orders, cancel and delivery - `500`: An error; see `ErrorCode` - `503`: An error; see `ErrorCode` ## Code example (cURL) ```sh curl -X POST https://management.horeca-24.de/api/partner/v1/orders \ -H "Authorization: Bearer $PARTNER_API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "schemaVersion": "1", "externalId": "A-1001", "currency": "EUR", "type": "delivery", "createdAt": "2026-10-15T10:00:00Z", "fulfillment": { "mode": "scheduled", "requestedAt": "2026-10-15T12:30:00+02:00", "courier": "store" }, "customer": { "name": "Kim Meyer", "phone": "+49 40 123456", "email": "kim@example.com", "address": { "street": "Hauptstraße", "streetNumber": "1", "zipCode": "20095", "city": "Hamburg", "countryCode": "DE", "extraAddressInfo": "2nd floor", "lat": 53.55, "lng": 10.0 } }, "items": [ { "lineId": "l1", "productId": "p1", "name": "Margherita", "quantity": 2, "unitPrice": 1200, "taxRate": 7, "note": "well done", "modifiers": [ { "modifierGroupId": "g1", "modifierOptionId": "o1", "name": "Cheese", "quantity": 1, "unitPrice": 150 } ] } ], "amounts": { "itemsSubtotal": 2700, "discount": 200, "deliveryFee": 300, "serviceFee": 0, "tip": 0, "total": 2800 }, "payment": { "method": "online", "status": "paid", "amountPaid": 2800, "reference": "pay_8d2f" }, "note": "Please ring twice", "deliveryInstructions": "Back building" }' ``` ## Response 200 — Example ```json { "orderId": "oo_0000000000000000000000000000000000000000000000000000000000000000", "externalId": "A", "status": "inProgress", "receivedAt": "2026-01-15T10:00:00Z", "duplicate": true } ```