{"slug":"restaurant-table-management-system","title":"How I Built a Full Restaurant Table Management System on AvailEngine","excerpt":"I built a complete restaurant management system — floor plan, table assignments, walk-in management, waitlist, turn-time tracking, and staff booking — entirely on top of AvailEngine's booking API. Here's how the resource model maps to tables, how I handle walk-ins vs reservations, and how the waitlist feeds into real-time availability.","date":"July 10, 2026","category":"Tutorial","categoryColor":"bg-blue-50 text-blue-700","keywords":"restaurant table management system, booking API restaurant, table reservation software, restaurant waitlist API","content_html":"<pre>A friend owns a 60-seat restaurant in Plaka — you know the type: white tablecloths, great wine list, 30-minute wait on a Tuesday night, and a stack of Post-it notes behind the host stand that constitutes the \"reservation system.\" I told him I could do better.\n\n### Modelling the Floor Plan\n\nAvailEngine's resource model is the foundation. Every table in the restaurant is a **resource**:\n\n- A 2-top window table with capacity 2\n- A 4-top near the kitchen with capacity 4\n- A 6-top private corner booth with capacity 6\n- A chef's counter with 4 bar seats (1 resource per seat)\n\nEach table gets a `name` (e.g. \"Window A\"), a `max_capacity`, and a `metadata` field that stores the x/y position on the floor plan for the visual layout. The availability endpoint then handles all the logic:\n\n```\n// Every table is a resource in AvailEngine\nconst tables = [\n  { id: 'table-window-a', name: 'Window A', capacity: 2, x: 10, y: 20 },\n  { id: 'table-window-b', name: 'Window B', capacity: 2, x: 10, y: 50 },\n  { id: 'table-center-1', name: 'Center 1', capacity: 4, x: 50, y: 30 },\n  { id: 'table-booth',   name: 'Corner Booth', capacity: 6, x: 90, y: 15 },\n  { id: 'table-bar-1',   name: 'Bar Seat 1', capacity: 1, x: 70, y: 80 },\n]\n\n// Fetch which tables are free for a 2-hour dinner slot\nconst { available_tables } = await fetch(\n  `https://api.availengine.com/v1/availability/${restaurantId}\n    ?date=2026-07-10&resource_ids=${ids}&duration=120`\n).then(r => r.json())\n```\n\n### Reservations vs Walk-Ins\n\nThe system handles both. Online reservations go through the standard booking endpoint — a guest picks a date, party size, and time, and AvailEngine checks which tables of sufficient capacity are free. A 4-top reservation might get \"Center 1,\" but if a 6-top is available, the system can assign that instead for a more comfortable experience.\n\nWalk-ins are managed through a **different flow**. When a party walks in without a reservation, the host uses the staff dashboard to create a booking via the `POST /v1/manage/bookings/staff` endpoint — this creates a **confirmed** booking instantly with no deposit required. The table is marked occupied on the floor plan and the turn-time clock starts ticking.\n\n```\n// Staff creates a walk-in booking (no deposit needed)\nconst booking = await fetch(\n  'https://api.availengine.com/v1/manage/bookings/staff',\n  {\n    method: 'POST',\n    headers: { 'Authorization': `Bearer ${apiKey}` },\n    body: JSON.stringify({\n      resource_id: 'table-center-1',\n      start_time: '2026-07-10T20:00:00Z',\n      end_time: '2026-07-10T22:00:00Z',\n      party_size: 3,\n      customer: { name: 'Walk-in — Elias' },\n      notes: 'Birthday dinner',\n    })\n  }\n)\n```\n\n### The Waitlist Integration\n\nWhen all tables are full, walk-ins can join the **digital waitlist**. I used AvailEngine's native waitlist endpoint — when a table becomes available (a party pays and leaves, triggering a status update), the system automatically checks if anyone on the waitlist fits the freed table's capacity and sends an SMS:\n\n```\n// Guest joins the waitlist\nPOST /v1/waitlist/{restaurantId}\n{ \"customer_name\": \"Elias\", \"party_size\": 3, \"phone\": \"+30 69X XXXXXXX\" }\n\n// Staff checks waitlist when a table frees up\nGET /v1/manage/waitlist?party_size=3&status=waiting\n\n// Or seat them directly -> then remove from waitlist\nPOST /v1/manage/bookings/staff { ... }\nDELETE /v1/manage/waitlist/{entry_id}\n```\n\n### Turn-Time Tracking & Table Turns\n\nRestaurants make money on table turns. I built a real-time display that shows each table's status: **free**, **occupied (15 min)**, **occupied (45 min — getting close)**, **overdue**. The check-in time is recorded when the staff creates the booking, and the expected turn time (90 minutes for a standard dinner, 120 for large parties) drives a colour-coded floor plan:\n\n- **Green** — table is free, ready for next seating\n- **Blue** — occupied, within expected turn time\n- **Amber** — approaching turn-time limit (waiter should check on them)\n- **Red** — overdue, host needs to manage the queue\n\nThe status updates come from the booking lifecycle. When a customer checks in, the booking transitions to `checked_in`. When they pay and leave, the staff marks it `completed`. Each transition fires a webhook, which I use to update the floor plan display in real time.\n\n### The Staff Booking Flow for VIPs\n\nRegular customers who call ahead don't go through the public booking form. The host creates their reservation through the **staff booking endpoint**, which bypasses deposits and creates a confirmed booking immediately. This also lets the host assign specific tables — something the public booking flow doesn't expose:\n\n```\n// VIP booking — host picks the exact table\nPATCH /v1/manage/bookings/{id}\n{ \"resource_id\": \"table-booth\" }\n```\n\n### The Full Picture\n\nWhat started as \"get rid of the Post-it notes\" turned into a complete restaurant operations system:\n\n- A **live floor plan** — drag-and-drop table assignment, colour-coded by status\n- **Online reservations** — OpenTable-style booking through a public widget\n- **Digital waitlist** — no more clipboards, SMS notifications when a table frees up\n- **Turn-time analytics** — average turn time per table, per server, per shift\n- **Staff booking** — phone reservations, VIP table holds, no deposit bypass\n- **Webhook-driven display** — the floor plan updates in real time as bookings transition\n\nEvery single one of these features is built on AvailEngine endpoints. The resource model handles table capacity, the booking lifecycle handles seating states, the waitlist handles overflow, and Stripe deposits handle no-shows. I wrote about 400 lines of front-end code and zero backend code.\n\nYou can get the same result in an afternoon. Grab an API key and start mapping your restaurant's floor plan to resources. The tables are already there — you just need to plug them in.</pre>"}