Alex Reservations - External Bookings API
REST API for creating reservations from external platforms such as TheFork, OpenTable, or any integration tool like Zapier.
Base URL: https://your-domain.com/wp-json/alexr/v1
Authentication
All requests must include a valid API key. Two methods are supported:
Option 1: X-API-Key header (recommended)
X-API-Key: your-api-key-here
Option 2: Authorization Bearer header
Authorization: Bearer your-api-key-here
API keys are 64-character hex strings tied to a specific restaurant. They can be revoked individually by setting them as inactive.
Authentication errors
| HTTP Code | Error Code | Description |
|---|---|---|
| 401 | rest_missing_api_key |
No API key provided in the request headers |
| 401 | rest_invalid_api_key |
The API key does not exist or has been deactivated |
Endpoints
Create Booking
POST /wp-json/alexr/v1/bookings
Creates a new reservation in Alex Reservations.
Request
Content-Type: application/json
Parameters
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
first_name |
string | Yes | - | Guest first name |
last_name |
string | No | "" |
Guest last name |
email |
string | Yes | - | Guest email address |
phone |
string | No | "" |
Guest phone number (e.g. "+34612345678") |
date |
string | Yes | - | Reservation date in YYYY-MM-DD format |
time |
string | Yes | - | Reservation time in HH:MM format (24h) |
party |
integer | Yes | - | Number of guests (must be >= 1) |
restaurant_id |
integer | No | API key's restaurant | Restaurant ID. If omitted, uses the restaurant associated with the API key |
service_id |
integer | No | Auto-resolved | Shift or Event ID. If omitted, the system automatically finds the active service for the given date and time |
platform |
string | No | API key's platform or "API" |
Source platform name (e.g. "TheFork", "OpenTable"). Stored in the booking's source field |
status |
string | No | "booked" |
Booking status. Allowed values: "pending", "booked" |
notes |
string | No | null |
Guest notes or special requests |
send_notifications |
boolean | No | false |
Whether to send confirmation email/SMS to the guest |
Example request
curl -X POST https://your-domain.com/wp-json/alexr/v1/bookings \
-H "Content-Type: application/json" \
-H "X-API-Key: a1b2c3d4e5f6..." \
-d '{
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"phone": "+34612345678",
"date": "2025-04-15",
"time": "20:30",
"party": 4,
"platform": "TheFork",
"notes": "Window table if possible",
"status": "booked",
"send_notifications": false
}'
Successful response
HTTP 201 Created
{
"success": true,
"booking_id": 456,
"uuid": "bo_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "booked"
}
Duplicate response
HTTP 200 OK - Returned when a booking with the same data already exists (see Idempotency / Duplicate Detection)
{
"success": true,
"booking_id": 456,
"uuid": "bo_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "booked",
"duplicate": true
}
The response contains the original booking data plus a "duplicate": true flag. No new booking is created.
Error responses
HTTP 400 Bad Request - Validation errors
{
"success": false,
"error": "Validation failed",
"details": {
"email": "email is required",
"party": "party is required and must be >= 1"
}
}
HTTP 400 Bad Request - Invalid time format
{
"success": false,
"error": "Invalid time format. Use HH:MM (e.g. 20:30)"
}
HTTP 400 Bad Request - Invalid status
{
"success": false,
"error": "Invalid status. Allowed values: pending, booked"
}
HTTP 401 Unauthorized - Missing or invalid API key
{
"code": "rest_missing_api_key",
"message": "API key is required. Send it via X-API-Key header or Authorization: Bearer <key>.",
"data": { "status": 401 }
}
HTTP 404 Not Found - Restaurant not found
{
"success": false,
"error": "Restaurant not found"
}
How it works
Service auto-resolution
When service_id is not provided, the API automatically finds the active shift or event for the given date and time:
- Searches all active shifts for the restaurant
- Checks if the shift operates on the booking's day of the week
- Checks day-specific seating hours first, then falls back to the shift's default hours
- If no shift matches, searches active events
- The first matching service is used
If no service is found, the booking is still created but without a linked service.
Duration calculation
The booking duration is automatically calculated from the matched service configuration:
-
Fixed duration mode: Uses the service's configured
duration_time(in seconds) -
Per-covers mode: Duration depends on the party size, using the service's
duration_coversmapping - Fallback: 5400 seconds (1.5 hours) if no service is found
Customer management
- If a customer with the same email already exists for the restaurant, the booking is linked to that customer
- If no customer exists, a new customer record is created automatically
Platform tracking
The platform value is stored in the booking's source field. This allows filtering bookings by origin in the dashboard:
-
source = "TheFork"- Booking came from TheFork -
source = "OpenTable"- Booking came from OpenTable -
source = "API"- Generic API booking (default when no platform is specified) -
source = NULL- Booking created from the widget or dashboard
Notifications
By default, no email or SMS notifications are sent when creating a booking via API. This avoids duplicating notifications that the external platform (TheFork, OpenTable) already sends.
Set send_notifications: true if you want Alex Reservations to send its own notifications to the guest. When enabled, the following notifications are sent:
- Email: Booking confirmation email
- SMS: SMS notification
- WhatsApp: WhatsApp notification (if configured)
-
Dashboard notification: The restaurant receives a
BookingOnlineReceivednotification
Idempotency / Duplicate Detection
The API includes built-in protection against duplicate bookings. When a request is received, the system generates a unique idempotency key based on the combination of:
-
restaurant_id -
email(case-insensitive) -
date -
time -
party
If a previous successful booking (HTTP 201) already exists with the same idempotency key, the API returns the original booking data with an additional "duplicate": true flag and an HTTP 200 status code. No new booking is created.
This is useful for scenarios where:
- Zapier retries: If a webhook delivery fails and Zapier retries the same request, the booking won't be duplicated
- Network issues: If the client doesn't receive the response and resends the request
- Platform sync: If an external platform sends the same reservation twice
How it works
- A SHA-256 hash is generated from
restaurant_id|email|date|time|party - The hash is checked against previous successful API logs
- If a match is found, the original response is returned with
"duplicate": true - If no match is found, the booking is created normally
- The idempotency key is stored in the
api_logstable for future checks
Important notes
- The idempotency check only considers successful bookings (HTTP 201). Failed requests do not block future attempts
- Two bookings for the same person, date, time, and party size at the same restaurant are considered duplicates
- If you need to create a second booking for the same person at the same time (e.g., different party size), change the
partyvalue - The duplicate detection is logged as a separate action (
create_booking_duplicate) in the API logs
API Logging
Every API request is logged in the api_logs table with:
- Restaurant ID
- API key used
- Action performed (
create_bookingorcreate_booking_duplicate) - HTTP status code
- Full request body (sensitive data excluded)
- Full response body
- Client IP address
- Idempotency key (SHA-256 hash for duplicate detection)
- Timestamp
Setting up API keys
From the dashboard (recommended)
API keys can be managed from the Alex Reservations dashboard:
- Go to Settings > Integrations > API Keys
- Click Generate New API Key
- Enter a name (e.g.
"TheFork production key") and platform (e.g."TheFork") - The key is generated automatically and shown once. Copy it and store it securely
- You can toggle keys active/inactive, edit their name/platform, or delete them
Access to the API Keys settings page is controlled by the API Keys permission in the Roles section. By default, only the Super Manager role has this permission enabled.
Manual setup via SQL
API keys are stored in the api_keys database table. You can also insert them manually:
INSERT INTO {prefix}_api_keys
(uuid, restaurant_id, api_key, platform, name, is_active, date_created, date_modified)
VALUES
('ak_unique_id', 1, 'your-64-char-hex-key', 'TheFork', 'TheFork via Zapier', 1, NOW(), NOW());
| Column | Description |
|---|---|
restaurant_id |
The restaurant this key grants access to |
api_key |
64-character hex string. Auto-generated from the dashboard, or generate manually with bin2hex(random_bytes(32)) in PHP |
platform |
Default platform name for bookings made with this key (e.g. "TheFork") |
name |
Human-readable description (e.g. "TheFork production key") |
is_active |
Set to 0 to revoke the key |
You can create multiple keys per restaurant, each with a different platform.
Zapier integration example
When integrating with TheFork via Zapier:
- Trigger: New reservation in TheFork (via Zapier's TheFork integration)
- Action: Webhooks by Zapier > POST request
-
Configuration:
-
URL:
https://your-domain.com/wp-json/alexr/v1/bookings - Payload Type: JSON
-
Headers:
X-API-Key: your-api-key-here - Body mapping:
-
URL:
| Zapier field | API field |
|---|---|
| First Name | first_name |
| Last Name | last_name |
email |
|
| Phone | phone |
| Date | date (make sure format is YYYY-MM-DD) |
| Time | time (make sure format is HH:MM, 24h) |
| Party Size | party |
| Notes | notes |
| (static value) | platform = "TheFork" |
Response codes summary
| HTTP Code | Meaning |
|---|---|
| 200 | Duplicate booking detected. Returns original booking data with "duplicate": true. No new booking created |
| 201 | Booking created successfully |
| 400 | Validation error (missing fields, invalid format, invalid status) |
| 401 | Authentication error (missing or invalid API key) |
| 404 | Restaurant not found |