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.
There is no idempotency key. A retried request that actually succeeded the first time creates a second order, so on a timeout reconcile rather than blindly retrying.
Request
POST https://your-site.example/api/index.php/v1/nextrestaurant/orders
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",
"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.id |
integer |
(int64) |
data.order_number |
string |
(pattern ^ORD-\d{6}$) |
data.location_id |
integer |
(int64) |
data.channel |
string |
|
data.status |
string |
Always placed on creation; it cannot be set by the caller. |
data.fulfillment_status |
string\|null |
|
data.subtotal |
number |
|
data.discount_total |
number |
|
data.tax_total |
number |
|
data.service_charge |
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.placed_at |
string\|null |
Y-m-d H:i:s in the site's configured timezone. |
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[].line_total |
number |
|
data.items[].notes |
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 |
MODULE_DISABLED |
This module is disabled. |
403 |
API_DISABLED |
API is disabled |
404 |
UNKNOWN_LOCATION |
No such published location. |
429 |
RATE_LIMITED |
Too many orders from this address — 60 per 5 minutes. No order was created, so the request is safe to retry after a pause. |
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}]}'