Create Order
POST /v1/nextrestaurant/orders
Place an order
Creates an ordinary order: it routes to kitchen stations by your routing rules, depletes stock at the status you configured, settles into the payments ledger, and appears in reports with its channel recorded.
Orders arrive unpaid — this endpoint records an order, it does not take payment.
Needs orders:write and the Orders app. Open in demo mode, like the
storefront. Send an Idempotency-Key so a retry after a timeout cannot
create a second order. Also limited to 60 orders per 5 minutes per address.
Request
POST https://your-site.example/api/index.php/v1/nextrestaurant/orders
Parameters
| Name | Type | In | Required | Description |
|---|---|---|---|---|
Idempotency-Key |
string |
header |
No | Any unique string up to 255 characters, e.g. a UUID. Send the same key when retrying after a timeout: the first response is replayed (with Idempotent-Replayed: true) and nothing happens twice. Reusing a key for a different request is 422 IDEMPOTENCY_MISMATCH; while the first request is still running it is 409 IDEMPOTENCY_IN_PROGRESS. Keys are kept 24 hours, per API key. (max length 255) |
Request body
Content type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
location_id |
integer |
Yes | Where the order is placed. (int64) |
items |
object[] |
Yes | |
items.item_id |
integer |
Yes | (int64) |
items.qty |
number |
Yes | |
items.variation_id |
integer|null |
No | Required in practice whenever the item has variations. (int64) |
items.notes |
string |
No | |
items.modifiers |
object[] |
No | |
items.modifiers.modifier_id |
integer |
Yes | (int64) |
items.modifiers.qty |
integer |
No | Defaults to 1. |
channel |
string |
No | Recorded on the order and visible in reports. Any other value falls back to web rather than being refused. One of web, phone, takeaway, delivery. Defaults to web. |
service_mode |
string |
No | Free text stored with the order. |
notes |
string |
No | Order-level note, shown to the kitchen. |
customer |
object |
No | Guest details stored with the order. Every field optional. |
customer.name |
string |
No | |
customer.phone |
string |
No | |
customer.email |
string |
No | (email) |
{
"location_id": 2,
"channel": "web",
"service_mode": "takeaway",
"notes": "Ring the bell at the side door",
"customer": {
"name": "Dana Whitfield",
"phone": "+1 555 0142",
"email": "[email protected]"
},
"items": [
{
"item_id": 16,
"variation_id": 2,
"qty": 1,
"notes": "Well done",
"modifiers": [
{
"modifier_id": 5,
"qty": 1
}
]
},
{
"item_id": 1,
"qty": 2
}
]
}
Responses
201 — The order was created.
{
"data": {
"id": 591,
"order_number": "ORD-000591",
"location_id": 2,
"channel": "web",
"status": "placed",
"fulfillment_status": "in_kitchen",
"subtotal": 18.5,
"discount_total": 0,
"tax_total": 1.53,
"service_charge": 0,
"delivery_fee": 0,
"grand_total": 20.03,
"currency": null,
"notes": null,
"placed_at": "2026-09-07 21:20:26",
"updated_at": "2026-09-07 21:20:26",
"payments": [],
"items": [
{
"id": 1300,
"item_id": 16,
"variation_id": 2,
"name_snapshot": "Margherita Pizza (Large)",
"qty": 1,
"unit_price": 17,
"modifiers_total": 1.5,
"line_total": 18.5,
"notes": null
}
]
}
}
| Field | Type | Meaning |
|---|---|---|
data |
object |
An order with its lines and payments — the same object GET /orders/{id} returns and every order.* webhook carries as data. Datetimes are Y-m-d H:i:s in UTC with no zone suffix. |
data.id |
integer |
(int64) |
data.order_number |
string|null |
|
data.location_id |
integer |
(int64) |
data.customer_id |
integer|null |
|
data.table_id |
integer|null |
|
data.channel |
string|null |
|
data.source |
string|null |
Where an online order came from (web, qr, api); null for staff-entered orders. |
data.status |
string |
placed on creation; then confirmed, preparing, ready, served, completed, cancelled or refunded. |
data.fulfillment_status |
string|null |
|
data.guest_count |
integer|null |
|
data.subtotal |
number |
|
data.discount_total |
number |
|
data.tax_total |
number |
|
data.service_charge |
number |
|
data.tip_total |
number |
|
data.delivery_fee |
number |
|
data.grand_total |
number |
|
data.currency |
string|null |
Often null — take the currency from the location. |
data.notes |
string|null |
|
data.scheduled_for |
string|null |
|
data.placed_at |
string|null |
|
data.completed_at |
string|null |
|
data.cancelled_at |
string|null |
|
data.cancel_reason |
string|null |
|
data.created_at |
string|null |
|
data.updated_at |
string|null |
|
data.items[].id |
integer |
(int64) |
data.items[].item_id |
integer |
(int64) |
data.items[].variation_id |
integer|null |
(int64) |
data.items[].name_snapshot |
string |
The line as the kitchen and the receipt will show it, including the variation. |
data.items[].qty |
number |
|
data.items[].unit_price |
number |
Resolved from the catalogue. A submitted price is ignored. |
data.items[].modifiers_total |
number |
|
data.items[].discount_amount |
number |
|
data.items[].line_total |
number |
|
data.items[].status |
string|null |
|
data.items[].seat_number |
integer|null |
|
data.items[].notes |
string|null |
|
data.payments[].id |
integer |
(int64) |
data.payments[].order_id |
integer|null |
|
data.payments[].location_id |
integer|null |
|
data.payments[].method |
string|null |
|
data.payments[].amount |
number |
|
data.payments[].tip_amount |
number |
|
data.payments[].refunded_total |
number |
Sum of the refunds against this payment. |
data.payments[].currency |
string|null |
|
data.payments[].status |
string |
|
data.payments[].reference |
string|null |
The provider's or the till's transaction reference. |
data.payments[].paid_at |
string|null |
|
data.payments[].created_at |
string|null |
|
data.payments[].updated_at |
string|null |
Errors
| Status | Code | When |
|---|---|---|
400 |
INVALID_ORDER |
One of the items is no longer available. |
401 |
INVALID_TOKEN |
The token is missing, wrong, or none has been generated. |
403 |
INSUFFICIENT_SCOPE |
This API key does not have the permission this request needs. |
403 |
MODULE_DISABLED |
This module is disabled. |
403 |
API_DISABLED |
API is disabled |
404 |
UNKNOWN_LOCATION |
No such published location. |
429 |
RATE_LIMITED |
Too many requests. RATE_LIMITED with a Retry-After header: this key used its budget for the minute, or this address sent 20 bad tokens in 5 minutes. Nothing ran, so the request is safe to retry after the wait. |
500 |
ORDER_FAILED |
The order could not be created. The message is deliberately generic; the detail is in the Joomla log under administrator/logs/. |
Examples
curl
curl -s -X POST "https://your-site.example/api/index.php/v1/nextrestaurant/orders" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"location_id":2,"channel":"web","service_mode":"takeaway","notes":"Ring the bell at the side door","customer":{"name":"Dana Whitfield","phone":"+1 555 0142","email":"[email protected]"},"items":[{"item_id":16,"variation_id":2,"qty":1,"notes":"Well done","modifiers":[{"modifier_id":5,"qty":1}]},{"item_id":1,"qty":2}]}'
PHP
<?php
$ch = curl_init('https://your-site.example/api/index.php/v1/nextrestaurant/orders');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('API_TOKEN'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => '{"location_id":2,"channel":"web","service_mode":"takeaway","notes":"Ring the bell at the side door","customer":{"name":"Dana Whitfield","phone":"+1 555 0142","email":"[email protected]"},"items":[{"item_id":16,"variation_id":2,"qty":1,"notes":"Well done","modifiers":[{"modifier_id":5,"qty":1}]},{"item_id":1,"qty":2}]}',
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
JavaScript
const API_TOKEN = process.env.API_TOKEN;
const response = await fetch("https://your-site.example/api/index.php/v1/nextrestaurant/orders", {
method: 'POST',
headers: {
"Authorization": `Bearer ${API_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"location_id": 2,
"channel": "web",
"service_mode": "takeaway",
"notes": "Ring the bell at the side door",
"customer": {
"name": "Dana Whitfield",
"phone": "+1 555 0142",
"email": "[email protected]"
},
"items": [
{
"item_id": 16,
"variation_id": 2,
"qty": 1,
"notes": "Well done",
"modifiers": [
{
"modifier_id": 5,
"qty": 1
}
]
},
{
"item_id": 1,
"qty": 2
}
]
}),
});
const data = await response.json();