Create Reservation
POST /v1/nextrestaurant/reservations
Book a table
Needs reservations:write and the Reservations app. Created pending for
staff to confirm, exactly like a storefront booking — the guest gets the same
acknowledgement email. The slot is re-checked under the location lock, so
two clients cannot both take the last seats. Open in demo mode, like the
storefront. Also limited to 30 bookings per 5 minutes per address.
Request
POST https://your-site.example/api/index.php/v1/nextrestaurant/reservations
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 | (min 1) |
guest_name |
string |
Yes | (min length 1) |
guest_phone |
string|null |
No | |
guest_email |
string|null |
No | (email) |
party_size |
integer |
Yes | (min 1, max 100) |
reserved_at |
string |
Yes | The venue's local time, Y-m-d H:i. Pick one from /availability. |
special_requests |
string|null |
No |
{
"location_id": 2,
"guest_name": "Dana Whitfield",
"guest_email": "[email protected]",
"party_size": 4,
"reserved_at": "2026-12-24 19:30"
}
Responses
201 — Booked (pending confirmation).
{
"data": {
"id": 1,
"reservation_number": "RES-000109",
"location_id": 1,
"customer_id": 1,
"area_id": 1,
"table_ids": [
1
],
"guest_name": "string",
"guest_phone": "string",
"guest_email": "string",
"party_size": 1,
"reserved_at": "string",
"reserved_at_local": "string",
"duration_minutes": 1,
"status": "pending",
"deposit_amount": 1.5,
"special_requests": "string",
"source": "web",
"created_at": "string",
"updated_at": "string"
}
}
| Field | Type | Meaning |
|---|---|---|
data.id |
integer |
(int64) |
data.reservation_number |
string|null |
|
data.location_id |
integer |
|
data.customer_id |
integer|null |
|
data.area_id |
integer|null |
|
data.table_ids[] |
integer[] |
|
data.guest_name |
string |
|
data.guest_phone |
string|null |
|
data.guest_email |
string|null |
|
data.party_size |
integer |
|
data.reserved_at |
string |
UTC. |
data.reserved_at_local |
string|null |
The venue's wall clock — what the guest was told. |
data.duration_minutes |
integer |
|
data.status |
string |
One of pending, confirmed, seated, completed, no_show, cancelled, waitlist. |
data.deposit_amount |
number |
|
data.special_requests |
string|null |
|
data.source |
string|null |
|
data.created_at |
string|null |
|
data.updated_at |
string|null |
Errors
| Status | Code | When |
|---|---|---|
400 |
INVALID_JSON |
INVALID_JSON (the body is not a JSON object) or INVALID_IDEMPOTENCY_KEY. |
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. |
409 |
SLOT_UNAVAILABLE |
SLOT_UNAVAILABLE (closed, fully booked, too soon — the title says which), TABLE_CONFLICT, ILLEGAL_TRANSITION (meta.allowed lists the statuses reachable from meta.from), or IDEMPOTENCY_IN_PROGRESS. |
413 |
PAYLOAD_TOO_LARGE: bodies are limited to 64 KB. |
|
415 |
UNSUPPORTED_MEDIA_TYPE: send Content-Type: application/json. |
|
422 |
VALIDATION_FAILED |
One error per invalid field, each with source.pointer. |
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. |
Examples
curl
curl -s -X POST "https://your-site.example/api/index.php/v1/nextrestaurant/reservations" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"location_id":2,"guest_name":"Dana Whitfield","guest_email":"[email protected]","party_size":4,"reserved_at":"2026-12-24 19:30"}'
PHP
<?php
$ch = curl_init('https://your-site.example/api/index.php/v1/nextrestaurant/reservations');
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,"guest_name":"Dana Whitfield","guest_email":"[email protected]","party_size":4,"reserved_at":"2026-12-24 19:30"}',
]);
$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/reservations", {
method: 'POST',
headers: {
"Authorization": `Bearer ${API_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"location_id": 2,
"guest_name": "Dana Whitfield",
"guest_email": "[email protected]",
"party_size": 4,
"reserved_at": "2026-12-24 19:30"
}),
});
const data = await response.json();