{"openapi":"3.1.0","info":{"title":"TableNest Public API","version":"1.0.0","description":"REST API for venue booking management. Enables AI assistants and third-party integrations to read bookings, customers, availability, and venue info, create bookings, and receive webhook events.","contact":{"email":"support@tablenest.app"}},"servers":[{"url":"https://tablenest.app/api/v1","description":"Production"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key in format: tn_live_<venue_slug>_<random>"}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}},"required":["code","message"]}}},"Pagination":{"type":"object","properties":{"cursor":{"type":"string","nullable":true},"has_more":{"type":"boolean"},"limit":{"type":"integer"}}},"Booking":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"booking_date":{"type":"string","format":"date"},"start_time":{"type":"string","example":"19:00"},"end_time":{"type":"string","example":"20:30"},"party_size":{"type":"integer"},"status":{"type":"string","enum":["pending","confirmed","seated","completed","left_message","sent_sms","pending_payment","cancelled","no_show"]},"source":{"type":"string","enum":["widget","dashboard","walk_in","phone","google","import","api"]},"customer_name":{"type":"string"},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"company_name":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true},"table_id":{"type":"string","format":"uuid","nullable":true},"area_id":{"type":"string","format":"uuid","nullable":true},"service_id":{"type":"string","format":"uuid","nullable":true},"event_id":{"type":"string","format":"uuid","nullable":true},"customer_id":{"type":"string","format":"uuid","nullable":true},"created_at":{"type":"string","format":"date-time"},"cancelled_at":{"type":"string","format":"date-time","nullable":true}}},"Customer":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"customer_name":{"type":"string"},"company_name":{"type":"string","nullable":true},"vip":{"type":"boolean"},"booking_restriction":{"type":"string","nullable":true},"no_show_count":{"type":"integer"},"total_bookings":{"type":"integer"},"last_visit":{"type":"string","format":"date","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"TimeSlot":{"type":"object","properties":{"time":{"type":"string","example":"19:00"},"available_tables":{"type":"integer"}}}}},"paths":{"/venue":{"get":{"summary":"Get venue profile","description":"Returns venue name, hours, areas with tables, services, and settings. All times are in the venue's local timezone.","operationId":"getVenue","tags":["Venue"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Venue profile with opening hours, areas, and settings"},"401":{"description":"Invalid or missing API key"},"403":{"description":"API access not enabled for this venue's plan"}}}},"/services":{"get":{"summary":"List venue services","description":"Returns active services (e.g. Lunch, Dinner) with their per-day schedules.","operationId":"listServices","tags":["Venue"],"responses":{"200":{"description":"List of services with schedules"}}}},"/events":{"get":{"summary":"List upcoming events","description":"Returns active future events with booked/remaining capacity.","operationId":"listEvents","tags":["Venue"],"responses":{"200":{"description":"List of events with capacity info"}}}},"/availability":{"get":{"summary":"Check availability","description":"Returns available time slots for a given date and party size.","operationId":"checkAvailability","tags":["Availability"],"parameters":[{"name":"date","in":"query","required":true,"schema":{"type":"string","format":"date"},"description":"Date to check (YYYY-MM-DD)"},{"name":"party_size","in":"query","schema":{"type":"integer","default":2},"description":"Number of guests"},{"name":"area_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filter by area"},{"name":"service_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filter by service"}],"responses":{"200":{"description":"Available time slots"}}}},"/bookings":{"get":{"summary":"List bookings","description":"Returns bookings with optional date range and status filters. Cursor-based pagination.","operationId":"listBookings","tags":["Bookings"],"parameters":[{"name":"date_from","in":"query","schema":{"type":"string","format":"date"}},{"name":"date_to","in":"query","schema":{"type":"string","format":"date"}},{"name":"status","in":"query","schema":{"type":"string","enum":["pending","confirmed","seated","completed","left_message","sent_sms","pending_payment","cancelled","no_show"]}},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":100}}],"responses":{"200":{"description":"Paginated list of bookings"}}},"post":{"summary":"Create a booking","description":"Creates a new booking with atomic availability check and auto table assignment. Supports idempotency via Idempotency-Key header.","operationId":"createBooking","tags":["Bookings"],"parameters":[{"name":"Idempotency-Key","in":"header","schema":{"type":"string"},"description":"Unique key to prevent duplicate bookings (24hr TTL)"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["date","time","party_size","customer_name"],"properties":{"date":{"type":"string","format":"date","description":"Booking date (YYYY-MM-DD)"},"time":{"type":"string","description":"Start time (HH:MM)","example":"19:00"},"party_size":{"type":"integer","minimum":1,"maximum":200},"customer_name":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"company_name":{"type":"string"},"notes":{"type":"string"},"sms_consent":{"type":"boolean","default":false},"area_id":{"type":"string","format":"uuid"},"service_id":{"type":"string","format":"uuid"},"event_id":{"type":"string","format":"uuid"},"policy_agreed":{"type":"boolean","default":false,"description":"REQUIRED (must be true) when event_id refers to an event with an enabled, non-empty policy. Rejected with 400 if that applies and this is not true."},"terms_agreed":{"type":"boolean","default":false,"description":"REQUIRED (must be true) when the service (or, absent a service override, the venue) has terms and conditions set. Rejected with 400 if that applies and this is not true. Read the text to show the guest from GET /services, which returns effective_terms_and_conditions per service (already resolved: the service override, else the venue default, else null). A booking with no service_id is gated on the venue default, which that field also reports for every active service."}}}}}},"responses":{"201":{"description":"Booking created successfully"},"400":{"description":"Validation error"},"409":{"description":"No availability"},"429":{"description":"Rate limited"}}}},"/bookings/{id}":{"get":{"summary":"Get booking details","operationId":"getBooking","tags":["Bookings"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Booking details"},"404":{"description":"Not found"}}},"patch":{"summary":"Update booking status","description":"Transition booking status. Valid transitions: pending → confirmed/cancelled, confirmed → cancelled/seated/no_show, left_message → confirmed/cancelled/no_show, sent_sms → confirmed/cancelled/left_message, pending_payment → confirmed/cancelled, seated → completed. An invalid transition returns a 400 with the current status and its allowed targets.","operationId":"updateBooking","tags":["Bookings"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["confirmed","cancelled","seated","no_show","left_message","completed"]}}}}}},"responses":{"200":{"description":"Updated booking"},"400":{"description":"Invalid transition"}}}},"/customers":{"get":{"summary":"List customers","description":"Search and paginate through venue customers.","operationId":"listCustomers","tags":["Customers"],"parameters":[{"name":"search","in":"query","schema":{"type":"string"},"description":"Search by name, email, phone, or company"},{"name":"cursor","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":100}}],"responses":{"200":{"description":"Paginated list of customers"}}},"post":{"summary":"Create or update customer","description":"Creates a new customer or updates existing (matched by email).","operationId":"upsertCustomer","tags":["Customers"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["customer_name"],"properties":{"customer_name":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"company_name":{"type":"string"},"vip":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated existing customer"},"201":{"description":"Created new customer"}}}},"/customers/{id}":{"get":{"summary":"Get customer details","description":"Returns customer profile with last 20 bookings.","operationId":"getCustomer","tags":["Customers"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Customer with booking history"},"404":{"description":"Not found"}}}},"/webhooks":{"get":{"summary":"List webhooks","operationId":"listWebhooks","tags":["Webhooks"],"responses":{"200":{"description":"List of registered webhooks"}}},"post":{"summary":"Register a webhook","description":"Register an HTTPS endpoint to receive event notifications. Returns a signing secret for HMAC-SHA256 signature verification.","operationId":"createWebhook","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","format":"uri","description":"HTTPS endpoint URL"},"events":{"type":"array","items":{"type":"string","enum":["booking.created","booking.updated","booking.cancelled","customer.created","customer.updated"]}}}}}}},"responses":{"201":{"description":"Webhook created with signing secret"}}}},"/webhooks/{id}":{"get":{"summary":"Get webhook delivery log","description":"Returns last 50 delivery attempts for this webhook.","operationId":"getWebhookDeliveries","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Delivery log entries"}}},"delete":{"summary":"Delete a webhook","operationId":"deleteWebhook","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Webhook deleted"}}},"post":{"summary":"Test a webhook","description":"Sends a sample booking.created event to verify the endpoint works.","operationId":"testWebhook","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Test delivery result"}}}}}}