Tables & pricing
The floorplan model: tables belong to a floor and (optionally) a
seating area. You can block tables for a window, combine them for
larger parties, let smart tables auto-optimise assignment, and attach
price lists that set a default deposit/min-spend per table. Every endpoint is
under /api/my-venues/:venueId/… and is operator (client role,
venue-scoped) - send an Authorization: Bearer token from the
Authentication flow. All prices are integer
pence.
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /api/my-venues/:venueId/tables |
List tables | operator |
| GET | /api/my-venues/:venueId/tables/:id |
Get a table | operator |
| POST | /api/my-venues/:venueId/tables |
Create a table | operator |
| PUT | /api/my-venues/:venueId/tables/:id |
Update a table | operator |
| DELETE | /api/my-venues/:venueId/tables/:id |
Delete a table | operator |
| POST | /api/my-venues/:venueId/tables/set-type |
Bulk set table type | operator |
| POST | /api/my-venues/:venueId/tables/set-area |
Bulk assign seating area | operator |
| GET | /api/my-venues/:venueId/tables/blocks |
List table blocks | operator |
| POST | /api/my-venues/:venueId/tables/block |
Block tables for a window | operator |
| PATCH | /api/my-venues/:venueId/tables/blocks/:id |
Edit a block | operator |
| DELETE | /api/my-venues/:venueId/tables/blocks/:id |
Delete a block | operator |
| POST | /api/my-venues/:venueId/tables/unblock |
Clear all blocks for tables | operator |
| GET | /api/my-venues/:venueId/tables/combos |
List combinations | operator |
| POST | /api/my-venues/:venueId/tables/combos |
Create a combination | operator |
| PATCH | /api/my-venues/:venueId/tables/combos/:id |
Override/reset a combination’s capacity | operator |
| DELETE | /api/my-venues/:venueId/tables/combos/:id |
Delete a combination | operator |
| GET | /api/my-venues/:venueId/floors |
List floors | operator |
| GET | /api/my-venues/:venueId/floors/:id |
Get a floor + its tables | operator |
| POST | /api/my-venues/:venueId/floors |
Create a floor | operator |
| PUT | /api/my-venues/:venueId/floors/:id |
Update a floor (canvas) | operator |
| PUT | /api/my-venues/:venueId/floors/:id/default |
Set the default floor | operator |
| DELETE | /api/my-venues/:venueId/floors/:id |
Delete a floor | operator |
| GET | /api/my-venues/:venueId/seating-areas |
List seating areas | operator |
| POST | /api/my-venues/:venueId/seating-areas |
Create a seating area | operator |
| PUT | /api/my-venues/:venueId/seating-areas/reorder |
Reorder seating areas | operator |
| PUT | /api/my-venues/:venueId/seating-areas/:id |
Rename a seating area / set its group | operator |
| DELETE | /api/my-venues/:venueId/seating-areas/:id |
Delete a seating area | operator |
| GET | /api/my-venues/:venueId/seating-area-groups |
List seating area groups | operator |
| POST | /api/my-venues/:venueId/seating-area-groups |
Create a group | operator |
| PUT | /api/my-venues/:venueId/seating-area-groups/reorder |
Reorder groups | operator |
| PUT | /api/my-venues/:venueId/seating-area-groups/:id |
Rename a group | operator |
| DELETE | /api/my-venues/:venueId/seating-area-groups/:id |
Delete a group | operator |
| GET | /api/my-venues/:venueId/smart-tables |
Get smart-table settings | operator |
| PUT | /api/my-venues/:venueId/smart-tables |
Update smart-table settings | operator |
| POST | /api/my-venues/:venueId/smart-tables/reassign |
Run smart reassignment now | operator |
| GET | /api/my-venues/:venueId/pricelists |
List price lists | operator |
| POST | /api/my-venues/:venueId/pricelists |
Create a price list | operator |
| PUT | /api/my-venues/:venueId/pricelists/:id |
Update a price list | operator |
| DELETE | /api/my-venues/:venueId/pricelists/:id |
Delete a price list | operator |
| PUT | /api/my-venues/:venueId/pricelists/:id/tables |
Bulk set/clear price for selected tables | operator |
| PUT | /api/my-venues/:venueId/pricelists/:id/tables/:tableId |
Per-table price override | operator |
| POST | /api/my-venues/:venueId/tables/:id/dirty |
Mark a table dirty, or clear the flag | operator |
| POST | /api/my-venues/:venueId/integrations/:provider/backfill |
Pull historic till sales into a date range | operator |
| POST | /api/my-venues/:venueId/integrations/:provider/reconcile |
Re-match till sales to tables over a date range | operator |
Create a table
Section titled “Create a table”POST /api/my-venues/:venueId/tables takes table_number (required), capacity
(default 4) and status (default active). Position, shape and type are
synced through the floor canvas (below) or set in bulk with set-type /
set-area.
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/tables" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"table_number":"12","capacity":6,"status":"active"}'const res = await fetch("https://thesidedoor.co/api/my-venues/VENUE_ID/tables", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ table_number: "12", capacity: 6, status: "active" }),});const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/tables");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "table_number" => "12", "capacity" => 6, "status" => "active", ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "id": "tbl_12" }Update with PUT /api/my-venues/:venueId/tables/:id (table_number, capacity,
status) and remove with DELETE /api/my-venues/:venueId/tables/:id. GET /api/my-venues/:venueId/tables accepts ?date=&time=&duration= to decorate each
row with is_blocked / is_taken. Add exclude_booking_id to leave one
reservation out of the is_taken check (handy when re-seating an existing
booking, so its own table doesn’t read as taken).
Create a floor
Section titled “Create a floor”POST /api/my-venues/:venueId/floors takes name and optional sort_order.
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/floors" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Ground Floor","sort_order":0}'const res = await fetch("https://thesidedoor.co/api/my-venues/VENUE_ID/floors", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Ground Floor", sort_order: 0 }),});const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/floors");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(["name" => "Ground Floor", "sort_order" => 0]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "id": "flr_1" }curl -X PUT "https://thesidedoor.co/api/my-venues/VENUE_ID/floors/flr_1" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Ground Floor","canvas_data":[{"type":"table","tableNumber":"12","tableType":"booth","minCovers":2,"maxCovers":6,"shape":"rect","size":"large","x":120,"y":80}]}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/floors/flr_1", { method: "PUT", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Ground Floor", canvas_data: [ { type: "table", tableNumber: "12", tableType: "booth", minCovers: 2, maxCovers: 6, shape: "rect", size: "large", x: 120, y: 80, }, ], }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/floors/flr_1");curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "name" => "Ground Floor", "canvas_data" => [[ "type" => "table", "tableNumber" => "12", "tableType" => "booth", "minCovers" => 2, "maxCovers" => 6, "shape" => "rect", "size" => "large", "x" => 120, "y" => 80, ]], ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true }PUT /api/my-venues/:venueId/floors/:id/default marks that floor as the venue’s
default room. A venue has at most one default, so this clears the flag on every
other floor and sets it on this one in a single step. No body is needed.
Seating areas
Section titled “Seating areas”POST /api/my-venues/:venueId/seating-areas creates a named area (name, plus
an optional group_id); PUT renames it (name) and/or moves it into a group
(group_id - send null or "" to un-group), and DELETE detaches its tables
then removes it. Assign tables
to an area in bulk with POST /api/my-venues/:venueId/tables/set-area
(table_ids, seating_area_id - send null to detach), and set their type with
POST /api/my-venues/:venueId/tables/set-type (table_ids, table_type one of
standard, low, high, booth, bar).
To save a custom order, send the areas in the order you want with PUT /api/my-venues/:venueId/seating-areas/reorder and a body of
{ "area_ids": ["area_1", "area_2", ...] }. Each area is stored at its position
in the list, and that order is what the reservations grid shows. Only areas that
belong to this venue are touched.
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/seating-areas" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Garden"}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/seating-areas", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Garden" }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/seating-areas");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(["name" => "Garden"]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "id": "area_1", "name": "Garden" }Seating area groups
Section titled “Seating area groups”A seating area group is a guest-facing name for one or more seating areas (for example Inside = High tables + Low tables). Guests pick the group and are seated in any of its areas; staff views keep the real area. Each area belongs to at most one group.
GET /api/my-venues/:venueId/seating-area-groupsreturns{ "data": [{ "id", "name", "sort_order" }] }in display order. Membership lives on the area: read each area’sgroup_idfromGET .../seating-areas.POST .../seating-area-groupswith{ "name": "Inside" }creates a group and returns{ "success": true, "id", "name", "sort_order" }(201). Names are trimmed and capped at 120 characters.PUT .../seating-area-groups/:idwith{ "name": "..." }renames it.PUT .../seating-area-groups/reorderwith{ "group_ids": [...] }saves the order guests see.DELETE .../seating-area-groups/:idun-groups its areas and removes its"group:<id>"entry from every table profile and access ruleguest_pick_area_idslist at the venue.
To add an area to a group, PUT /api/my-venues/:venueId/seating-areas/:areaId
with { "group_id": "GROUP_ID" }. A group_id that isn’t a group of this venue
is rejected with 400. To offer a group in “guest picks area” mode, add
"group:GROUP_ID" to the profile’s or rule’s guest_pick_area_ids.
Block tables
Section titled “Block tables”POST /api/my-venues/:venueId/tables/block reserves one or more tables for a
window. table_ids, from and to are required; reason defaults to “Venue
Reserved”. Edit a block with PATCH .../tables/blocks/:id (reason,
blocked_from, blocked_to), delete one with DELETE .../tables/blocks/:id, or
clear every block on given tables with POST .../tables/unblock (table_ids).
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/tables/block" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"table_ids":["tbl_12","tbl_13"],"from":"2026-07-04T18:00:00","to":"2026-07-04T23:00:00","reason":"Private event"}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/tables/block", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ table_ids: ["tbl_12", "tbl_13"], from: "2026-07-04T18:00:00", to: "2026-07-04T23:00:00", reason: "Private event", }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/tables/block");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "table_ids" => ["tbl_12", "tbl_13"], "from" => "2026-07-04T18:00:00", "to" => "2026-07-04T23:00:00", "reason" => "Private event", ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "ids": ["blk_1", "blk_2"], "rule_id": null }Recurring blocks
Section titled “Recurring blocks”To block the same tables across several dates in one call, send a windows
array of { "from": ..., "to": ... } objects instead of a single top-level
from/to. Each window becomes its own block, and every block in the batch
shares one rule_id so the set can be managed as a series (a single window
leaves rule_id as null). Up to 500 occurrences are allowed per call, so set
an end date on open-ended patterns.
{ "table_ids": ["tbl_12"], "windows": [ { "from": "2026-07-04T18:00:00", "to": "2026-07-04T23:00:00" }, { "from": "2026-07-11T18:00:00", "to": "2026-07-11T23:00:00" } ], "reason": "Weekly private hire"}When a block belongs to a series, add ?scope=series to DELETE .../tables/blocks/:id to remove every occurrence sharing its rule_id, or send
{ "scope": "series", "reason": "..." } to PATCH .../tables/blocks/:id to
relabel the whole series in one go (start and end stay per-occurrence). Without
scope=series, both verbs act on the single occurrence only.
Combine tables
Section titled “Combine tables”POST /api/my-venues/:venueId/tables/combos joins two or more tables into one
bookable unit; combined_capacity is computed from the members. Remove one with
DELETE .../tables/combos/:id.
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/tables/combos" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Booth A+B","table_ids":["tbl_12","tbl_13"]}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/tables/combos", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Booth A+B", table_ids: ["tbl_12", "tbl_13"] }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/tables/combos");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "name" => "Booth A+B", "table_ids" => ["tbl_12", "tbl_13"], ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "id": "cmb_1", "combined_capacity": 8 }By default a combination’s capacity is the sum of its member tables. To set it by
hand, send PATCH /api/my-venues/:venueId/tables/combos/:id with
{ "combined_capacity": 10 }. Pass null (or leave it blank) to reset the
capacity back to the auto sum. The response echoes the value in effect and the
auto sum it would fall back to:
{ "success": true, "combined_capacity": 10, "auto_capacity": 8, "override": true }Smart tables
Section titled “Smart tables”GET /api/my-venues/:venueId/smart-tables returns the auto-assignment settings;
PUT updates them. reassign_enabled runs a daily pass that moves unlocked
reservations to optimal tables at reassign_time; duration_enabled extends the
hold for larger parties. reassign_last_run is cron-managed and read-only.
Three optional fields tune the smart-duration extension (all accepted on PUT
and returned on GET):
duration_per_cover_mins- extra minutes added to the hold per cover once the party reaches the large-party threshold (default10).duration_large_party_threshold- the party size at which the extension kicks in (default5).duration_max_total_mins- an absolute cap on the total hold in minutes. Sendnull(or leave it blank) for no cap.
curl -X PUT "https://thesidedoor.co/api/my-venues/VENUE_ID/smart-tables" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"reassign_enabled":true,"reassign_time":"06:30","duration_enabled":true}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/smart-tables", { method: "PUT", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ reassign_enabled: true, reassign_time: "06:30", duration_enabled: true, }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/smart-tables");curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "reassign_enabled" => true, "reassign_time" => "06:30", "duration_enabled" => true, ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "data": { "reassign_enabled": true, "reassign_time": "06:30", "reassign_last_run": null, "duration_enabled": true, "duration_per_cover_mins": 10, "duration_large_party_threshold": 5, "duration_max_total_mins": null }}Run smart reassignment now
Section titled “Run smart reassignment now”POST /api/my-venues/:venueId/smart-tables/reassign runs the same optimisation
as the daily pass on demand, for one venue and one day. It defaults to today; add
?date=YYYY-MM-DD (or send { "date": "..." } in the body) to run it for the day
you’re viewing. The response reports how many reservations were moved, plus the
detail of each move and anything it could not place:
{ "success": true, "date": "2026-07-04", "reassigned": 2, "scanned": 9, "moves": [], "unresolved": []}Price lists
Section titled “Price lists”A price list sets a default_price_pence (the deposit/min-spend) that applies to
every table, plus optional per-table overrides. POST /api/my-venues/:venueId/pricelists creates one (name required); PUT updates
name/default; DELETE clears its overrides and detaches it from any access rules.
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/pricelists" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"Weekend","default_price_pence":5000}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/pricelists", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "Weekend", default_price_pence: 5000 }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/pricelists");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "name" => "Weekend", "default_price_pence" => 5000, ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "id": "pl_weekend" }Per-table price override
Section titled “Per-table price override”PUT /api/my-venues/:venueId/pricelists/:id/tables/:tableId sets a table’s price
within a list. Send price_pence to set it, or null (or "") to clear the
override so the list default applies again.
curl -X PUT "https://thesidedoor.co/api/my-venues/VENUE_ID/pricelists/pl_weekend/tables/tbl_12" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"price_pence":7500}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/pricelists/pl_weekend/tables/tbl_12", { method: "PUT", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ price_pence: 7500 }), },);const data = await res.json();$ch = curl_init( "https://thesidedoor.co/api/my-venues/VENUE_ID/pricelists/pl_weekend/tables/tbl_12");curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(["price_pence" => 7500]),]);$data = json_decode(curl_exec($ch), true);{ "success": true }Set prices for several tables at once
Section titled “Set prices for several tables at once”PUT /api/my-venues/:venueId/pricelists/:id/tables (note: no :tableId) applies
one price across many tables in a single call. Send table_ids and a
price_pence to set, or price_pence as null (or "") to clear the override
on each so the list default applies again. Only tables that belong to the venue
are updated; the response reports how many were changed and how many were skipped.
curl -X PUT "https://thesidedoor.co/api/my-venues/VENUE_ID/pricelists/pl_weekend/tables" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"table_ids":["tbl_12","tbl_13"],"price_pence":7500}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/pricelists/pl_weekend/tables", { method: "PUT", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ table_ids: ["tbl_12", "tbl_13"], price_pence: 7500 }), },);const data = await res.json();$ch = curl_init( "https://thesidedoor.co/api/my-venues/VENUE_ID/pricelists/pl_weekend/tables");curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => "PUT", CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode([ "table_ids" => ["tbl_12", "tbl_13"], "price_pence" => 7500, ]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "changed": 2, "cleared": false, "skipped": 0 }Mark a table dirty
Section titled “Mark a table dirty”POST /api/my-venues/:venueId/tables/:id/dirty sets or clears a table’s
“needs clearing” flag, shown on the reservations floorplan so staff know it
isn’t ready for the next party yet.
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/tables/tbl_12/dirty" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"dirty":true}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/tables/tbl_12/dirty", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ dirty: true }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/tables/tbl_12/dirty");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(["dirty" => true]),]);$data = json_decode(curl_exec($ch), true);{ "success": true, "dirty": true }Send {"dirty": false} to clear the flag once the table’s ready again.
Till integrations
Section titled “Till integrations”If the venue has a till integration connected (for
example Alliance iPos), these endpoints pull its sales into Sidedoor and
match them to tables. :provider is the integration’s id, e.g. alliance.
Backfill historic sales
Section titled “Backfill historic sales”POST .../integrations/:provider/backfill pulls till sales for a date range
the automatic poll wouldn’t otherwise reach - useful right after connecting a
till, or to fill a gap after an outage. Send a from and to date (inclusive,
capped at 31 days); the same auto-matching the live poll uses then runs over
that range.
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/integrations/alliance/backfill" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"from":"2026-06-01","to":"2026-06-30"}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/integrations/alliance/backfill", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ from: "2026-06-01", to: "2026-06-30" }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/integrations/alliance/backfill");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(["from" => "2026-06-01", "to" => "2026-06-30"]),]);$data = json_decode(curl_exec($ch), true);{ "ok": true, "days": 30, "fetched": 412, "inserted": 390, "updated": 22, "errors": 0, "attached": 180 }Reconcile sales to tables
Section titled “Reconcile sales to tables”POST .../integrations/:provider/reconcile re-matches till sales to tables
over a date range (also capped at 31 days), correcting mismatches as well as
attaching new sales. It defaults to a dry run - it returns the plan
without changing anything - so send "dryRun": false once you’re happy to
apply it.
curl -X POST "https://thesidedoor.co/api/my-venues/VENUE_ID/integrations/alliance/reconcile" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"from":"2026-06-01","to":"2026-06-07","dryRun":false}'const res = await fetch( "https://thesidedoor.co/api/my-venues/VENUE_ID/integrations/alliance/reconcile", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ from: "2026-06-01", to: "2026-06-07", dryRun: false }), },);const data = await res.json();$ch = curl_init("https://thesidedoor.co/api/my-venues/VENUE_ID/integrations/alliance/reconcile");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_POSTFIELDS => json_encode(["from" => "2026-06-01", "to" => "2026-06-07", "dryRun" => false]),]);$data = json_decode(curl_exec($ch), true);A dry run returns the plan without writing anything; passing dryRun: false
applies it.