REST endpoints for partner integrations — POS systems, chat widgets, AI phone agents, anything that needs to read availability or write bookings on behalf of a restaurant.
https://tablefox.co.uk/api/v1/* — semver, breaking changes only on a major bump.{ "error": "...", "errors": ["...", "..."] } shape with the right HTTP status (400/401/403/404/409/500).
Each restaurant has its own API key, issued by TableFox. To integrate on behalf of a restaurant, ask them to generate a key from their TableFox dashboard, or email hello@tablefox.co.uk. Master keys (one key, many restaurants) are available for accredited partners — get in touch and we'll provision one.
Check availability for a party of 4 on 5 April 2026:
# cURL curl 'https://tablefox.co.uk/api/v1/availability?date=2026-04-05&covers=4' \ -H 'X-API-Key: your-key-here'
Create a booking:
# cURL curl 'https://tablefox.co.uk/api/v1/bookings' \ -X POST \ -H 'X-API-Key: your-key-here' \ -H 'Content-Type: application/json' \ -d '{"date":"2026-04-05","time":"19:00","covers":4,"first_name":"Alex","phone":"+447450000000"}'
Once you've got that working, download the OpenAPI spec and import it into Postman, Insomnia or your client generator of choice for the rest.
Three ways to send your key — pick whichever your HTTP client prefers. Click any endpoint below to see request fields, responses and error codes.
GET /bookings/lookup, POST /bookings/modify, POST /bookings/cancel) are tagged self-service in the sidebar — they're the customer-facing variants used by chat widgets and AI phone agents. They require the booking's phone number to match the request and respect the restaurant's edit/cancel cutoff window. If you're building an integration on behalf of the restaurant (POS, EPOS, CRM, etc.) you'll usually want the equivalent untagged endpoints instead: POST /bookings/:id/modify and POST /bookings/:id/status with CANCELLED.
All integration endpoints require authentication via one of:
| X-API-Key header | X-API-Key: your-key-here |
| Query parameter | ?api_key=your-key-here |
| Bearer token | Authorization: Bearer your-key-here |
Per-restaurant key: Identifies the restaurant automatically. No restaurant_id needed.
Master key: Requires restaurant_id parameter to identify which restaurant.
Check available time slots for a date and party size.
| Param | Type | Required | Description |
|---|---|---|---|
date | string | Yes | Date in YYYY-MM-DD format |
covers | integer | Yes | Number of guests (1-30) |
service | string | No | Service code filter (e.g. LUNCH, DINNER) |
restaurant_id | integer | Master key only | Restaurant ID |
{ "success": true, "restaurant": "Viva Brazil", "date": "2026-04-05", "covers": 4, "slots": [{ "time": "12:00", "service": "LUNCH", "available": true }, ...], "count": 15 }
Create a new booking. Sends confirmation email/SMS if auto-accepted. Sends payment link if restaurant requires deposit/card hold.
| Field | Type | Required | Description |
|---|---|---|---|
date | string | Yes | YYYY-MM-DD |
time | string | Yes | HH:MM (24-hour) |
covers | integer | Yes | Number of guests (1-30) |
first_name | string | Yes | Customer first name |
last_name | string | No | Customer last name |
phone | string | Conditional | Phone or email required |
email | string | Conditional | Required if restaurant has payment mode enabled |
notes | string | No | Special requests |
tags | string/array | No | Comma-separated or array: BIRTHDAY, ANNIVERSARY, ALLERGY, HIGH_CHAIR, VIP |
source | string | No | ONLINE, INTERNAL, PHONE, WALKIN, AI_PHONE. Default: PHONE |
restaurant_id | integer | Master key only | Restaurant ID |
{ "success": true, "booking": { "booking_id": 123, "booking_reference": "RB-20260405-A1B2C3", "status": "CONFIRMED", "date": "2026-04-05", "time": "19:00", "covers": 4, "customer_name": "John Smith" }, "payment": null, "message": "Booking confirmed successfully" }
Payment response (when deposit/hold required):
{ "success": true, "booking": { ... "status": "PENDING_PAYMENT" }, "payment": { "required": true, "mode": "DEPOSIT", "amount_formatted": "£20.00", "message": "A deposit of £20.00 is required. A payment link has been sent." } }
List bookings for a date with optional filters. Cancelled bookings are excluded unless you ask for them via status=CANCELLED.
| Param | Type | Required | Description |
|---|---|---|---|
date | string | No | YYYY-MM-DD (defaults to today) |
status | string | No | Filter: PENDING, CONFIRMED, SEATED, COMPLETED, NO_SHOW, CANCELLED |
service | string | No | Service code (LUNCH, DINNER, …) |
q | string | No | Search name / phone / email / reference (min 2 chars) |
search_all_dates | boolean | No | Set 1 with q to search across all dates |
page | integer | No | Page number (default 1) |
per_page | integer | No | 1–50 (default 30) |
restaurant_id | integer | Master key only | Restaurant ID |
{ "success": true, "date": "2026-04-05", "bookings": [{ "booking_id": 123, "reference": "RB-...", "name": "John Smith", "phone": "+44...", "covers": 4, "time": "19:00", "status": "CONFIRMED", "service": "DINNER", "tables": ["T5"], "notes": "Window seat", "extras_count": 0, ... }], "pagination": { "page": 1, "per_page": 30, "total": 17, "total_pages": 1 } }
Full detail for a single booking — covers, tables, tags, voucher redemptions, extras, customer stats (VIP / blocked / total visits) and timestamps.
| Param | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Booking ID |
restaurant_id | integer | Master key only | Restaurant ID (query param) |
{ "success": true, "booking": { "booking_id": 123, "reference": "RB-...", "status": "CONFIRMED", "date": "2026-04-05", "time": "19:00", "covers": 4, "customer": { "customer_id": 42, "name": "John Smith", "phone": "+44...", "email": "...", "is_vip": false, "total_bookings": 5, "total_no_shows": 0 }, "tables": [{ "table_id": 7, "name": "T5", "zone": "Window" }], "tags": ["BIRTHDAY"], "notes": "...", "internal_notes": "...", "vouchers": [...], "extras": [...] } }
Look up a customer's upcoming bookings by phone number. Only returns bookings matching the phone — no data leakage.
| Param | Type | Required | Description |
|---|---|---|---|
phone | string | Yes | Customer phone number |
restaurant_id | integer | Master key only | Restaurant ID |
{ "success": true, "bookings": [{ "booking_id": 123, "booking_reference": "RB-...", "date": "2026-04-05", "time": "19:00", "covers": 4, "status": "CONFIRMED", "name": "John Smith", "editable": true, "editable_reason": null, "cancellable": true, "cancellable_reason": null }], "count": 1, "editing_allowed": true, "editing_cutoff_hours": 24 }
editable requires both customer editing to be enabled AND the booking to be outside the cutoff window. cancellable only requires the booking to be outside the cutoff window. The same editing_cutoff_hours applies to both.
Modify an existing booking. Requires phone match for security. Respects restaurant edit settings and cutoff.
| Field | Type | Required | Description |
|---|---|---|---|
booking_id | integer | Yes | Booking ID from lookup |
phone | string | Yes | Must match booking's customer phone |
new_date | string | No | New date YYYY-MM-DD (omit to keep same) |
new_time | string | No | New time HH:MM (omit to keep same) |
new_covers | integer | No | New party size (omit to keep same) |
notes | string | No | Updated special requests (omit to keep same) |
tags | string/array | No | Updated tags: BIRTHDAY, ANNIVERSARY, ALLERGY, HIGH_CHAIR (omit to keep same) |
restaurant_id | integer | Master key only | Restaurant ID |
{ "success": true, "message": "Booking updated successfully", "booking": { "booking_id": 123, "booking_reference": "RB-...", "date": "2026-04-06", "time": "20:00", "covers": 4, "status": "CONFIRMED" } }
403 — Editing not allowed or within cutoff period404 — Booking not found or phone doesn't match400 — Time conflict with another bookingChange a booking's date, time, covers, notes, tags, or assigned tables. Acts on behalf of the restaurant — no phone match, no cutoff window. Use this from POS, EPOS, CRM or any other restaurant-side integration. (The self-service /api/v1/bookings/modify is the customer-facing equivalent with phone-match + cutoff.)
| Field | Type | Required | Description |
|---|---|---|---|
date | string | No | New YYYY-MM-DD |
time | string | No | New HH:MM |
covers | integer | No | New party size |
notes | string | No | Replace customer-facing notes |
internal_notes | string | No | Replace staff-only notes |
tags | array | No | Replace tag list |
table_ids | array | No | Replace table assignment (use GET /api/v1/tables/available for options) |
restaurant_id | integer | Master key only | Restaurant ID |
404 — Booking not found409 — OVERLAP_WARNING when the new time/tables conflict with another bookingCancel an existing booking. Requires phone match for security. Respects the restaurant's customer edit/cancel cutoff (see editing_cutoff_hours from /bookings/lookup) — cancellations inside that window are rejected with 403.
| Field | Type | Required | Description |
|---|---|---|---|
booking_id | integer | Yes | Booking ID from lookup |
phone | string | Yes | Must match booking's customer phone |
restaurant_id | integer | Master key only | Restaurant ID |
{ "success": true, "message": "Booking cancelled successfully", "booking_reference": "RB-..." }
403 — Within the cutoff window; customer must speak to the restaurant directly404 — Booking not found or phone mismatchChange a booking's status — e.g. SEATED when the host seats the guest, COMPLETED when the bill is closed, NO_SHOW after a grace window.
| Field | Type | Required | Description |
|---|---|---|---|
status | string | Yes | One of: PENDING, CONFIRMED, SEATED, COMPLETED, NO_SHOW, CANCELLED |
restaurant_id | integer | Master key only | Restaurant ID |
{ "success": true, "booking": { "booking_id": 123, "reference": "RB-...", "status": "SEATED" }, "message": "Status updated to SEATED" }
Same as POST /api/v1/vouchers/redeem, but the redemption is automatically linked to the booking at :id — convenient when the POS already knows which check the voucher applies to.
| Field | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Voucher code |
amount_pence | integer | Yes | Amount to redeem, in pence |
note | string | No | Free-text note |
restaurant_id | integer | Master key only | Restaurant ID |
Search customers by name, email or phone. Useful when a POS wants to surface "is this a returning guest?" before opening a tab.
| Param | Type | Required | Description |
|---|---|---|---|
q | string | Yes | Search term (min 2 chars). Matches name / email / phone. |
page | integer | No | Page number (default 1) |
per_page | integer | No | 1–50 (default 20) |
restaurant_id | integer | Master key only | Restaurant ID |
{ "success": true, "customers": [{ "customer_id": 42, "name": "John Smith", "email": "...", "phone": "+44...", "total_bookings": 5, "total_no_shows": 0, "last_visit": "2026-03-12", "is_vip": false, "blocked": false }], "pagination": { ... } }
Full customer record plus recent booking history (last 50 bookings most-recent first). Use this to show "this is their Nth visit" pop-ups in the POS.
| Param | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Customer ID |
restaurant_id | integer | Master key only | Restaurant ID (query param) |
{ "success": true, "customer": { "customer_id": 42, "first_name": "John", "last_name": "Smith", "email": "...", "phone": "+44...", "total_bookings": 5, "total_no_shows": 0, "is_vip": false, "blocked": false }, "bookings": [{ "booking_id": 123, "reference": "RB-...", "date": "2026-03-12", "time": "19:00", "covers": 4, "status": "COMPLETED" }] }
Returns every active table with an is_occupied flag for the requested time window and an is_suggested flag for the best-fit suggestion. Used to power POS floor plans.
| Param | Type | Required | Description |
|---|---|---|---|
covers | integer | No | Party size (default 2) |
date | string | No | YYYY-MM-DD (default today) |
time | string | No | HH:MM (default now) |
restaurant_id | integer | Master key only | Restaurant ID |
{ "success": true, "tables": [{ "table_id": 7, "name": "T5", "zone": "Window", "min_covers": 2, "max_covers": 4, "is_occupied": false, "is_suggested": true, "fits_party": true }], "suggested_ids": [7] }
Look up a voucher by exact code. Returns value, remaining balance, expiry, and a convenience can_redeem flag (active & not expired & positive balance). Always returns 200 with found: false when the code doesn't exist — no 404 — so POS can show a friendly "voucher not found" message.
| Param | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Voucher code (case-insensitive) |
restaurant_id | integer | Master key only | Restaurant ID |
{ "success": true, "found": true, "voucher": { "voucher_id": 88, "code": "TF-ABC123", "status": "ACTIVE", "value_pence": 10000, "balance_pence": 6000, "expires_at": "2027-04-05 23:59:59", "is_expired": false, "recipient_name": "Alex", "product_name": "Gift £100", "can_redeem": true } }
Redeem (partially or fully) against a voucher's remaining balance. Standalone — pass booking_reference if you want the redemption linked to a specific booking.
| Field | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Voucher code |
amount_pence | integer | Yes | Amount to redeem, in pence. Can't exceed remaining balance. |
booking_reference | string | No | Booking to link the redemption to (e.g. RB-20260405-A1B2C3) |
note | string | No | Free-text note attached to the redemption |
restaurant_id | integer | Master key only | Restaurant ID |
{ "success": true, "redemption_id": 555, "new_balance_pence": 3000, "fully_redeemed": false, "voucher": { "voucher_id": 88, "code": "TF-ABC123", "balance_pence": 3000, "status": "ACTIVE" }, "message": "Redeemed £30.00" }
Get restaurant info and services for a given date.
| Param | Type | Required | Description |
|---|---|---|---|
date | string | No | Date in YYYY-MM-DD (defaults to today) |
restaurant_id | integer | Master key only | Restaurant ID |
{ "success": true, "restaurant": { "name": "Viva Brazil", "phone": "0330...", "email": "...", "max_online_covers": 8 }, "date": "2026-04-05", "services": [{ "name": "Lunch", "code": "LUNCH", "start_time": "12:00:00", "end_time": "16:00:00" }, ...] }
Submit a callback request (typically from an AI phone agent). Sends an email to the restaurant and a push notification to staff.
| Param | Type | Required | Description |
|---|---|---|---|
phone | string | Recommended | Caller's phone number |
first_name | string | No | Caller's first name |
last_name | string | No | Caller's last name |
reason | string | No | Reason for callback (e.g. "menu enquiry") |
notes | string | No | Additional notes from the caller |
restaurant_id | integer | Master key only | Restaurant ID |
{ "success": true, "message": "Callback request sent to restaurant" }
Point any of these at /developers/openapi.json to get an interactive sandbox or generated client SDK:
Questions, missing fields, or want to integrate something we haven't published yet? Email hello@tablefox.co.uk — we actively support POS, EPOS, ticketing and CRM integrations.