{
  "openapi": "3.1.0",
  "info": {
    "title": "WaitQ API",
    "version": "1.0.0",
    "summary": "Public REST API for WaitQ businesses: lists, guests, bookings, customers and webhooks.",
    "description": "The WaitQ API lets a business connect its own website, software or automations (such as Zapier) to WaitQ: read its locations and lists, add guests to waitlists and guest lists, create bookings and pick-up orders, look up customers and guests, and get webhooks when something changes.\n\n## Quick start\n\n1. In WaitQ, go to **Integrations → API and Webhooks** and create an API key. Integrations are included in the Business plan and with an Access Pass.\n2. Check the key with `GET /me`, which returns the business it belongs to:\n\n```bash\ncurl https://waitq.app/api/v1/me \\\n  -H \"Authorization: Bearer wq_live_…\"\n```\n\n3. List your locations with `GET /locations` and your lists with `GET /waitlists`, `GET /booking-lists`, `GET /order-lists` or `GET /guest-lists`. Each list's `/fields` endpoint describes what its form asks for, so you know what to send when you create a record.\n\n## Base URL\n\n`https://waitq.app/api/v1`. Every request and response body is JSON (`Content-Type: application/json`).\n\n## Authentication\n\nEvery endpoint requires an API key. Business administrators create and revoke keys in WaitQ under [Integrations → API and Webhooks](/integrations/api). A key is shown once, when it's created. Use a separate named key for each integration, so you can revoke one without affecting the others. Keys created for Zapier are listed in the Zapier setup instead, and work the same way. Revoking a key stops every request and webhook subscription that uses it. A key looks like `wq_live_` followed by 43 URL-safe base64 characters (`[A-Za-z0-9_-]`). Send it in either header:\n\n- `Authorization: Bearer wq_live_…`\n- `X-API-Key: wq_live_…`\n\nIf both are sent, the `Authorization: Bearer` value is used. A missing, malformed, unknown or revoked key answers `401 unauthorized`.\n\nThe key's business must have integrations in its plan: a **Business plan or an active Access Pass** (during an Access Pass's post-event phase only a paid Business plan keeps them). Otherwise every endpoint answers `403 plan_required`. Some endpoints additionally need a plan module (reservations for booking endpoints, guest lists for guest list endpoints) and answer `403 forbidden` without it.\n\n## Idempotency\n\nThe four create endpoints accept an optional `Idempotency-Key` header (at most 255 characters), scoped to the API key:\n\n- The first request with a key runs; a later request with the same key and the same path and body (compared after validation and normalisation, e.g. trimmed strings and lowercased emails) returns the stored response with the header `Idempotent-Replayed: true` and is not counted against the create limits.\n- The same key with a different request answers `422 idempotency_conflict`.\n- While the first request is still running, a retry answers `409 conflict` with `Retry-After: 10`.\n- If the first request did not finish, or ended in a server error, after 2 minutes the key answers `409 conflict` with `error.details.idempotency = \"outcome_unknown\"`: check whether the record was created before retrying with a new key.\n- A request rejected with a 4xx status frees its key, so a corrected request can reuse it. Only successful (2xx) responses are stored and replayed.\n- A key answers for 24 hours from its first request, or for a shorter window sent in `Idempotency-Window` (whole seconds; values under 600 are raised to 600, values over 86400 are capped). A non-numeric `Idempotency-Window` answers `400 invalid_request`.\n\nWithout the header a create simply runs.\n\n## Errors\n\nEvery error, including unknown paths, has the same body:\n\n```json\n{ \"error\": { \"code\": \"invalid_request\", \"message\": \"waitlist_id: Invalid UUID\", \"details\": [ { \"path\": \"waitlist_id\", \"message\": \"Invalid UUID\" } ] } }\n```\n\n`details` is present only on some errors. Codes and their HTTP status:\n\n| code | status |\n| --- | --- |\n| `invalid_request` | 400 |\n| `unauthorized` | 401 |\n| `plan_required` | 403 |\n| `forbidden` | 403 |\n| `not_found` | 404 |\n| `conflict` | 409 |\n| `unavailable` | 409 (503 when `POST /hooks` cannot look up a `target_url` right now) |\n| `idempotency_conflict` | 422 |\n| `rate_limited` | 429 |\n| `internal_error` | 500 |\n\nValidation errors (`400`) name the first problem in `message` as `<field>: <problem>` and list every problem in `details` as `[{ path, message }]`. Request bodies reject unknown properties. A body that is not valid JSON answers `400` with `Body must be valid JSON`.\n\nAny path under `/api/v1` that is not documented here answers `404 not_found` with the message `Unknown endpoint: <METHOD> <path>`, in the same error shape and without authentication. Calling a documented path with a method it does not support is answered by the hosting framework (HTTP 405), not in the API's error shape.\n\n## Responses\n\nEvery API response carries `Cache-Control: private, no-store`. Responses can include fields that aren't documented here: ignore them, and don't build on them. List endpoints wrap their items in `{ \"data\": [...] }` and return all matching items in one response: there is no pagination. Searches return at most 10 items.\n\nRecords created through the API are attributed to the API in WaitQ; requests whose `User-Agent` starts with `Zapier` are labelled as Zapier. Guests, orders and bookings created through the API are not sent join, order-ready or confirmation messages.\n\nTo read a record's current state, use `GET /bookings/{id}`, `GET /waitlist-entries/{id}`, `GET /orders/{id}` or `GET /guest-list-entries/{id}`. An idempotent replay of a create returns its original response, not the record's current state.\n\n## Webhooks\n\n`POST /hooks` subscribes a URL to one event (REST hooks, as Zapier uses them), optionally for one location. The events are:\n\n- `waitlist_entry.created` and `waitlist_entry.status_changed`, for queue visits and pick-up orders\n- `booking.created` and `booking.confirmed`\n- `guest_list_entry.created` and `guest_list_entry.status_changed`\n- `customer.created`\n\n`booking.created` is sent for every new booking, including pending ones. `booking.confirmed` is sent when a booking is created confirmed or becomes confirmed later.\n\nEach delivery is signed with the subscription's `signing_secret` and retried for about 8.5 hours if your endpoint doesn't answer with a 2xx. Payloads, headers, signature verification and retry rules are documented under **Webhooks**. `GET /events/{type}/samples` returns recent records in the shape of an event, for testing.\n\n## Waitlists and orders\n\nUse **Waitlists** for queue visits (`GET /waitlists?kind=waitlist`, `POST /waitlist-entries`) and **Orders** for pick-up orders (`GET /order-lists`, `POST /orders`). Orders have their own statuses: `waiting`, `ready`, `picked_up`, `cancelled`, `not_picked_up` and `archived`.\n\nQueue visits and orders share the `waitlist_entry.*` webhook events. Check `list_kind` (`waitlist` or `pickup_orders`) to tell them apart. In these events, orders use the queue statuses: `notified` (ready), `seated` (picked up) and `no_show` (not picked up).\n\n## Services\n\nBookings, waitlist entries, orders and guest list entries list their selected services in `services` (ID, name, duration, price and currency) and `service_ids`. `service_id` and `service_name` are filled in only when exactly one service is selected. For emails, use `selected_services`: all selected service names in one text, separated by commas, and empty when there are none.\n\nTo create a booking on a list that uses services, send `service_ids`. Availability and the booking's length follow the services' combined duration, and `GET /booking-lists/{id}/availability` accepts the same `service_ids`. Services must be active, assigned to the list, unique, within the list's selection limit and at most eight hours combined.\n\n## Prices and totals\n\n- **Reservation total:** to show what a booking, waitlist entry, order or guest list entry costs, use `reservation_total_formatted` (for example `45.00 EUR`) with `reservation_total_status`. WaitQ captures it when the record is created: the sum of the selected services' prices, once per service, not per person, with no tax or discounts added. It doesn't change when the catalog or the business currency changes later; changing a record's services captures a new total.\n- **No partial totals:** if a selected price isn't set, or the currencies differ, there is no total (`not_calculated`). `0.00` with status `complete` means free. Records created before totals were captured have no total.\n- **Amounts:** `reservation_total_minor` is in the currency's minor units (2 decimals for EUR, 0 for JPY, 3 for KWD).\n- **Service prices:** each service's `price_cents` is its current price. For what the customer was quoted, use `reservation_total_*`.\n\n## Public pages\n\nCustomers use these WaitQ pages: the waitlist form at `/join/{display_token}` and their place in line at `/w/{entry_token}`, the order form at `/order/{display_token}` and order tracking at `/o/{entry_token}`, and the booking form at `/reserve/{public_token}` and their booking at `/r/{booking_token}`. Order lists include `public_form_path`, a link to their public form. Order tracking links are private to the customer and aren't returned by the Orders endpoints.\n\n## Rate limits\n\n- **Per key:** 120 requests per minute (all endpoints).\n- **Failed authentication:** after 30 invalid or revoked keys within a minute from one IP address, further invalid keys from that address get `429 rate_limited` instead of `401`. Valid keys from the same address keep working.\n- **Creates per business:** `POST /waitlist-entries`, `POST /orders`, `POST /bookings` and `POST /guest-list-entries` are also limited per business across all of its keys: 60 per minute and 5,000 per UTC day (fixed windows: a UTC minute, a UTC day). Over either limit the answer is `429 rate_limited` with `error.details.limit` set to `business_creates_per_minute` or `business_creates_per_day` and `error.details.max` to the limit. Creates rejected with a 4xx status and idempotent replays are not counted; creates that fail with a server error stay counted.\n\nEvery `429` carries a `Retry-After` header (seconds).",
    "contact": {
      "name": "WaitQ",
      "url": "https://waitq.app"
    }
  },
  "servers": [
    {
      "url": "https://waitq.app/api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    },
    {
      "apiKeyHeader": []
    }
  ],
  "tags": [
    {
      "name": "Account",
      "description": "The API key's business and its locations."
    },
    {
      "name": "Waitlists",
      "description": "Add guests to a queue for a service or a place. For pick-up orders, use **Orders**.\n\n1. Read `GET /waitlists?kind=waitlist` and choose a list.\n2. Read `GET /waitlists/{id}/fields` for that list's required contact fields, party size rules and custom fields.\n3. Use the **Walk-in party** request example on `POST /waitlist-entries`, then read `GET /waitlist-entries/{id}` for current state.\n\nUse `ticket_number` / `display_number` for the guest's ticket. Status labels include Waiting, Called and Checked in: `notified` means called and `seated` means checked in. Creating an entry through the API doesn't notify the guest.\n\nQueue visits and pick-up orders share the `waitlist_entry.*` webhook events. Check `list_kind` to tell them apart."
    },
    {
      "name": "Orders",
      "description": "Pick-up orders that customers collect when they're ready.\n\n1. Choose a list from `GET /order-lists`.\n2. Read `GET /order-lists/{id}/fields` for required fields and whether order numbers are set by hand or automatically.\n3. Create with `POST /orders` using `order_list_id`, then read `GET /orders/{id}`.\n\nOrders use the statuses `waiting`, `ready`, `picked_up`, `cancelled`, `not_picked_up` and `archived`, and their `list_kind` is `pickup_orders`. Creating an order through the API doesn't notify the customer.\n\nOrder events are sent as `waitlist_entry.*` webhooks with `list_kind: pickup_orders`, using the queue statuses `notified` (ready), `seated` (picked up) and `no_show` (not picked up)."
    },
    {
      "name": "Bookings",
      "description": "Open times and booking creation."
    },
    {
      "name": "Guest lists",
      "description": "Add and find guests on guest lists."
    },
    {
      "name": "Customers",
      "description": "Customer lookup."
    },
    {
      "name": "Webhooks",
      "description": "REST hook subscriptions, sample payloads and webhook events."
    }
  ],
  "paths": {
    "/me": {
      "get": {
        "operationId": "getMe",
        "tags": [
          "Account"
        ],
        "summary": "Get the API key's business",
        "description": "Returns the business the API key belongs to and the key's own details. Zapier uses it to test the connection and label the account.",
        "responses": {
          "200": {
            "description": "The key's business and key details.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                },
                "example": {
                  "business": {
                    "id": "6f1d2c9a-3b4e-4f5a-8c7d-0e1f2a3b4c5d",
                    "name": "Casa do Porto Bistro"
                  },
                  "api_key": {
                    "id": "0b7e4a12-9c3d-4e8f-a1b2-c3d4e5f60718",
                    "name": "Zapier",
                    "prefix": "wq_live_Xk3v9Q"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/locations": {
      "get": {
        "operationId": "listLocations",
        "tags": [
          "Account"
        ],
        "summary": "List locations",
        "description": "All of the business's locations, ordered by name.",
        "responses": {
          "200": {
            "description": "The business's locations.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Location"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                      "name": "Harbour Street",
                      "timezone": "Atlantic/Azores"
                    },
                    {
                      "id": "b4d6f8a0-2c3e-4f70-9b1d-3f5a7b9d1f2c",
                      "name": "Old Town",
                      "timezone": "Europe/Lisbon"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/waitlists": {
      "get": {
        "operationId": "listWaitlists",
        "tags": [
          "Waitlists"
        ],
        "summary": "List waitlists and pick-up order lists",
        "description": "The business's lists, ordered by name, optionally filtered by location and `kind`. Use `kind=waitlist` for queues; for pick-up order lists, use `GET /order-lists`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/LocationFilter"
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "description": "Use `waitlist` to list queues only.",
            "schema": {
              "type": "string",
              "enum": [
                "waitlist",
                "pickup_orders"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Waitlists.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WaitlistSummary"
                      }
                    }
                  }
                },
                "examples": {
                  "all_lists": {
                    "summary": "Both list kinds (actual shared response)",
                    "value": {
                      "data": [
                        {
                          "id": "c1e3a5b7-9d0f-4a2c-8e4a-6c8e0a2c4e6f",
                          "name": "Dinner walk-ins",
                          "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                          "kind": "waitlist",
                          "order_numbers": null,
                          "accepts_api_creates": true
                        },
                        {
                          "id": "d2f4b6c8-0e1a-4b3d-9f5b-7d9f1b3d5f70",
                          "name": "Takeaway orders",
                          "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                          "kind": "pickup_orders",
                          "order_numbers": "manual",
                          "accepts_api_creates": true
                        }
                      ]
                    }
                  },
                  "waitlist": {
                    "summary": "Waitlist (business with one queue)",
                    "value": {
                      "data": [
                        {
                          "id": "c1e3a5b7-9d0f-4a2c-8e4a-6c8e0a2c4e6f",
                          "name": "Dinner walk-ins",
                          "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                          "kind": "waitlist",
                          "order_numbers": null,
                          "accepts_api_creates": true
                        }
                      ]
                    }
                  },
                  "pickup": {
                    "summary": "Pick-up orders (business with one collection list)",
                    "value": {
                      "data": [
                        {
                          "id": "d2f4b6c8-0e1a-4b3d-9f5b-7d9f1b3d5f70",
                          "name": "Takeaway orders",
                          "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                          "kind": "pickup_orders",
                          "order_numbers": "manual",
                          "accepts_api_creates": true
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "bad_location": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "location_id must be a UUID"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/waitlists/{id}/fields": {
      "get": {
        "operationId": "getWaitlistFields",
        "tags": [
          "Waitlists"
        ],
        "summary": "Describe a waitlist or pick-up list form",
        "description": "The fields `POST /waitlist-entries` fills for this list, as staff see them: which standard fields the list asks for and requires, and its active custom fields. A pick-up list with manual order numbers lists `order_number` first.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Waitlist id. A value that is not a UUID answers `404`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The list's form.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WaitlistFields"
                    }
                  }
                },
                "examples": {
                  "waitlist": {
                    "summary": "Waitlist form",
                    "value": {
                      "data": {
                        "kind": "waitlist",
                        "order_numbers": null,
                        "standard_fields": [
                          {
                            "key": "customer_name",
                            "label": "Name",
                            "required": true
                          },
                          {
                            "key": "phone",
                            "label": "Phone",
                            "required": false
                          },
                          {
                            "key": "email",
                            "label": "Email",
                            "required": false
                          },
                          {
                            "key": "party_size",
                            "label": "Party Size",
                            "required": true,
                            "min": 1,
                            "max": 12
                          },
                          {
                            "key": "marketing_consent",
                            "label": "Marketing Consent",
                            "required": false,
                            "consent_text": "Send me news and offers."
                          }
                        ],
                        "custom_fields": [
                          {
                            "id": "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71",
                            "label": "Seating preference",
                            "help_text": null,
                            "type": "single_select",
                            "required": false,
                            "staff_only": false,
                            "options": [
                              {
                                "id": "7d05c7e9-1b3d-4f5a-8c8e-0a2c4e6a8c93",
                                "label": "Terrace"
                              },
                              {
                                "id": "8e16d8fa-2c4e-4a6b-9d9f-1b3d5f7b9da4",
                                "label": "Indoors"
                              }
                            ]
                          },
                          {
                            "id": "6cf4b6d8-0a2c-4e4f-9b7d-9f1b3d5f7b82",
                            "label": "High chair needed",
                            "help_text": "For guests with small children",
                            "type": "checkbox",
                            "required": false,
                            "staff_only": true
                          }
                        ]
                      }
                    }
                  },
                  "pickup": {
                    "summary": "Manual pick-up order form",
                    "value": {
                      "data": {
                        "kind": "pickup_orders",
                        "order_numbers": "manual",
                        "standard_fields": [
                          {
                            "key": "order_number",
                            "label": "Order Number",
                            "required": true,
                            "max_length": 6
                          },
                          {
                            "key": "customer_name",
                            "label": "Name",
                            "required": false
                          }
                        ],
                        "custom_fields": []
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No such waitlist for this business.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Waitlist not found"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/booking-lists": {
      "get": {
        "operationId": "listBookingLists",
        "tags": [
          "Bookings"
        ],
        "summary": "List booking lists",
        "description": "The business's booking lists (not deleted), ordered by name, optionally for one location. Does not check the reservations module.",
        "parameters": [
          {
            "$ref": "#/components/parameters/LocationFilter"
          }
        ],
        "responses": {
          "200": {
            "description": "Booking lists.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BookingListSummary"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "e3a5c7d9-1f2b-4c4e-8a6c-8e0a2c4e6a81",
                      "name": "Table reservations",
                      "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                      "accepts_api_creates": true
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "bad_location": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "location_id must be a UUID"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/booking-lists/{id}/fields": {
      "get": {
        "operationId": "getBookingListFields",
        "tags": [
          "Bookings"
        ],
        "summary": "Describe a booking list's form",
        "description": "The fields `POST /bookings` fills for this list: standard fields (name, phone, email, party size, notes, marketing consent) the list asks for, and its active custom fields. Also returns service_selection with the active catalog, prices and maximum number of services.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Booking list id. A value that is not a UUID answers `404`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The list's form.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ListFields"
                        },
                        {
                          "type": "object",
                          "required": [
                            "service_selection"
                          ],
                          "properties": {
                            "service_selection": {
                              "$ref": "#/components/schemas/BookingServiceSelection"
                            }
                          }
                        }
                      ]
                    }
                  }
                },
                "example": {
                  "data": {
                    "standard_fields": [
                      {
                        "key": "customer_name",
                        "label": "Name",
                        "required": true
                      },
                      {
                        "key": "phone",
                        "label": "Phone",
                        "required": true
                      },
                      {
                        "key": "email",
                        "label": "Email",
                        "required": false
                      },
                      {
                        "key": "party_size",
                        "label": "Party Size",
                        "required": true,
                        "min": 1,
                        "max": 8
                      },
                      {
                        "key": "notes",
                        "label": "Notes",
                        "required": false
                      }
                    ],
                    "custom_fields": [],
                    "service_selection": {
                      "enabled": false,
                      "max_selections": 1,
                      "options": []
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The plan does not include integrations (`plan_required`) or reservations (`forbidden`).",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "plan_required": {
                    "value": {
                      "error": {
                        "code": "plan_required",
                        "message": "Integrations require a WaitQ Business plan."
                      }
                    }
                  },
                  "forbidden": {
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "message": "Reservations are not included in the current plan."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such booking list for this business.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Booking list not found"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/booking-lists/{id}/availability": {
      "get": {
        "operationId": "getBookingAvailability",
        "tags": [
          "Bookings"
        ],
        "summary": "List open booking times",
        "description": "Open start times on a booking list for a party size, over 1-7 days, under the same rules `POST /bookings` uses, so every listed time can be booked (until it fills up). Empty query values are treated as absent. For service lists, pass the same service_ids as creation: open times use their combined duration.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Booking list id. A value that is not a UUID answers `404`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "date",
            "in": "query",
            "required": false,
            "description": "First day (`YYYY-MM-DD`, a real calendar date). Defaults to today at the location.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-10-30"
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "Number of days from `date`.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 7,
              "default": 1
            }
          },
          {
            "name": "party_size",
            "in": "query",
            "required": false,
            "description": "Whole number 1-500, then within the list's range. Defaults to 1; always 1 when the list does not ask for a party size.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500
            }
          },
          {
            "name": "service_ids",
            "in": "query",
            "required": false,
            "description": "Selected service UUIDs, comma-separated or repeated. Required for service-enabled lists. Uses the same selection limits and duration as creation.",
            "style": "form",
            "explode": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "format": "uuid"
              },
              "maxItems": 100,
              "description": "Selected service IDs. Required when services are enabled; choose active services assigned to the list, without duplicates, within its selection cap and eight hours total. Omit for lists without services."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Open slots.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Availability"
                },
                "example": {
                  "data": [
                    {
                      "starts_at": "2026-10-30T19:00:00.000Z",
                      "ends_at": "2026-10-30T20:30:00.000Z",
                      "local_date": "2026-10-30",
                      "local_time": "19:00",
                      "party_size": 2,
                      "spots_open": 5,
                      "remaining_capacity": 11
                    },
                    {
                      "starts_at": "2026-10-30T19:30:00.000Z",
                      "ends_at": "2026-10-30T21:00:00.000Z",
                      "local_date": "2026-10-30",
                      "local_time": "19:30",
                      "party_size": 2,
                      "spots_open": 3,
                      "remaining_capacity": 7
                    }
                  ],
                  "booking_list_id": "e3a5c7d9-1f2b-4c4e-8a6c-8e0a2c4e6a81",
                  "date": "2026-10-30",
                  "days": 1,
                  "party_size": 2,
                  "timezone": "Europe/London"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query, or a party size outside the list's range.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "bad_date": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "date: Use YYYY-MM-DD",
                        "details": [
                          {
                            "path": "date",
                            "message": "Use YYYY-MM-DD"
                          }
                        ]
                      }
                    }
                  },
                  "party_size": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "party_size: Party size exceeds the maximum of 8 guests allowed for this list."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The plan does not include integrations (`plan_required`) or reservations (`forbidden`).",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "plan_required": {
                    "value": {
                      "error": {
                        "code": "plan_required",
                        "message": "Integrations require a WaitQ Business plan."
                      }
                    }
                  },
                  "forbidden": {
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "message": "Reservations are not included in the current plan."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such booking list for this business.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Booking list not found"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The selected time or capacity is unavailable.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {}
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/guest-lists": {
      "get": {
        "operationId": "listGuestLists",
        "tags": [
          "Guest lists"
        ],
        "summary": "List guest lists",
        "description": "The business's guest lists (not deleted): undated lists first, then by event start, latest first; then by name. Optionally for one location. Needs the guest lists plan module.",
        "parameters": [
          {
            "$ref": "#/components/parameters/LocationFilter"
          }
        ],
        "responses": {
          "200": {
            "description": "Guest lists.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GuestListSummary"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "f4b6d8e0-2a3c-4d5f-9b7d-9f1b3d5f7b92",
                      "name": "Friday Jazz Night",
                      "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                      "event_starts_at": "2026-10-30T21:00:00+00:00",
                      "event_ends_at": "2026-10-31T02:00:00+00:00",
                      "capacity": 120,
                      "timezone": "Atlantic/Azores",
                      "accepts_api_creates": true
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "bad_location": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "location_id must be a UUID"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The plan does not include integrations (`plan_required`) or guest lists (`forbidden`).",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "plan_required": {
                    "value": {
                      "error": {
                        "code": "plan_required",
                        "message": "Integrations require a WaitQ Business plan."
                      }
                    }
                  },
                  "forbidden": {
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "message": "Guest lists are not included in the current plan."
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/guest-lists/{id}/fields": {
      "get": {
        "operationId": "getGuestListFields",
        "tags": [
          "Guest lists"
        ],
        "summary": "Describe a guest list's form",
        "description": "The fields `POST /guest-list-entries` fills for this list. `customer_name` is always listed and required.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Guest list id. A value that is not a UUID answers `404`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The list's form.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ListFields"
                    }
                  }
                },
                "example": {
                  "data": {
                    "standard_fields": [
                      {
                        "key": "customer_name",
                        "label": "Name",
                        "required": true
                      },
                      {
                        "key": "phone",
                        "label": "Phone",
                        "required": false
                      },
                      {
                        "key": "email",
                        "label": "Email",
                        "required": false
                      },
                      {
                        "key": "party_size",
                        "label": "Party Size",
                        "required": false,
                        "min": 1,
                        "max": 50
                      },
                      {
                        "key": "category",
                        "label": "Category",
                        "required": false,
                        "max_length": 40
                      },
                      {
                        "key": "tags",
                        "label": "Tags",
                        "required": false,
                        "max_items": 20,
                        "max_length": 40
                      },
                      {
                        "key": "notes",
                        "label": "Notes",
                        "required": false
                      }
                    ],
                    "custom_fields": []
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The plan does not include integrations (`plan_required`) or guest lists (`forbidden`).",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "plan_required": {
                    "value": {
                      "error": {
                        "code": "plan_required",
                        "message": "Integrations require a WaitQ Business plan."
                      }
                    }
                  },
                  "forbidden": {
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "message": "Guest lists are not included in the current plan."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such guest list for this business.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Guest list not found"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/waitlist-entries": {
      "post": {
        "operationId": "createWaitlistEntry",
        "tags": [
          "Waitlists"
        ],
        "summary": "Add a waitlist guest or a pick-up order",
        "description": "Adds a guest (or a pick-up order) on behalf of the business, as staff would, using the list's own field rules, party size range, duplicate rule (one active place per phone and per email), plan visit limit and ticket numbering. The guest is not notified. Counts against the business create limits. Returns the created entry (`previous_status` is null); in the unlikely case the record cannot be read back, the body is only `{ \"id\": \"…\" }`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/IdempotencyWindow"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWaitlistEntryRequest"
              },
              "examples": {
                "waitlist": {
                  "summary": "Walk-in party",
                  "value": {
                    "waitlist_id": "c1e3a5b7-9d0f-4a2c-8e4a-6c8e0a2c4e6f",
                    "customer_name": "Marta Silveira",
                    "phone": "+351912000111",
                    "email": "marta.silveira@example.com",
                    "party_size": 4,
                    "custom_fields": {
                      "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71": "Terrace",
                      "6cf4b6d8-0a2c-4e4f-9b7d-9f1b3d5f7b82": true
                    }
                  }
                },
                "pickup": {
                  "summary": "Manual pick-up order",
                  "value": {
                    "waitlist_id": "d2f4b6c8-0e1a-4b3d-9f5b-7d9f1b3d5f70",
                    "order_number": "B12",
                    "customer_name": "Rui Bettencourt"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created entry.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WaitlistEntry"
                },
                "examples": {
                  "waitlist": {
                    "summary": "Waitlist guest",
                    "value": {
                      "id": "17a9c1e3-5b7d-4f9a-8c2e-4a6c8e0a2c3d",
                      "waitlist_id": "c1e3a5b7-9d0f-4a2c-8e4a-6c8e0a2c4e6f",
                      "waitlist_name": "Dinner walk-ins",
                      "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                      "location_name": "Harbour Street",
                      "status": "waiting",
                      "previous_status": null,
                      "customer_id": "4ad2f4b6-8e0a-4c2d-9f5b-7d9f1b3d5f60",
                      "customer_name": "Marta Silveira",
                      "phone": "+351912000111",
                      "email": "marta.silveira@example.com",
                      "marketing_consent": false,
                      "party_size": 4,
                      "ticket_number": 27,
                      "order_number": null,
                      "order_number_is_automatic": null,
                      "list_kind": "waitlist",
                      "status_label": "Waiting",
                      "display_number": "A27",
                      "custom_fields": [
                        {
                          "id": "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71",
                          "label": "Seating preference",
                          "type": "single_select",
                          "value": "7d05c7e9-1b3d-4f5a-8c8e-0a2c4e6a8c93",
                          "display_value": "Terrace"
                        },
                        {
                          "id": "6cf4b6d8-0a2c-4e4f-9b7d-9f1b3d5f7b82",
                          "label": "High chair needed",
                          "type": "checkbox",
                          "value": true,
                          "display_value": "Yes"
                        }
                      ],
                      "created_at": "2026-10-24T19:42:10.512Z",
                      "updated_at": "2026-10-24T19:42:10.512Z"
                    }
                  },
                  "pickup": {
                    "summary": "Pick-up order",
                    "value": {
                      "id": "28bad2f4-6c8e-40ab-9d3f-5b7d9f1a2c4e",
                      "waitlist_id": "d2f4b6c8-0e1a-4b3d-9f5b-7d9f1b3d5f70",
                      "waitlist_name": "Takeaway orders",
                      "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                      "location_name": "Harbour Street",
                      "status": "waiting",
                      "previous_status": null,
                      "customer_id": "4ad2f4b6-8e0a-4c2d-9f5b-7d9f1b3d5f60",
                      "customer_name": "Rui Bettencourt",
                      "phone": null,
                      "email": null,
                      "marketing_consent": false,
                      "party_size": null,
                      "ticket_number": 28,
                      "order_number": "B12",
                      "order_number_is_automatic": false,
                      "list_kind": "pickup_orders",
                      "status_label": "Waiting",
                      "display_number": "B12",
                      "custom_fields": [],
                      "created_at": "2026-10-24T19:42:10.512Z",
                      "updated_at": "2026-10-24T19:42:10.512Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body, a required field missing, a value outside the list's rules, or invalid custom field answers / Idempotency headers.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "validation": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "waitlist_id: Invalid UUID",
                        "details": [
                          {
                            "path": "waitlist_id",
                            "message": "Invalid UUID"
                          }
                        ]
                      }
                    }
                  },
                  "required": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "phone: This waitlist requires it"
                      }
                    }
                  },
                  "phone": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "phone: Use international format, e.g. +351912345678"
                      }
                    }
                  },
                  "order_number": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "order_number: Use up to 6 letters or digits"
                      }
                    }
                  },
                  "custom_fields": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "custom_fields: Seating preference: Choose an available option",
                        "details": [
                          {
                            "field_id": "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71",
                            "label": "Seating preference",
                            "message": "Choose an available option"
                          }
                        ]
                      }
                    }
                  },
                  "json": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "Body must be valid JSON"
                      }
                    }
                  },
                  "unknown_key": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "Unrecognized key: \"table\"",
                        "details": [
                          {
                            "path": "",
                            "message": "Unrecognized key: \"table\""
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No such waitlist for this business.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Waitlist not found"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "A conflict with existing data, an Idempotency-Key still in use or with an unknown outcome, or the list cannot take this create (`unavailable`).",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "duplicate": {
                    "value": {
                      "error": {
                        "code": "conflict",
                        "message": "This phone is already waiting on this list."
                      }
                    }
                  },
                  "order_taken": {
                    "value": {
                      "error": {
                        "code": "conflict",
                        "message": "This order number is already active on this list."
                      }
                    }
                  },
                  "services": {
                    "value": {
                      "error": {
                        "code": "unavailable",
                        "message": "This waitlist asks for services, which can't be chosen through the API yet."
                      }
                    }
                  },
                  "plan_limit": {
                    "value": {
                      "error": {
                        "code": "unavailable",
                        "message": "The monthly visit limit for this plan has been reached."
                      }
                    }
                  },
                  "full": {
                    "value": {
                      "error": {
                        "code": "unavailable",
                        "message": "This waitlist is full right now."
                      }
                    }
                  },
                  "idempotency_in_progress": {
                    "value": {
                      "error": {
                        "code": "conflict",
                        "message": "A request with this Idempotency-Key is still being processed"
                      }
                    }
                  },
                  "idempotency_outcome_unknown": {
                    "value": {
                      "error": {
                        "code": "conflict",
                        "message": "The first request with this Idempotency-Key did not finish, and it may have been created. Check before retrying with a new Idempotency-Key.",
                        "details": {
                          "idempotency": "outcome_unknown"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/CreateRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/bookings": {
      "post": {
        "operationId": "createBooking",
        "tags": [
          "Bookings"
        ],
        "summary": "Create a booking",
        "description": "Creates a confirmed booking on behalf of the business, with the same slot and capacity checks as staff bookings. No confirmation message is sent. Needs the reservations plan module. Counts against the business create limits. Returns the created booking; in the unlikely case it cannot be read back, the body is only `{ \"id\": \"…\" }`. Read current state later with GET /bookings/{id}.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/IdempotencyWindow"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateBookingRequest"
              },
              "examples": {
                "local_time": {
                  "summary": "Local time at the location",
                  "value": {
                    "booking_list_id": "e3a5c7d9-1f2b-4c4e-8a6c-8e0a2c4e6a81",
                    "starts_at": "2026-10-30 19:30",
                    "customer_name": "Tomas Arruda",
                    "phone": "+351913000222",
                    "email": "tomas.arruda@example.com",
                    "party_size": 2,
                    "notes": "Anniversary dinner",
                    "marketing_consent": true
                  }
                },
                "offset": {
                  "summary": "Instant with offset",
                  "value": {
                    "booking_list_id": "e3a5c7d9-1f2b-4c4e-8a6c-8e0a2c4e6a81",
                    "starts_at": "2026-10-30T19:30:00+00:00",
                    "customer_name": "Tomas Arruda",
                    "party_size": "2"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created booking.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Booking"
                },
                "example": {
                  "id": "28b0d2f4-6c8e-4a0b-9d3f-5b7d9f1b3d4e",
                  "booking_list_id": "e3a5c7d9-1f2b-4c4e-8a6c-8e0a2c4e6a81",
                  "booking_list_name": "Table reservations",
                  "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                  "location_name": "Harbour Street",
                  "status": "confirmed",
                  "customer_id": "4ad2f4b6-8e0a-4c2d-9f5b-7d9f1b3d5f60",
                  "customer_name": "Tomas Arruda",
                  "phone": "+351913000222",
                  "email": "tomas.arruda@example.com",
                  "marketing_consent": true,
                  "party_size": 2,
                  "starts_at": "2026-10-30T19:30:00+00:00",
                  "ends_at": "2026-10-30T21:00:00+00:00",
                  "notes": "Anniversary dinner",
                  "custom_fields": [
                    {
                      "id": "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71",
                      "label": "Seating preference",
                      "type": "single_select",
                      "value": "7d05c7e9-1b3d-4f5a-8c8e-0a2c4e6a8c93",
                      "display_value": "Terrace"
                    }
                  ],
                  "created_at": "2026-10-24T10:05:33.201Z",
                  "updated_at": "2026-10-24T10:05:33.201Z",
                  "reservation_total_minor": null,
                  "reservation_total_currency": null,
                  "reservation_total_formatted": "",
                  "reservation_total_status": "not_calculated",
                  "reservation_pricing": null,
                  "payment": {
                    "availability": "not_available"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body, a required field missing, or a value outside the list's rules.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "starts_at": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "starts_at: Use a date and time like 2026-10-23 14:30 (the location's time), or with an offset like 2026-10-23T14:30:00+01:00",
                        "details": [
                          {
                            "path": "starts_at",
                            "message": "Use a date and time like 2026-10-23 14:30 (the location's time), or with an offset like 2026-10-23T14:30:00+01:00"
                          }
                        ]
                      }
                    }
                  },
                  "required": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "customer_name: This booking list requires it"
                      }
                    }
                  },
                  "consent": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "marketing_consent: Needs a phone or email to record consent"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The plan does not include integrations (`plan_required`) or reservations (`forbidden`).",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "plan_required": {
                    "value": {
                      "error": {
                        "code": "plan_required",
                        "message": "Integrations require a WaitQ Business plan."
                      }
                    }
                  },
                  "forbidden": {
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "message": "Reservations are not included in the current plan."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such booking list for this business.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Booking list not found"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "A conflict with existing data, an Idempotency-Key still in use or with an unknown outcome, or the list cannot take this create (`unavailable`).",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "slot": {
                    "value": {
                      "error": {
                        "code": "unavailable",
                        "message": "This time is not available for that party size."
                      }
                    }
                  },
                  "idempotency_in_progress": {
                    "value": {
                      "error": {
                        "code": "conflict",
                        "message": "A request with this Idempotency-Key is still being processed"
                      }
                    }
                  },
                  "idempotency_outcome_unknown": {
                    "value": {
                      "error": {
                        "code": "conflict",
                        "message": "The first request with this Idempotency-Key did not finish, and it may have been created. Check before retrying with a new Idempotency-Key.",
                        "details": {
                          "idempotency": "outcome_unknown"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/CreateRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/guest-list-entries": {
      "post": {
        "operationId": "createGuestListEntry",
        "tags": [
          "Guest lists"
        ],
        "summary": "Add a guest to a guest list",
        "description": "Adds one guest to a guest list for a date, as staff would from the dashboard (same field rules, duplicate rule and custom field handling). Needs the guest lists plan module. Counts against the business create limits. Returns the created guest; in the unlikely case it cannot be read back, the body is only `{ \"id\": \"…\" }`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/IdempotencyWindow"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateGuestListEntryRequest"
              },
              "examples": {
                "guest": {
                  "summary": "Guest with tags",
                  "value": {
                    "guest_list_id": "f4b6d8e0-2a3c-4d5f-9b7d-9f1b3d5f7b92",
                    "visit_date": "2026-10-30",
                    "customer_name": "Ines Medeiros",
                    "phone": "+351914000333",
                    "party_size": 3,
                    "category": "VIP",
                    "tags": "press, plus-two",
                    "notes": "Arriving after 22:00"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created guest.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GuestListEntry"
                },
                "example": {
                  "id": "39c1e3a5-7d9f-4b1c-8e4a-6c8e0a2c4e5f",
                  "guest_list_id": "f4b6d8e0-2a3c-4d5f-9b7d-9f1b3d5f7b92",
                  "guest_list_name": "Friday Jazz Night",
                  "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                  "location_name": "Harbour Street",
                  "event_starts_at": "2026-10-30T21:00:00+00:00",
                  "event_ends_at": "2026-10-31T02:00:00+00:00",
                  "visit_date": "2026-10-30",
                  "status": "confirmed",
                  "previous_status": null,
                  "status_label": "Confirmed",
                  "customer_id": null,
                  "customer_name": "Ines Medeiros",
                  "phone": "+351914000333",
                  "email": null,
                  "party_size": 3,
                  "category": "VIP",
                  "tags": [
                    "press",
                    "plus-two"
                  ],
                  "notes": "Arriving after 22:00",
                  "showed_at": null,
                  "no_show_at": null,
                  "custom_fields": [],
                  "created_at": "2026-10-24T15:20:00.000Z",
                  "updated_at": "2026-10-24T15:20:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body, a required field missing, or a value outside the list's rules.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "name": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "customer_name: Invalid input: expected string, received undefined",
                        "details": [
                          {
                            "path": "customer_name",
                            "message": "Invalid input: expected string, received undefined"
                          }
                        ]
                      }
                    }
                  },
                  "visit_date": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "visit_date: Use a date between 2025-10-25 and 2028-10-24"
                      }
                    }
                  },
                  "tags": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "tags: Use up to 20 tags of up to 40 characters"
                      }
                    }
                  },
                  "party_size": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "party_size: Between 1 and 50"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Integrations or the guest lists module missing from the plan, or the business is in an Access Pass's post-event period.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "plan_required": {
                    "value": {
                      "error": {
                        "code": "plan_required",
                        "message": "Integrations require a WaitQ Business plan."
                      }
                    }
                  },
                  "forbidden": {
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "message": "Guest lists are not included in the current plan."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such guest list for this business.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Guest list not found"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "A conflict with existing data, an Idempotency-Key still in use or with an unknown outcome, or the list cannot take this create (`unavailable`).",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "duplicate": {
                    "value": {
                      "error": {
                        "code": "conflict",
                        "message": "This guest is already on the list for this day."
                      }
                    }
                  },
                  "fields_changed": {
                    "value": {
                      "error": {
                        "code": "conflict",
                        "message": "The guest list's fields just changed. Please try again."
                      }
                    }
                  },
                  "services": {
                    "value": {
                      "error": {
                        "code": "unavailable",
                        "message": "This guest list asks for services, which can't be chosen through the API yet."
                      }
                    }
                  },
                  "idempotency_in_progress": {
                    "value": {
                      "error": {
                        "code": "conflict",
                        "message": "A request with this Idempotency-Key is still being processed"
                      }
                    }
                  },
                  "idempotency_outcome_unknown": {
                    "value": {
                      "error": {
                        "code": "conflict",
                        "message": "The first request with this Idempotency-Key did not finish, and it may have been created. Check before retrying with a new Idempotency-Key.",
                        "details": {
                          "idempotency": "outcome_unknown"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/CreateRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "get": {
        "operationId": "findGuestListEntries",
        "tags": [
          "Guest lists"
        ],
        "summary": "Find guests on a guest list",
        "description": "Up to 10 guests on one list matching any of the given phone (as stored, or by its digits), email (case-insensitive) or exact name (case-insensitive, inner whitespace collapsed), latest visit date first, then newest. At least one of `phone`, `email`, `customer_name` is required. Needs the guest lists plan module.",
        "parameters": [
          {
            "name": "guest_list_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "visit_date",
            "in": "query",
            "required": false,
            "description": "Only this date: `YYYY-MM-DD`, or a date-time whose date part is used.",
            "schema": {
              "type": "string"
            },
            "example": "2026-10-30"
          },
          {
            "name": "phone",
            "in": "query",
            "required": false,
            "description": "International format; normalised (invalid answers 400).",
            "schema": {
              "type": "string"
            },
            "example": "+351914000333"
          },
          {
            "name": "email",
            "in": "query",
            "required": false,
            "description": "Trimmed and lowercased.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "customer_name",
            "in": "query",
            "required": false,
            "description": "Exact name, any capitals.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching guests (possibly empty).",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "maxItems": 10,
                      "items": {
                        "$ref": "#/components/schemas/GuestListEntry"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "39c1e3a5-7d9f-4b1c-8e4a-6c8e0a2c4e5f",
                      "guest_list_id": "f4b6d8e0-2a3c-4d5f-9b7d-9f1b3d5f7b92",
                      "guest_list_name": "Friday Jazz Night",
                      "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                      "location_name": "Harbour Street",
                      "event_starts_at": "2026-10-30T21:00:00+00:00",
                      "event_ends_at": "2026-10-31T02:00:00+00:00",
                      "visit_date": "2026-10-30",
                      "status": "confirmed",
                      "previous_status": null,
                      "status_label": "Confirmed",
                      "customer_id": null,
                      "customer_name": "Ines Medeiros",
                      "phone": "+351914000333",
                      "email": null,
                      "party_size": 3,
                      "category": "VIP",
                      "tags": [
                        "press",
                        "plus-two"
                      ],
                      "notes": "Arriving after 22:00",
                      "showed_at": null,
                      "no_show_at": null,
                      "custom_fields": [],
                      "created_at": "2026-10-24T15:20:00.000Z",
                      "updated_at": "2026-10-24T15:20:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid query parameters.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "list": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "guest_list_id must be a UUID"
                      }
                    }
                  },
                  "none": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "Provide phone, email or customer_name"
                      }
                    }
                  },
                  "visit_date": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "visit_date: Use a date like 2026-10-24"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The plan does not include integrations (`plan_required`) or guest lists (`forbidden`).",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "plan_required": {
                    "value": {
                      "error": {
                        "code": "plan_required",
                        "message": "Integrations require a WaitQ Business plan."
                      }
                    }
                  },
                  "forbidden": {
                    "value": {
                      "error": {
                        "code": "forbidden",
                        "message": "Guest lists are not included in the current plan."
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such guest list for this business.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Guest list not found"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/customers": {
      "get": {
        "operationId": "findCustomers",
        "tags": [
          "Customers"
        ],
        "summary": "Find customers by phone or email",
        "description": "Up to 10 customers (merged-away profiles excluded) with this exact phone or email, matching any phone or email the customer has used, most recently updated first. With both, both must belong to the customer found; a value no customer has yet is ignored, so a guest known only by phone is still found. At least one of `phone` or `email` is required.",
        "parameters": [
          {
            "name": "phone",
            "in": "query",
            "required": false,
            "description": "International format; normalised (invalid answers 400).",
            "schema": {
              "type": "string"
            },
            "example": "+351912000111"
          },
          {
            "name": "email",
            "in": "query",
            "required": false,
            "description": "Trimmed and lowercased.",
            "schema": {
              "type": "string"
            },
            "example": "marta.silveira@example.com"
          }
        ],
        "responses": {
          "200": {
            "description": "Matching customers (possibly empty).",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "maxItems": 10,
                      "items": {
                        "$ref": "#/components/schemas/Customer"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "4ad2f4b6-8e0a-4c2d-9f5b-7d9f1b3d5f60",
                      "name": "Marta Silveira",
                      "phone": "+351912000111",
                      "email": "marta.silveira@example.com",
                      "marketing_consent": false,
                      "created_at": "2026-03-02T12:00:00.000Z",
                      "updated_at": "2026-10-24T19:42:10.512Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Neither phone nor email given, or an invalid phone.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "none": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "Provide phone or email"
                      }
                    }
                  },
                  "phone": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "phone: Use international format, e.g. +351912345678"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/events/{type}/samples": {
      "get": {
        "operationId": "listEventSamples",
        "tags": [
          "Webhooks"
        ],
        "summary": "Get sample event payloads",
        "description": "Up to 3 recent records shaped exactly like the event's webhook payload, for previewing data while building an automation. Records of deleted lists are excluded. `*.status_changed` samples only include records whose status changed (waitlists: notified, seated, cancelled, no_show; guests: show, no_show), with the `previous_status` their timestamps suggest. In samples `event_id` is the record's id and `occurred_at` its `created_at`/`updated_at`. `booking.confirmed` samples are bookings that are confirmed now, with their creation time.",
        "parameters": [
          {
            "name": "type",
            "in": "path",
            "required": true,
            "description": "Event type. Anything else answers `404`.",
            "schema": {
              "type": "string",
              "enum": [
                "waitlist_entry.created",
                "waitlist_entry.status_changed",
                "booking.created",
                "booking.confirmed",
                "customer.created",
                "guest_list_entry.created",
                "guest_list_entry.status_changed"
              ]
            }
          },
          {
            "name": "location_id",
            "in": "query",
            "required": false,
            "description": "Only records of this location. Ignored when `list_id` is given, and for `customer.created`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "list_id",
            "in": "query",
            "required": false,
            "description": "Only records of this waitlist, booking list or guest list (the list decides the location). Ignored for `customer.created`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sample payloads, newest first (possibly empty).",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "maxItems": 3,
                      "items": {
                        "anyOf": [
                          {
                            "$ref": "#/components/schemas/WaitlistEntryEvent"
                          },
                          {
                            "$ref": "#/components/schemas/BookingEvent"
                          },
                          {
                            "$ref": "#/components/schemas/CustomerEvent"
                          },
                          {
                            "$ref": "#/components/schemas/GuestListEntryEvent"
                          }
                        ]
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "28b0d2f4-6c8e-4a0b-9d3f-5b7d9f1b3d4e",
                      "booking_list_id": "e3a5c7d9-1f2b-4c4e-8a6c-8e0a2c4e6a81",
                      "booking_list_name": "Table reservations",
                      "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                      "location_name": "Harbour Street",
                      "status": "confirmed",
                      "customer_id": "4ad2f4b6-8e0a-4c2d-9f5b-7d9f1b3d5f60",
                      "customer_name": "Tomas Arruda",
                      "phone": "+351913000222",
                      "email": "tomas.arruda@example.com",
                      "marketing_consent": true,
                      "party_size": 2,
                      "starts_at": "2026-10-30T19:30:00+00:00",
                      "ends_at": "2026-10-30T21:00:00+00:00",
                      "notes": "Anniversary dinner",
                      "custom_fields": [
                        {
                          "id": "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71",
                          "label": "Seating preference",
                          "type": "single_select",
                          "value": "7d05c7e9-1b3d-4f5a-8c8e-0a2c4e6a8c93",
                          "display_value": "Terrace"
                        }
                      ],
                      "created_at": "2026-10-24T10:05:33.201Z",
                      "updated_at": "2026-10-24T10:05:33.201Z",
                      "event_id": "28b0d2f4-6c8e-4a0b-9d3f-5b7d9f1b3d4e",
                      "event_type": "booking.created",
                      "occurred_at": "2026-10-24T10:05:33.201Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "location": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "location_id must be a UUID"
                      }
                    }
                  },
                  "list": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "list_id must be a UUID"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Unknown event type.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Unknown event type"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/hooks": {
      "post": {
        "operationId": "subscribeWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Subscribe to an event",
        "description": "Creates a REST hook subscription for this API key. Subscribing the same key to the same event, target URL and location again returns the existing active subscription with `200` (also when the business is at its cap). A business has at most 100 active subscriptions across its keys. Not idempotency-keyed and not counted against create limits.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscribeRequest"
              },
              "examples": {
                "subscribe": {
                  "value": {
                    "event": "waitlist_entry.created",
                    "target_url": "https://hooks.example.com/waitq/new-guest",
                    "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "An identical active subscription already existed for this key; it is returned.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                },
                "example": {
                  "id": "9f27e90b-3d5f-4b7c-8eaa-2c4e6a8cae05",
                  "event": "waitlist_entry.created",
                  "target_url": "https://hooks.example.com/waitq/new-guest",
                  "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                  "signing_secret": "whsec_3qL0aV2b8nW5tY7cK1mP4rS6uX9zA0dF2gH5jL8oQ1e",
                  "created_at": "2026-10-24T09:00:00.000Z"
                }
              }
            }
          },
          "201": {
            "description": "Subscription created.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                },
                "example": {
                  "id": "9f27e90b-3d5f-4b7c-8eaa-2c4e6a8cae05",
                  "event": "waitlist_entry.created",
                  "target_url": "https://hooks.example.com/waitq/new-guest",
                  "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                  "signing_secret": "whsec_3qL0aV2b8nW5tY7cK1mP4rS6uX9zA0dF2gH5jL8oQ1e",
                  "created_at": "2026-10-24T09:00:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body or target URL.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "event": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "event: Invalid option: expected one of \"waitlist_entry.created\"|\"waitlist_entry.status_changed\"|\"booking.created\"|\"booking.confirmed\"|\"customer.created\"|\"guest_list_entry.created\"|\"guest_list_entry.status_changed\"",
                        "details": [
                          {
                            "path": "event",
                            "message": "Invalid option: expected one of \"waitlist_entry.created\"|\"waitlist_entry.status_changed\"|\"booking.created\"|\"booking.confirmed\"|\"customer.created\"|\"guest_list_entry.created\"|\"guest_list_entry.status_changed\""
                          }
                        ]
                      }
                    }
                  },
                  "https": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "target_url must use https"
                      }
                    }
                  },
                  "host": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "target_url host does not exist"
                      }
                    }
                  },
                  "private": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "target_url must resolve to a public address"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "`location_id` is not one of the business's locations.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Location not found"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "The business has too many active subscriptions.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "cap": {
                    "value": {
                      "error": {
                        "code": "conflict",
                        "message": "This business has too many active subscriptions (limit 100)"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "description": "The target URL could not be looked up right now; retry later.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "lookup": {
                    "value": {
                      "error": {
                        "code": "unavailable",
                        "message": "target_url could not be looked up right now. Try again in a minute."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/hooks/{id}": {
      "delete": {
        "operationId": "unsubscribeWebhook",
        "tags": [
          "Webhooks"
        ],
        "summary": "Unsubscribe",
        "description": "Disables a subscription created by this API key and drops its queued deliveries. Idempotent: an already disabled subscription of this key answers `200` again. Subscriptions of other keys answer `404`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Subscription id. A value that is not a UUID answers `404`.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Unsubscribed.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnsubscribeResult"
                },
                "example": {
                  "id": "9f27e90b-3d5f-4b7c-8eaa-2c4e6a8cae05",
                  "deleted": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No such subscription for this API key.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Subscription not found"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/bookings/{id}": {
      "get": {
        "operationId": "getBooking",
        "tags": [
          "Bookings"
        ],
        "summary": "Get a Booking by ID",
        "description": "Reads the record's current state. Returns `404` for missing records and for records of another business. Needs the same plan module as creating one. The body is the resource directly, without a data wrapper.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current resource.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Booking"
                },
                "example": {
                  "id": "28b0d2f4-6c8e-4a0b-9d3f-5b7d9f1b3d4e",
                  "booking_list_id": "e3a5c7d9-1f2b-4c4e-8a6c-8e0a2c4e6a81",
                  "booking_list_name": "Table reservations",
                  "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                  "location_name": "Harbour Street",
                  "status": "confirmed",
                  "customer_id": "4ad2f4b6-8e0a-4c2d-9f5b-7d9f1b3d5f60",
                  "customer_name": "Tomas Arruda",
                  "phone": "+351913000222",
                  "email": "tomas.arruda@example.com",
                  "marketing_consent": true,
                  "party_size": 2,
                  "starts_at": "2026-10-30T19:30:00+00:00",
                  "ends_at": "2026-10-30T21:00:00+00:00",
                  "notes": "Anniversary dinner",
                  "custom_fields": [
                    {
                      "id": "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71",
                      "label": "Seating preference",
                      "type": "single_select",
                      "value": "7d05c7e9-1b3d-4f5a-8c8e-0a2c4e6a8c93",
                      "display_value": "Terrace"
                    }
                  ],
                  "created_at": "2026-10-24T10:05:33.201Z",
                  "updated_at": "2026-10-24T10:05:33.201Z",
                  "reservation_total_minor": null,
                  "reservation_total_currency": null,
                  "reservation_total_formatted": "",
                  "reservation_total_status": "not_calculated",
                  "reservation_pricing": null,
                  "payment": {
                    "availability": "not_available"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Record not found in this business.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/waitlist-entries/{id}": {
      "get": {
        "operationId": "getWaitlistEntry",
        "tags": [
          "Waitlists"
        ],
        "summary": "Get a waitlist entry or pick-up order",
        "description": "Reads an entry's current state. Returns `404` for missing records and for records of another business. Needs the same plan module as creating one. The body is the resource directly, without a data wrapper. Check `list_kind` and use `status_label` for the appropriate workflow: Called / Checked in for queues, Ready for pickup / Picked up for orders.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current resource.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WaitlistEntry"
                },
                "example": {
                  "id": "17a9c1e3-5b7d-4f9a-8c2e-4a6c8e0a2c3d",
                  "waitlist_id": "c1e3a5b7-9d0f-4a2c-8e4a-6c8e0a2c4e6f",
                  "waitlist_name": "Dinner walk-ins",
                  "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                  "location_name": "Harbour Street",
                  "status": "waiting",
                  "previous_status": null,
                  "customer_id": "4ad2f4b6-8e0a-4c2d-9f5b-7d9f1b3d5f60",
                  "customer_name": "Marta Silveira",
                  "phone": "+351912000111",
                  "email": "marta.silveira@example.com",
                  "marketing_consent": false,
                  "party_size": 4,
                  "ticket_number": 27,
                  "order_number": null,
                  "order_number_is_automatic": null,
                  "list_kind": "waitlist",
                  "status_label": "Waiting",
                  "display_number": "A27",
                  "custom_fields": [
                    {
                      "id": "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71",
                      "label": "Seating preference",
                      "type": "single_select",
                      "value": "7d05c7e9-1b3d-4f5a-8c8e-0a2c4e6a8c93",
                      "display_value": "Terrace"
                    },
                    {
                      "id": "6cf4b6d8-0a2c-4e4f-9b7d-9f1b3d5f7b82",
                      "label": "High chair needed",
                      "type": "checkbox",
                      "value": true,
                      "display_value": "Yes"
                    }
                  ],
                  "created_at": "2026-10-24T19:42:10.512Z",
                  "updated_at": "2026-10-24T19:42:10.512Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Record not found in this business.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/guest-list-entries/{id}": {
      "get": {
        "operationId": "getGuestListEntry",
        "tags": [
          "Guest lists"
        ],
        "summary": "Get a GuestListEntry by ID",
        "description": "Reads the record's current state. Returns `404` for missing records and for records of another business. Needs the same plan module as creating one. The body is the resource directly, without a data wrapper.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current resource.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GuestListEntry"
                },
                "example": {
                  "id": "39c1e3a5-7d9f-4b1c-8e4a-6c8e0a2c4e5f",
                  "guest_list_id": "f4b6d8e0-2a3c-4d5f-9b7d-9f1b3d5f7b92",
                  "guest_list_name": "Friday Jazz Night",
                  "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                  "location_name": "Harbour Street",
                  "event_starts_at": "2026-10-30T21:00:00+00:00",
                  "event_ends_at": "2026-10-31T02:00:00+00:00",
                  "visit_date": "2026-10-30",
                  "status": "confirmed",
                  "previous_status": null,
                  "status_label": "Confirmed",
                  "customer_id": null,
                  "customer_name": "Ines Medeiros",
                  "phone": "+351914000333",
                  "email": null,
                  "party_size": 3,
                  "category": "VIP",
                  "tags": [
                    "press",
                    "plus-two"
                  ],
                  "notes": "Arriving after 22:00",
                  "showed_at": null,
                  "no_show_at": null,
                  "custom_fields": [],
                  "created_at": "2026-10-24T15:20:00.000Z",
                  "updated_at": "2026-10-24T15:20:00.000Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Record not found in this business.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/order-lists": {
      "get": {
        "operationId": "listOrderLists",
        "tags": [
          "Orders"
        ],
        "summary": "List order lists",
        "description": "The business's non-deleted order lists, optionally filtered by location, ordered by name. Returns only kind pickup_orders.",
        "parameters": [
          {
            "$ref": "#/components/parameters/LocationFilter"
          }
        ],
        "responses": {
          "200": {
            "description": "Waitlists.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/OrderListSummary"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "d2f4b6c8-0e1a-4b3d-9f5b-7d9f1b3d5f70",
                      "name": "Takeaway orders",
                      "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                      "kind": "pickup_orders",
                      "order_numbers": "manual",
                      "accepts_api_creates": true,
                      "requires_service_selection": false,
                      "max_service_selections": 1
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameter.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "bad_location": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "location_id must be a UUID"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/order-lists/{id}/fields": {
      "get": {
        "operationId": "getOrderListFields",
        "tags": [
          "Orders"
        ],
        "summary": "Describe an order list form",
        "description": "The selected order list's actual required standard and custom fields. Returns 404 for queue lists, missing/deleted lists and lists belonging to another business.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Order list ID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The list's form.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/OrderListFields"
                    }
                  }
                },
                "example": {
                  "data": {
                    "kind": "pickup_orders",
                    "order_numbers": "manual",
                    "standard_fields": [
                      {
                        "key": "order_number",
                        "label": "Order Number",
                        "required": true,
                        "max_length": 6
                      },
                      {
                        "key": "customer_name",
                        "label": "Name",
                        "required": false
                      }
                    ],
                    "custom_fields": [],
                    "service_selection": {
                      "enabled": false,
                      "max_selections": 1,
                      "options": []
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No such waitlist for this business.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "not_found": {
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "Waitlist not found"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/orders": {
      "post": {
        "operationId": "createOrder",
        "tags": [
          "Orders"
        ],
        "summary": "Create an order",
        "description": "Creates an order in an order list, with the list's own validation, order numbering and prices, and the same create and idempotency limits as the other create endpoints. Queue lists answer `404` and nothing is created. The customer isn't notified. Lists with services need `service_ids` from `GET /order-lists/{id}/fields`; the order captures their total when it's created. Idempotency keys apply across endpoints: reusing a key on another path answers `422`. New orders are sent as `waitlist_entry.created` webhooks.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/IdempotencyWindow"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOrderRequest"
              },
              "examples": {
                "order": {
                  "summary": "Manual pick-up order",
                  "value": {
                    "order_number": "B12",
                    "customer_name": "Rui Bettencourt",
                    "order_list_id": "d2f4b6c8-0e1a-4b3d-9f5b-7d9f1b3d5f70"
                  }
                },
                "services": {
                  "summary": "Order with two selected services",
                  "value": {
                    "order_list_id": "d2f4b6c8-0e1a-4b3d-9f5b-7d9f1b3d5f70",
                    "customer_name": "Marta Silveira",
                    "order_number": "B13",
                    "service_ids": [
                      "00000000-0000-4000-8000-000000000001",
                      "00000000-0000-4000-8000-000000000002"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The created entry.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/IdempotentReplayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                },
                "example": {
                  "id": "28bad2f4-6c8e-40ab-9d3f-5b7d9f1a2c4e",
                  "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                  "location_name": "Harbour Street",
                  "status": "waiting",
                  "previous_status": null,
                  "customer_id": "4ad2f4b6-8e0a-4c2d-9f5b-7d9f1b3d5f60",
                  "customer_name": "Rui Bettencourt",
                  "phone": null,
                  "email": null,
                  "marketing_consent": false,
                  "party_size": null,
                  "order_number": "B12",
                  "order_number_is_automatic": false,
                  "list_kind": "pickup_orders",
                  "status_label": "Waiting",
                  "display_number": "B12",
                  "custom_fields": [],
                  "created_at": "2026-10-24T19:42:10.512Z",
                  "updated_at": "2026-10-24T19:42:10.512Z",
                  "order_list_id": "d2f4b6c8-0e1a-4b3d-9f5b-7d9f1b3d5f70",
                  "order_list_name": "Takeaway orders"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body, a required field missing, a value outside the list's rules, or invalid custom field answers / Idempotency headers.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "validation": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "waitlist_id: Invalid UUID",
                        "details": [
                          {
                            "path": "waitlist_id",
                            "message": "Invalid UUID"
                          }
                        ]
                      }
                    }
                  },
                  "required": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "phone: This waitlist requires it"
                      }
                    }
                  },
                  "phone": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "phone: Use international format, e.g. +351912345678"
                      }
                    }
                  },
                  "order_number": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "order_number: Use up to 6 letters or digits"
                      }
                    }
                  },
                  "custom_fields": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "custom_fields: Seating preference: Choose an available option",
                        "details": [
                          {
                            "field_id": "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71",
                            "label": "Seating preference",
                            "message": "Choose an available option"
                          }
                        ]
                      }
                    }
                  },
                  "json": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "Body must be valid JSON"
                      }
                    }
                  },
                  "unknown_key": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "Unrecognized key: \"table\"",
                        "details": [
                          {
                            "path": "",
                            "message": "Unrecognized key: \"table\""
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Record not found in this business.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "A conflict with existing data, an Idempotency-Key still in use or with an unknown outcome, or the list cannot take this create (`unavailable`).",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              },
              "Retry-After": {
                "$ref": "#/components/headers/RetryAfter"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "duplicate": {
                    "value": {
                      "error": {
                        "code": "conflict",
                        "message": "This phone is already waiting on this list."
                      }
                    }
                  },
                  "order_taken": {
                    "value": {
                      "error": {
                        "code": "conflict",
                        "message": "This order number is already active on this list."
                      }
                    }
                  },
                  "services": {
                    "value": {
                      "error": {
                        "code": "unavailable",
                        "message": "This waitlist asks for services, which can't be chosen through the API yet."
                      }
                    }
                  },
                  "plan_limit": {
                    "value": {
                      "error": {
                        "code": "unavailable",
                        "message": "The monthly visit limit for this plan has been reached."
                      }
                    }
                  },
                  "full": {
                    "value": {
                      "error": {
                        "code": "unavailable",
                        "message": "This waitlist is full right now."
                      }
                    }
                  },
                  "idempotency_in_progress": {
                    "value": {
                      "error": {
                        "code": "conflict",
                        "message": "A request with this Idempotency-Key is still being processed"
                      }
                    }
                  },
                  "idempotency_outcome_unknown": {
                    "value": {
                      "error": {
                        "code": "conflict",
                        "message": "The first request with this Idempotency-Key did not finish, and it may have been created. Check before retrying with a new Idempotency-Key.",
                        "details": {
                          "idempotency": "outcome_unknown"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/IdempotencyConflict"
          },
          "429": {
            "$ref": "#/components/responses/CreateRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/orders/{id}": {
      "get": {
        "operationId": "getOrder",
        "tags": [
          "Orders"
        ],
        "summary": "Get an order",
        "description": "Reads an order's current state. Returns `404` for queue entries and for records of another business. An order has the same ID in `waitlist_entry.*` webhooks. The body is the order directly, without a data wrapper.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current resource.",
            "headers": {
              "Cache-Control": {
                "$ref": "#/components/headers/CacheControl"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                },
                "example": {
                  "id": "28bad2f4-6c8e-40ab-9d3f-5b7d9f1a2c4e",
                  "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                  "location_name": "Harbour Street",
                  "status": "waiting",
                  "previous_status": null,
                  "customer_id": "4ad2f4b6-8e0a-4c2d-9f5b-7d9f1b3d5f60",
                  "customer_name": "Rui Bettencourt",
                  "phone": null,
                  "email": null,
                  "marketing_consent": false,
                  "party_size": null,
                  "order_number": "B12",
                  "order_number_is_automatic": false,
                  "list_kind": "pickup_orders",
                  "status_label": "Waiting",
                  "display_number": "B12",
                  "custom_fields": [],
                  "created_at": "2026-10-24T19:42:10.512Z",
                  "updated_at": "2026-10-24T19:42:10.512Z",
                  "order_list_id": "d2f4b6c8-0e1a-4b3d-9f5b-7d9f1b3d5f70",
                  "order_list_name": "Takeaway orders"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "Record not found in this business.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    }
  },
  "webhooks": {
    "waitlist_entry.created": {
      "post": {
        "operationId": "webhook_waitlist_entry_created",
        "tags": [
          "Webhooks"
        ],
        "summary": "Guest added to a waitlist",
        "description": "A guest (or pick-up order) was added to a waitlist, from any source. Sent after the entry's transaction commits, so custom answers saved with it are included. Filtered by `location_id` when the subscription has one.\n\nSent as `POST` to the subscription's `target_url` with a JSON body: the record (same shape as the API's resource object) plus `event_id`, `event_type` and `occurred_at`. Request headers:\n\n- `Content-Type: application/json`, `User-Agent: WaitQ-Webhooks/1.0`\n- `X-WaitQ-Event`: the event type\n- `X-WaitQ-Event-Id`: the event id (same for every subscription and every retry of this event; use it to de-duplicate)\n- `X-WaitQ-Delivery-Id`: this subscription's delivery (same across its retries)\n- `X-WaitQ-Signature`: `t=<unix seconds>,v1=<hex HMAC-SHA256>`\n\n**Verifying the signature:** compute HMAC-SHA256 with the subscription's `signing_secret` as the key over `<t>.<raw request body>` (the timestamp, a dot, then the body bytes exactly as received), hex-encode it and compare it with `v1` in constant time. Reject timestamps older than five minutes. `t` is set on each attempt.\n\n**Responses and retries:** any 2xx marks the delivery sent. `410 Gone` disables the subscription (unsubscribes) and drops its remaining queued events. Anything else, a timeout (10 seconds) or a connection error is retried with exponential backoff, waits of 1, 2, 4 … 256 minutes, for 10 attempts (about 8.5 hours); after that the delivery fails for good. After 20 deliveries in a row fail for good, the subscription is disabled, but only once its target has gone three days without a success. One subscription's deliveries go out one at a time in the order the events happened. Deliveries are held (and eventually dropped) while the business's plan does not include integrations, and are not sent after the subscription is disabled or its API key revoked. Deliveries run from a scheduled worker, typically within a minute or two of the event.",
        "parameters": [
          {
            "name": "X-WaitQ-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "waitlist_entry.created",
                "waitlist_entry.status_changed",
                "booking.created",
                "booking.confirmed",
                "customer.created",
                "guest_list_entry.created",
                "guest_list_entry.status_changed"
              ]
            }
          },
          {
            "name": "X-WaitQ-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-WaitQ-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-WaitQ-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with the signing_secret>`.",
            "schema": {
              "type": "string",
              "pattern": "^t=[0-9]+,v1=[0-9a-f]{64}$"
            },
            "example": "t=1761334930,v1=5f2b0c1e9a7d43b8c6e1f0a2d4b6c8e0f1a3b5c7d9e1f3a5b7c9d1e3f5a7b9c1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WaitlistEntryEvent"
              },
              "example": {
                "id": "17a9c1e3-5b7d-4f9a-8c2e-4a6c8e0a2c3d",
                "waitlist_id": "c1e3a5b7-9d0f-4a2c-8e4a-6c8e0a2c4e6f",
                "waitlist_name": "Dinner walk-ins",
                "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                "location_name": "Harbour Street",
                "status": "waiting",
                "previous_status": null,
                "customer_id": "4ad2f4b6-8e0a-4c2d-9f5b-7d9f1b3d5f60",
                "customer_name": "Marta Silveira",
                "phone": "+351912000111",
                "email": "marta.silveira@example.com",
                "marketing_consent": false,
                "party_size": 4,
                "ticket_number": 27,
                "order_number": null,
                "order_number_is_automatic": null,
                "list_kind": "waitlist",
                "status_label": "Waiting",
                "display_number": "A27",
                "custom_fields": [
                  {
                    "id": "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71",
                    "label": "Seating preference",
                    "type": "single_select",
                    "value": "7d05c7e9-1b3d-4f5a-8c8e-0a2c4e6a8c93",
                    "display_value": "Terrace"
                  },
                  {
                    "id": "6cf4b6d8-0a2c-4e4f-9b7d-9f1b3d5f7b82",
                    "label": "High chair needed",
                    "type": "checkbox",
                    "value": true,
                    "display_value": "Yes"
                  }
                ],
                "created_at": "2026-10-24T19:42:10.512Z",
                "updated_at": "2026-10-24T19:42:10.512Z",
                "event_id": "a038fa1c-4e6a-4c8d-9fbb-3d5f7b9dbf16",
                "event_type": "waitlist_entry.created",
                "occurred_at": "2026-10-24T19:42:10.640Z",
                "reservation_total_minor": null,
                "reservation_total_currency": null,
                "reservation_total_formatted": "",
                "reservation_total_status": "not_calculated",
                "reservation_pricing": null,
                "payment": {
                  "availability": "not_available"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx status marks the delivery as sent; the response body is ignored."
          },
          "410": {
            "description": "Gone: WaitQ disables the subscription and sends nothing more to it."
          },
          "default": {
            "description": "Any other status (or a timeout of 10 seconds) is retried with exponential backoff, up to 10 attempts."
          }
        },
        "security": []
      }
    },
    "waitlist_entry.status_changed": {
      "post": {
        "operationId": "webhook_waitlist_entry_status_changed",
        "tags": [
          "Webhooks"
        ],
        "summary": "Waitlist entry status changed",
        "description": "A waitlist entry's status changed (called, checked in, cancelled, no-show, or back). Changes to `archived` (list clears, cleanup) are not sent. `previous_status` holds the status before the change. Clearing a pick-up list sends `no_show` for orders still ready.\n\nSent as `POST` to the subscription's `target_url` with a JSON body: the record (same shape as the API's resource object) plus `event_id`, `event_type` and `occurred_at`. Request headers:\n\n- `Content-Type: application/json`, `User-Agent: WaitQ-Webhooks/1.0`\n- `X-WaitQ-Event`: the event type\n- `X-WaitQ-Event-Id`: the event id (same for every subscription and every retry of this event; use it to de-duplicate)\n- `X-WaitQ-Delivery-Id`: this subscription's delivery (same across its retries)\n- `X-WaitQ-Signature`: `t=<unix seconds>,v1=<hex HMAC-SHA256>`\n\n**Verifying the signature:** compute HMAC-SHA256 with the subscription's `signing_secret` as the key over `<t>.<raw request body>` (the timestamp, a dot, then the body bytes exactly as received), hex-encode it and compare it with `v1` in constant time. Reject timestamps older than five minutes. `t` is set on each attempt.\n\n**Responses and retries:** any 2xx marks the delivery sent. `410 Gone` disables the subscription (unsubscribes) and drops its remaining queued events. Anything else, a timeout (10 seconds) or a connection error is retried with exponential backoff, waits of 1, 2, 4 … 256 minutes, for 10 attempts (about 8.5 hours); after that the delivery fails for good. After 20 deliveries in a row fail for good, the subscription is disabled, but only once its target has gone three days without a success. One subscription's deliveries go out one at a time in the order the events happened. Deliveries are held (and eventually dropped) while the business's plan does not include integrations, and are not sent after the subscription is disabled or its API key revoked. Deliveries run from a scheduled worker, typically within a minute or two of the event.",
        "parameters": [
          {
            "name": "X-WaitQ-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "waitlist_entry.created",
                "waitlist_entry.status_changed",
                "booking.created",
                "booking.confirmed",
                "customer.created",
                "guest_list_entry.created",
                "guest_list_entry.status_changed"
              ]
            }
          },
          {
            "name": "X-WaitQ-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-WaitQ-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-WaitQ-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with the signing_secret>`.",
            "schema": {
              "type": "string",
              "pattern": "^t=[0-9]+,v1=[0-9a-f]{64}$"
            },
            "example": "t=1761334930,v1=5f2b0c1e9a7d43b8c6e1f0a2d4b6c8e0f1a3b5c7d9e1f3a5b7c9d1e3f5a7b9c1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WaitlistEntryEvent"
              },
              "example": {
                "id": "17a9c1e3-5b7d-4f9a-8c2e-4a6c8e0a2c3d",
                "waitlist_id": "c1e3a5b7-9d0f-4a2c-8e4a-6c8e0a2c4e6f",
                "waitlist_name": "Dinner walk-ins",
                "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                "location_name": "Harbour Street",
                "status": "notified",
                "previous_status": "waiting",
                "customer_id": "4ad2f4b6-8e0a-4c2d-9f5b-7d9f1b3d5f60",
                "customer_name": "Marta Silveira",
                "phone": "+351912000111",
                "email": "marta.silveira@example.com",
                "marketing_consent": false,
                "party_size": 4,
                "ticket_number": 27,
                "order_number": null,
                "order_number_is_automatic": null,
                "list_kind": "waitlist",
                "status_label": "Called",
                "display_number": "A27",
                "custom_fields": [
                  {
                    "id": "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71",
                    "label": "Seating preference",
                    "type": "single_select",
                    "value": "7d05c7e9-1b3d-4f5a-8c8e-0a2c4e6a8c93",
                    "display_value": "Terrace"
                  },
                  {
                    "id": "6cf4b6d8-0a2c-4e4f-9b7d-9f1b3d5f7b82",
                    "label": "High chair needed",
                    "type": "checkbox",
                    "value": true,
                    "display_value": "Yes"
                  }
                ],
                "created_at": "2026-10-24T19:42:10.512Z",
                "updated_at": "2026-10-24T20:01:05.000Z",
                "event_id": "a038fa1c-4e6a-4c8d-9fbb-3d5f7b9dbf16",
                "event_type": "waitlist_entry.status_changed",
                "occurred_at": "2026-10-24T20:01:05.120Z",
                "reservation_total_minor": null,
                "reservation_total_currency": null,
                "reservation_total_formatted": "",
                "reservation_total_status": "not_calculated",
                "reservation_pricing": null,
                "payment": {
                  "availability": "not_available"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx status marks the delivery as sent; the response body is ignored."
          },
          "410": {
            "description": "Gone: WaitQ disables the subscription and sends nothing more to it."
          },
          "default": {
            "description": "Any other status (or a timeout of 10 seconds) is retried with exponential backoff, up to 10 attempts."
          }
        },
        "security": []
      }
    },
    "booking.created": {
      "post": {
        "operationId": "webhook_booking_created",
        "tags": [
          "Webhooks"
        ],
        "summary": "Booking created",
        "description": "A booking was created, from any source. Sent after the transaction commits. Filtered by `location_id` when the subscription has one.\n\nSent as `POST` to the subscription's `target_url` with a JSON body: the record (same shape as the API's resource object) plus `event_id`, `event_type` and `occurred_at`. Request headers:\n\n- `Content-Type: application/json`, `User-Agent: WaitQ-Webhooks/1.0`\n- `X-WaitQ-Event`: the event type\n- `X-WaitQ-Event-Id`: the event id (same for every subscription and every retry of this event; use it to de-duplicate)\n- `X-WaitQ-Delivery-Id`: this subscription's delivery (same across its retries)\n- `X-WaitQ-Signature`: `t=<unix seconds>,v1=<hex HMAC-SHA256>`\n\n**Verifying the signature:** compute HMAC-SHA256 with the subscription's `signing_secret` as the key over `<t>.<raw request body>` (the timestamp, a dot, then the body bytes exactly as received), hex-encode it and compare it with `v1` in constant time. Reject timestamps older than five minutes. `t` is set on each attempt.\n\n**Responses and retries:** any 2xx marks the delivery sent. `410 Gone` disables the subscription (unsubscribes) and drops its remaining queued events. Anything else, a timeout (10 seconds) or a connection error is retried with exponential backoff, waits of 1, 2, 4 … 256 minutes, for 10 attempts (about 8.5 hours); after that the delivery fails for good. After 20 deliveries in a row fail for good, the subscription is disabled, but only once its target has gone three days without a success. One subscription's deliveries go out one at a time in the order the events happened. Deliveries are held (and eventually dropped) while the business's plan does not include integrations, and are not sent after the subscription is disabled or its API key revoked. Deliveries run from a scheduled worker, typically within a minute or two of the event.",
        "parameters": [
          {
            "name": "X-WaitQ-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "waitlist_entry.created",
                "waitlist_entry.status_changed",
                "booking.created",
                "booking.confirmed",
                "customer.created",
                "guest_list_entry.created",
                "guest_list_entry.status_changed"
              ]
            }
          },
          {
            "name": "X-WaitQ-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-WaitQ-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-WaitQ-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with the signing_secret>`.",
            "schema": {
              "type": "string",
              "pattern": "^t=[0-9]+,v1=[0-9a-f]{64}$"
            },
            "example": "t=1761334930,v1=5f2b0c1e9a7d43b8c6e1f0a2d4b6c8e0f1a3b5c7d9e1f3a5b7c9d1e3f5a7b9c1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingEvent"
              },
              "example": {
                "id": "28b0d2f4-6c8e-4a0b-9d3f-5b7d9f1b3d4e",
                "booking_list_id": "e3a5c7d9-1f2b-4c4e-8a6c-8e0a2c4e6a81",
                "booking_list_name": "Table reservations",
                "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                "location_name": "Harbour Street",
                "status": "confirmed",
                "customer_id": "4ad2f4b6-8e0a-4c2d-9f5b-7d9f1b3d5f60",
                "customer_name": "Tomas Arruda",
                "phone": "+351913000222",
                "email": "tomas.arruda@example.com",
                "marketing_consent": true,
                "party_size": 2,
                "starts_at": "2026-10-30T19:30:00+00:00",
                "ends_at": "2026-10-30T21:00:00+00:00",
                "notes": "Anniversary dinner",
                "custom_fields": [
                  {
                    "id": "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71",
                    "label": "Seating preference",
                    "type": "single_select",
                    "value": "7d05c7e9-1b3d-4f5a-8c8e-0a2c4e6a8c93",
                    "display_value": "Terrace"
                  }
                ],
                "created_at": "2026-10-24T10:05:33.201Z",
                "updated_at": "2026-10-24T10:05:33.201Z",
                "event_id": "a038fa1c-4e6a-4c8d-9fbb-3d5f7b9dbf16",
                "event_type": "booking.created",
                "occurred_at": "2026-10-24T10:05:33.300Z",
                "reservation_total_minor": null,
                "reservation_total_currency": null,
                "reservation_total_formatted": "",
                "reservation_total_status": "not_calculated",
                "reservation_pricing": null,
                "payment": {
                  "availability": "not_available"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx status marks the delivery as sent; the response body is ignored."
          },
          "410": {
            "description": "Gone: WaitQ disables the subscription and sends nothing more to it."
          },
          "default": {
            "description": "Any other status (or a timeout of 10 seconds) is retried with exponential backoff, up to 10 attempts."
          }
        },
        "security": []
      }
    },
    "customer.created": {
      "post": {
        "operationId": "webhook_customer_created",
        "tags": [
          "Webhooks"
        ],
        "summary": "Customer created",
        "description": "A new customer profile was created. Queued about one minute after creation and sent as the profile then stands; skipped if the profile was merged into another customer or deleted before delivery. Not filtered by location.\n\nSent as `POST` to the subscription's `target_url` with a JSON body: the record (same shape as the API's resource object) plus `event_id`, `event_type` and `occurred_at`. Request headers:\n\n- `Content-Type: application/json`, `User-Agent: WaitQ-Webhooks/1.0`\n- `X-WaitQ-Event`: the event type\n- `X-WaitQ-Event-Id`: the event id (same for every subscription and every retry of this event; use it to de-duplicate)\n- `X-WaitQ-Delivery-Id`: this subscription's delivery (same across its retries)\n- `X-WaitQ-Signature`: `t=<unix seconds>,v1=<hex HMAC-SHA256>`\n\n**Verifying the signature:** compute HMAC-SHA256 with the subscription's `signing_secret` as the key over `<t>.<raw request body>` (the timestamp, a dot, then the body bytes exactly as received), hex-encode it and compare it with `v1` in constant time. Reject timestamps older than five minutes. `t` is set on each attempt.\n\n**Responses and retries:** any 2xx marks the delivery sent. `410 Gone` disables the subscription (unsubscribes) and drops its remaining queued events. Anything else, a timeout (10 seconds) or a connection error is retried with exponential backoff, waits of 1, 2, 4 … 256 minutes, for 10 attempts (about 8.5 hours); after that the delivery fails for good. After 20 deliveries in a row fail for good, the subscription is disabled, but only once its target has gone three days without a success. One subscription's deliveries go out one at a time in the order the events happened. Deliveries are held (and eventually dropped) while the business's plan does not include integrations, and are not sent after the subscription is disabled or its API key revoked. Deliveries run from a scheduled worker, typically within a minute or two of the event.",
        "parameters": [
          {
            "name": "X-WaitQ-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "waitlist_entry.created",
                "waitlist_entry.status_changed",
                "booking.created",
                "booking.confirmed",
                "customer.created",
                "guest_list_entry.created",
                "guest_list_entry.status_changed"
              ]
            }
          },
          {
            "name": "X-WaitQ-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-WaitQ-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-WaitQ-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with the signing_secret>`.",
            "schema": {
              "type": "string",
              "pattern": "^t=[0-9]+,v1=[0-9a-f]{64}$"
            },
            "example": "t=1761334930,v1=5f2b0c1e9a7d43b8c6e1f0a2d4b6c8e0f1a3b5c7d9e1f3a5b7c9d1e3f5a7b9c1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerEvent"
              },
              "example": {
                "id": "4ad2f4b6-8e0a-4c2d-9f5b-7d9f1b3d5f60",
                "name": "Marta Silveira",
                "phone": "+351912000111",
                "email": "marta.silveira@example.com",
                "marketing_consent": false,
                "created_at": "2026-03-02T12:00:00.000Z",
                "updated_at": "2026-10-24T19:42:10.512Z",
                "event_id": "a038fa1c-4e6a-4c8d-9fbb-3d5f7b9dbf16",
                "event_type": "customer.created",
                "occurred_at": "2026-03-02T12:00:00.004Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx status marks the delivery as sent; the response body is ignored."
          },
          "410": {
            "description": "Gone: WaitQ disables the subscription and sends nothing more to it."
          },
          "default": {
            "description": "Any other status (or a timeout of 10 seconds) is retried with exponential backoff, up to 10 attempts."
          }
        },
        "security": []
      }
    },
    "guest_list_entry.created": {
      "post": {
        "operationId": "webhook_guest_list_entry_created",
        "tags": [
          "Webhooks"
        ],
        "summary": "Guest added to a guest list",
        "description": "A guest was added to a guest list one at a time (dashboard or API). Guests imported from a file (several rows in one insert) do not fire it. Filtered by `location_id` when the subscription has one.\n\nSent as `POST` to the subscription's `target_url` with a JSON body: the record (same shape as the API's resource object) plus `event_id`, `event_type` and `occurred_at`. Request headers:\n\n- `Content-Type: application/json`, `User-Agent: WaitQ-Webhooks/1.0`\n- `X-WaitQ-Event`: the event type\n- `X-WaitQ-Event-Id`: the event id (same for every subscription and every retry of this event; use it to de-duplicate)\n- `X-WaitQ-Delivery-Id`: this subscription's delivery (same across its retries)\n- `X-WaitQ-Signature`: `t=<unix seconds>,v1=<hex HMAC-SHA256>`\n\n**Verifying the signature:** compute HMAC-SHA256 with the subscription's `signing_secret` as the key over `<t>.<raw request body>` (the timestamp, a dot, then the body bytes exactly as received), hex-encode it and compare it with `v1` in constant time. Reject timestamps older than five minutes. `t` is set on each attempt.\n\n**Responses and retries:** any 2xx marks the delivery sent. `410 Gone` disables the subscription (unsubscribes) and drops its remaining queued events. Anything else, a timeout (10 seconds) or a connection error is retried with exponential backoff, waits of 1, 2, 4 … 256 minutes, for 10 attempts (about 8.5 hours); after that the delivery fails for good. After 20 deliveries in a row fail for good, the subscription is disabled, but only once its target has gone three days without a success. One subscription's deliveries go out one at a time in the order the events happened. Deliveries are held (and eventually dropped) while the business's plan does not include integrations, and are not sent after the subscription is disabled or its API key revoked. Deliveries run from a scheduled worker, typically within a minute or two of the event.",
        "parameters": [
          {
            "name": "X-WaitQ-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "waitlist_entry.created",
                "waitlist_entry.status_changed",
                "booking.created",
                "booking.confirmed",
                "customer.created",
                "guest_list_entry.created",
                "guest_list_entry.status_changed"
              ]
            }
          },
          {
            "name": "X-WaitQ-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-WaitQ-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-WaitQ-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with the signing_secret>`.",
            "schema": {
              "type": "string",
              "pattern": "^t=[0-9]+,v1=[0-9a-f]{64}$"
            },
            "example": "t=1761334930,v1=5f2b0c1e9a7d43b8c6e1f0a2d4b6c8e0f1a3b5c7d9e1f3a5b7c9d1e3f5a7b9c1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GuestListEntryEvent"
              },
              "example": {
                "id": "39c1e3a5-7d9f-4b1c-8e4a-6c8e0a2c4e5f",
                "guest_list_id": "f4b6d8e0-2a3c-4d5f-9b7d-9f1b3d5f7b92",
                "guest_list_name": "Friday Jazz Night",
                "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                "location_name": "Harbour Street",
                "event_starts_at": "2026-10-30T21:00:00+00:00",
                "event_ends_at": "2026-10-31T02:00:00+00:00",
                "visit_date": "2026-10-30",
                "status": "confirmed",
                "previous_status": null,
                "status_label": "Confirmed",
                "customer_id": null,
                "customer_name": "Ines Medeiros",
                "phone": "+351914000333",
                "email": null,
                "party_size": 3,
                "category": "VIP",
                "tags": [
                  "press",
                  "plus-two"
                ],
                "notes": "Arriving after 22:00",
                "showed_at": null,
                "no_show_at": null,
                "custom_fields": [],
                "created_at": "2026-10-24T15:20:00.000Z",
                "updated_at": "2026-10-24T15:20:00.000Z",
                "event_id": "a038fa1c-4e6a-4c8d-9fbb-3d5f7b9dbf16",
                "event_type": "guest_list_entry.created",
                "occurred_at": "2026-10-24T15:20:00.090Z",
                "reservation_total_minor": null,
                "reservation_total_currency": null,
                "reservation_total_formatted": "",
                "reservation_total_status": "not_calculated",
                "reservation_pricing": null,
                "payment": {
                  "availability": "not_available"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx status marks the delivery as sent; the response body is ignored."
          },
          "410": {
            "description": "Gone: WaitQ disables the subscription and sends nothing more to it."
          },
          "default": {
            "description": "Any other status (or a timeout of 10 seconds) is retried with exponential backoff, up to 10 attempts."
          }
        },
        "security": []
      }
    },
    "guest_list_entry.status_changed": {
      "post": {
        "operationId": "webhook_guest_list_entry_status_changed",
        "tags": [
          "Webhooks"
        ],
        "summary": "Guest status changed",
        "description": "A guest's status changed between `confirmed`, `show` and `no_show`, including bulk no-shows and their undo. Each event carries the status, `showed_at` and `no_show_at` its own change set, and `previous_status`. Deleting a guest sends nothing.\n\nSent as `POST` to the subscription's `target_url` with a JSON body: the record (same shape as the API's resource object) plus `event_id`, `event_type` and `occurred_at`. Request headers:\n\n- `Content-Type: application/json`, `User-Agent: WaitQ-Webhooks/1.0`\n- `X-WaitQ-Event`: the event type\n- `X-WaitQ-Event-Id`: the event id (same for every subscription and every retry of this event; use it to de-duplicate)\n- `X-WaitQ-Delivery-Id`: this subscription's delivery (same across its retries)\n- `X-WaitQ-Signature`: `t=<unix seconds>,v1=<hex HMAC-SHA256>`\n\n**Verifying the signature:** compute HMAC-SHA256 with the subscription's `signing_secret` as the key over `<t>.<raw request body>` (the timestamp, a dot, then the body bytes exactly as received), hex-encode it and compare it with `v1` in constant time. Reject timestamps older than five minutes. `t` is set on each attempt.\n\n**Responses and retries:** any 2xx marks the delivery sent. `410 Gone` disables the subscription (unsubscribes) and drops its remaining queued events. Anything else, a timeout (10 seconds) or a connection error is retried with exponential backoff, waits of 1, 2, 4 … 256 minutes, for 10 attempts (about 8.5 hours); after that the delivery fails for good. After 20 deliveries in a row fail for good, the subscription is disabled, but only once its target has gone three days without a success. One subscription's deliveries go out one at a time in the order the events happened. Deliveries are held (and eventually dropped) while the business's plan does not include integrations, and are not sent after the subscription is disabled or its API key revoked. Deliveries run from a scheduled worker, typically within a minute or two of the event.",
        "parameters": [
          {
            "name": "X-WaitQ-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "waitlist_entry.created",
                "waitlist_entry.status_changed",
                "booking.created",
                "booking.confirmed",
                "customer.created",
                "guest_list_entry.created",
                "guest_list_entry.status_changed"
              ]
            }
          },
          {
            "name": "X-WaitQ-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-WaitQ-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-WaitQ-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with the signing_secret>`.",
            "schema": {
              "type": "string",
              "pattern": "^t=[0-9]+,v1=[0-9a-f]{64}$"
            },
            "example": "t=1761334930,v1=5f2b0c1e9a7d43b8c6e1f0a2d4b6c8e0f1a3b5c7d9e1f3a5b7c9d1e3f5a7b9c1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GuestListEntryEvent"
              },
              "example": {
                "id": "39c1e3a5-7d9f-4b1c-8e4a-6c8e0a2c4e5f",
                "guest_list_id": "f4b6d8e0-2a3c-4d5f-9b7d-9f1b3d5f7b92",
                "guest_list_name": "Friday Jazz Night",
                "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                "location_name": "Harbour Street",
                "event_starts_at": "2026-10-30T21:00:00+00:00",
                "event_ends_at": "2026-10-31T02:00:00+00:00",
                "visit_date": "2026-10-30",
                "status": "show",
                "previous_status": "confirmed",
                "status_label": "Showed",
                "customer_id": null,
                "customer_name": "Ines Medeiros",
                "phone": "+351914000333",
                "email": null,
                "party_size": 3,
                "category": "VIP",
                "tags": [
                  "press",
                  "plus-two"
                ],
                "notes": "Arriving after 22:00",
                "showed_at": "2026-10-30T22:14:00.000Z",
                "no_show_at": null,
                "custom_fields": [],
                "created_at": "2026-10-24T15:20:00.000Z",
                "updated_at": "2026-10-30T22:14:00.000Z",
                "event_id": "a038fa1c-4e6a-4c8d-9fbb-3d5f7b9dbf16",
                "event_type": "guest_list_entry.status_changed",
                "occurred_at": "2026-10-30T22:14:00.050Z",
                "reservation_total_minor": null,
                "reservation_total_currency": null,
                "reservation_total_formatted": "",
                "reservation_total_status": "not_calculated",
                "reservation_pricing": null,
                "payment": {
                  "availability": "not_available"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx status marks the delivery as sent; the response body is ignored."
          },
          "410": {
            "description": "Gone: WaitQ disables the subscription and sends nothing more to it."
          },
          "default": {
            "description": "Any other status (or a timeout of 10 seconds) is retried with exponential backoff, up to 10 attempts."
          }
        },
        "security": []
      }
    },
    "booking.confirmed": {
      "post": {
        "operationId": "webhook_booking_confirmed",
        "tags": [
          "Webhooks"
        ],
        "summary": "Booking confirmed",
        "description": "A booking was created confirmed or became confirmed, from any source. Sent at most once per change, and only if the booking is still confirmed when the change is saved. Sent after the transaction commits. Filtered by `location_id` when the subscription has one.\n\nSent as `POST` to the subscription's `target_url` with a JSON body: the record (same shape as the API's resource object) plus `event_id`, `event_type` and `occurred_at`. Request headers:\n\n- `Content-Type: application/json`, `User-Agent: WaitQ-Webhooks/1.0`\n- `X-WaitQ-Event`: the event type\n- `X-WaitQ-Event-Id`: the event id (same for every subscription and every retry of this event; use it to de-duplicate)\n- `X-WaitQ-Delivery-Id`: this subscription's delivery (same across its retries)\n- `X-WaitQ-Signature`: `t=<unix seconds>,v1=<hex HMAC-SHA256>`\n\n**Verifying the signature:** compute HMAC-SHA256 with the subscription's `signing_secret` as the key over `<t>.<raw request body>` (the timestamp, a dot, then the body bytes exactly as received), hex-encode it and compare it with `v1` in constant time. Reject timestamps older than five minutes. `t` is set on each attempt.\n\n**Responses and retries:** any 2xx marks the delivery sent. `410 Gone` disables the subscription (unsubscribes) and drops its remaining queued events. Anything else, a timeout (10 seconds) or a connection error is retried with exponential backoff, waits of 1, 2, 4 … 256 minutes, for 10 attempts (about 8.5 hours); after that the delivery fails for good. After 20 deliveries in a row fail for good, the subscription is disabled, but only once its target has gone three days without a success. One subscription's deliveries go out one at a time in the order the events happened. Deliveries are held (and eventually dropped) while the business's plan does not include integrations, and are not sent after the subscription is disabled or its API key revoked. Deliveries run from a scheduled worker, typically within a minute or two of the event.",
        "parameters": [
          {
            "name": "X-WaitQ-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "waitlist_entry.created",
                "waitlist_entry.status_changed",
                "booking.created",
                "booking.confirmed",
                "customer.created",
                "guest_list_entry.created",
                "guest_list_entry.status_changed"
              ]
            }
          },
          {
            "name": "X-WaitQ-Event-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-WaitQ-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "X-WaitQ-Signature",
            "in": "header",
            "required": true,
            "description": "`t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" keyed with the signing_secret>`.",
            "schema": {
              "type": "string",
              "pattern": "^t=[0-9]+,v1=[0-9a-f]{64}$"
            },
            "example": "t=1761334930,v1=5f2b0c1e9a7d43b8c6e1f0a2d4b6c8e0f1a3b5c7d9e1f3a5b7c9d1e3f5a7b9c1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingEvent"
              },
              "example": {
                "id": "28b0d2f4-6c8e-4a0b-9d3f-5b7d9f1b3d4e",
                "booking_list_id": "e3a5c7d9-1f2b-4c4e-8a6c-8e0a2c4e6a81",
                "booking_list_name": "Table reservations",
                "location_id": "a3c5e7f9-1b2d-4e6f-8a0c-2e4f6a8c0e1b",
                "location_name": "Harbour Street",
                "status": "confirmed",
                "customer_id": "4ad2f4b6-8e0a-4c2d-9f5b-7d9f1b3d5f60",
                "customer_name": "Tomas Arruda",
                "phone": "+351913000222",
                "email": "tomas.arruda@example.com",
                "marketing_consent": true,
                "party_size": 2,
                "starts_at": "2026-10-30T19:30:00+00:00",
                "ends_at": "2026-10-30T21:00:00+00:00",
                "notes": "Anniversary dinner",
                "custom_fields": [
                  {
                    "id": "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71",
                    "label": "Seating preference",
                    "type": "single_select",
                    "value": "7d05c7e9-1b3d-4f5a-8c8e-0a2c4e6a8c93",
                    "display_value": "Terrace"
                  }
                ],
                "created_at": "2026-10-24T10:05:33.201Z",
                "updated_at": "2026-10-24T10:05:33.201Z",
                "event_id": "a038fa1c-4e6a-4c8d-9fbb-3d5f7b9dbf16",
                "event_type": "booking.confirmed",
                "occurred_at": "2026-10-24T10:05:33.300Z",
                "reservation_total_minor": null,
                "reservation_total_currency": null,
                "reservation_total_formatted": "",
                "reservation_total_status": "not_calculated",
                "reservation_pricing": null,
                "payment": {
                  "availability": "not_available"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx status marks the delivery as sent; the response body is ignored."
          },
          "410": {
            "description": "Gone: WaitQ disables the subscription and sends nothing more to it."
          },
          "default": {
            "description": "Any other status (or a timeout of 10 seconds) is retried with exponential backoff, up to 10 attempts."
          }
        },
        "security": []
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "wq_live_<43 URL-safe base64 characters>",
        "description": "`Authorization: Bearer wq_live_…`"
      },
      "apiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "`X-API-Key: wq_live_…` (used when no Bearer token is sent)."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "unauthorized",
                  "forbidden",
                  "plan_required",
                  "rate_limited",
                  "invalid_request",
                  "not_found",
                  "conflict",
                  "unavailable",
                  "idempotency_conflict",
                  "internal_error",
                  "payment_required"
                ]
              },
              "message": {
                "type": "string",
                "description": "Human-readable; for field problems it starts with the field name (`party_size: …`)."
              },
              "details": {
                "description": "Present on some errors only.",
                "anyOf": [
                  {
                    "type": "array",
                    "description": "Request validation problems.",
                    "items": {
                      "$ref": "#/components/schemas/ValidationIssue"
                    }
                  },
                  {
                    "type": "array",
                    "description": "Custom field problems.",
                    "items": {
                      "$ref": "#/components/schemas/CustomFieldProblem"
                    }
                  },
                  {
                    "$ref": "#/components/schemas/CreateLimitDetails"
                  },
                  {
                    "$ref": "#/components/schemas/IdempotencyDetails"
                  }
                ]
              }
            }
          }
        }
      },
      "ValidationIssue": {
        "type": "object",
        "required": [
          "path",
          "message"
        ],
        "properties": {
          "path": {
            "type": "string",
            "description": "Dot-separated path of the offending property; empty for the body itself."
          },
          "message": {
            "type": "string"
          }
        }
      },
      "CustomFieldProblem": {
        "type": "object",
        "required": [
          "field_id",
          "label",
          "message"
        ],
        "properties": {
          "field_id": {
            "type": "string"
          },
          "label": {
            "type": [
              "string",
              "null"
            ],
            "description": "Field label, or null for an id the list does not have."
          },
          "message": {
            "type": "string"
          }
        }
      },
      "CreateLimitDetails": {
        "type": "object",
        "required": [
          "limit",
          "max"
        ],
        "properties": {
          "limit": {
            "type": "string",
            "enum": [
              "business_creates_per_minute",
              "business_creates_per_day"
            ]
          },
          "max": {
            "type": "integer",
            "examples": [
              60
            ]
          }
        }
      },
      "IdempotencyDetails": {
        "type": "object",
        "required": [
          "idempotency"
        ],
        "properties": {
          "idempotency": {
            "type": "string",
            "const": "outcome_unknown"
          }
        }
      },
      "Me": {
        "type": "object",
        "required": [
          "business",
          "api_key"
        ],
        "properties": {
          "business": {
            "type": "object",
            "required": [
              "id",
              "name"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "api_key": {
            "type": "object",
            "required": [
              "id",
              "name",
              "prefix"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string",
                "description": "The name given to the key in WaitQ."
              },
              "prefix": {
                "type": "string",
                "description": "The key's first 14 characters (`wq_live_` plus 6), enough to tell keys apart."
              }
            }
          }
        }
      },
      "Location": {
        "type": "object",
        "required": [
          "id",
          "name",
          "timezone"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "timezone": {
            "type": [
              "string",
              "null"
            ],
            "description": "IANA time zone of the location."
          }
        }
      },
      "WaitlistSummary": {
        "type": "object",
        "required": [
          "id",
          "name",
          "location_id",
          "kind",
          "order_numbers",
          "accepts_api_creates"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "location_id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "waitlist",
              "pickup_orders"
            ],
            "description": "`pickup_orders` lists are waitlists with order numbers instead of tickets."
          },
          "order_numbers": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "automatic",
              "manual",
              null
            ],
            "description": "How a pick-up order list numbers orders; `null` on ordinary waitlists."
          },
          "accepts_api_creates": {
            "type": "boolean",
            "description": "`false` for lists that make staff choose services: `POST /waitlist-entries` answers `409 unavailable` for them. Their webhooks still work."
          }
        }
      },
      "BookingListSummary": {
        "type": "object",
        "required": [
          "id",
          "name",
          "location_id",
          "accepts_api_creates"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "location_id": {
            "type": "string",
            "format": "uuid"
          },
          "accepts_api_creates": {
            "type": "boolean",
            "description": "Whether a booking can be created without choosing services. `false` for lists that use services: send `service_ids` (see `requires_service_selection`)."
          },
          "requires_service_selection": {
            "type": "boolean",
            "description": "When `true`, send `service_ids` to create a booking on this list."
          }
        }
      },
      "GuestListSummary": {
        "type": "object",
        "required": [
          "id",
          "name",
          "location_id",
          "event_starts_at",
          "event_ends_at",
          "capacity",
          "timezone",
          "accepts_api_creates"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "location_id": {
            "type": "string",
            "format": "uuid"
          },
          "event_starts_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "event_ends_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "capacity": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Informational only, as in the dashboard: it never stops guests being added."
          },
          "timezone": {
            "type": [
              "string",
              "null"
            ],
            "description": "IANA time zone of the list's location."
          },
          "accepts_api_creates": {
            "type": "boolean",
            "description": "`false` for lists that make staff choose services: `POST /guest-list-entries` answers `409 unavailable` for them."
          }
        }
      },
      "StandardField": {
        "type": "object",
        "description": "A built-in field the list's form shows. Fields the list does not show are not listed and are ignored on create.",
        "required": [
          "key",
          "label",
          "required"
        ],
        "properties": {
          "key": {
            "type": "string",
            "enum": [
              "order_number",
              "customer_name",
              "phone",
              "email",
              "party_size",
              "category",
              "tags",
              "notes",
              "marketing_consent"
            ]
          },
          "label": {
            "type": "string",
            "examples": [
              "Party Size"
            ]
          },
          "required": {
            "type": "boolean"
          },
          "min": {
            "type": "integer",
            "description": "Party size: the list's minimum."
          },
          "max": {
            "type": "integer",
            "description": "Party size: the list's maximum."
          },
          "max_length": {
            "type": "integer",
            "description": "Order number (6), category (40) or each tag (40)."
          },
          "max_items": {
            "type": "integer",
            "description": "Tags: how many a guest can have (20)."
          },
          "consent_text": {
            "type": [
              "string",
              "null"
            ],
            "description": "Marketing consent: the list's own consent wording, or null."
          }
        }
      },
      "CustomFieldDescription": {
        "type": "object",
        "required": [
          "id",
          "label",
          "help_text",
          "type",
          "required",
          "staff_only"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "label": {
            "type": "string"
          },
          "help_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "short_text",
              "long_text",
              "number",
              "date",
              "single_select",
              "multi_select",
              "checkbox"
            ]
          },
          "required": {
            "type": "boolean",
            "description": "Required for staff entries, which is how the API fills the form."
          },
          "staff_only": {
            "type": "boolean"
          },
          "options": {
            "type": "array",
            "description": "Active options of a select field (absent when there are none).",
            "items": {
              "type": "object",
              "required": [
                "id",
                "label"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                }
              }
            }
          },
          "min": {
            "type": "number"
          },
          "max": {
            "type": "number"
          },
          "integer": {
            "type": "boolean",
            "const": true,
            "description": "Present (true) when a number field only takes whole numbers."
          },
          "max_selections": {
            "type": "integer",
            "description": "Multi-select only (default 20)."
          }
        }
      },
      "ListFields": {
        "type": "object",
        "required": [
          "standard_fields",
          "custom_fields"
        ],
        "properties": {
          "standard_fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StandardField"
            }
          },
          "custom_fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomFieldDescription"
            },
            "description": "Active custom fields in form order; empty when the plan does not include custom fields."
          }
        }
      },
      "WaitlistFields": {
        "allOf": [
          {
            "type": "object",
            "required": [
              "kind",
              "order_numbers"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "waitlist",
                  "pickup_orders"
                ]
              },
              "order_numbers": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "automatic",
                  "manual",
                  null
                ]
              }
            }
          },
          {
            "$ref": "#/components/schemas/ListFields"
          }
        ]
      },
      "OpenSlot": {
        "type": "object",
        "required": [
          "starts_at",
          "ends_at",
          "local_date",
          "local_time",
          "party_size",
          "spots_open",
          "remaining_capacity"
        ],
        "properties": {
          "starts_at": {
            "type": "string",
            "format": "date-time",
            "description": "Slot start (UTC). Pass this, or `local_date` + `local_time`, as `starts_at` to `POST /bookings`."
          },
          "ends_at": {
            "type": "string",
            "format": "date-time"
          },
          "local_date": {
            "type": "string",
            "format": "date",
            "description": "Date at the location."
          },
          "local_time": {
            "type": "string",
            "pattern": "^[0-2][0-9]:[0-5][0-9]$",
            "description": "Clock time at the location (HH:MM)."
          },
          "party_size": {
            "type": "integer"
          },
          "spots_open": {
            "type": "integer",
            "description": "How many more bookings of this party size fit."
          },
          "remaining_capacity": {
            "type": "integer",
            "description": "Covers still free in the slot."
          }
        }
      },
      "Availability": {
        "type": "object",
        "required": [
          "data",
          "booking_list_id",
          "date",
          "days",
          "party_size",
          "timezone"
        ],
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OpenSlot"
            }
          },
          "booking_list_id": {
            "type": "string",
            "format": "uuid"
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "First day searched (defaults to today at the location)."
          },
          "days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 7
          },
          "party_size": {
            "type": "integer",
            "description": "Party size the slots were checked for (1 when the list does not ask for a party size)."
          },
          "timezone": {
            "type": "string",
            "description": "Location time zone (`UTC` when the location has none)."
          }
        }
      },
      "CustomFieldValue": {
        "type": "object",
        "description": "One custom field answer on a record. Staff-only fields are included. Answers to archived fields are left out.",
        "required": [
          "id",
          "label",
          "type",
          "value",
          "display_value"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Custom field id."
          },
          "label": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "short_text",
              "long_text",
              "number",
              "date",
              "single_select",
              "multi_select",
              "checkbox"
            ]
          },
          "value": {
            "description": "The stored answer as JSON: a string, number, boolean, `YYYY-MM-DD` string, option id, or array of option ids."
          },
          "display_value": {
            "type": [
              "string",
              "null"
            ],
            "description": "The answer as staff read it: option labels (comma-separated for multi-select), `Yes`/`No` for checkboxes, text for the rest."
          }
        }
      },
      "WaitlistEntry": {
        "type": "object",
        "description": "A guest on a waitlist, or an order on a pick-up order list.",
        "required": [
          "id",
          "waitlist_id",
          "waitlist_name",
          "location_id",
          "location_name",
          "status",
          "previous_status",
          "customer_id",
          "customer_name",
          "phone",
          "email",
          "marketing_consent",
          "party_size",
          "ticket_number",
          "order_number",
          "order_number_is_automatic",
          "list_kind",
          "status_label",
          "display_number",
          "custom_fields",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "waitlist_id": {
            "type": "string",
            "format": "uuid"
          },
          "waitlist_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "location_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "location_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "waiting",
              "notified",
              "seated",
              "cancelled",
              "no_show",
              "archived"
            ],
            "description": "`notified` = called (pick-up: ready), `seated` = checked in (pick-up: picked up)."
          },
          "previous_status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "waiting",
              "notified",
              "seated",
              "cancelled",
              "no_show",
              "archived",
              null
            ],
            "description": "Status before the change on `waitlist_entry.status_changed`; null elsewhere."
          },
          "customer_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "customer_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "marketing_consent": {
            "type": "boolean",
            "description": "The linked customer's marketing consent (false without a customer)."
          },
          "party_size": {
            "type": [
              "integer",
              "null"
            ]
          },
          "ticket_number": {
            "type": [
              "integer",
              "null"
            ]
          },
          "order_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "order_number_is_automatic": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "list_kind": {
            "type": "string",
            "enum": [
              "waitlist",
              "pickup_orders"
            ]
          },
          "status_label": {
            "type": [
              "string",
              "null"
            ],
            "description": "Status in the list's own words. Waitlists: Waiting, Called, Checked in, Cancelled, No-show, Archived. Pick-up lists: Waiting, Ready for pickup, Picked up, Cancelled, Not picked up, Archived."
          },
          "display_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "The number guests see: the ticket or generated order number with the list's one-letter prefix, or a manual order number as typed."
          },
          "custom_fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomFieldValue"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "service_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "ID when exactly one service was selected; otherwise null."
          },
          "service_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Name when exactly one service was selected; otherwise null."
          },
          "service_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "services": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BookingService"
            },
            "description": "Selected services ordered by name and ID; empty for bookings without services. Includes services archived after they were selected. Catalog details reflect their current values."
          },
          "selected_services": {
            "type": "string",
            "description": "Email-ready text containing all selected service names separated by commas, in the same order as services. Empty string when no services are selected. Use this field for email templates regardless of selection count.",
            "example": "Consultation, Treatment"
          },
          "reservation_total_minor": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Captured price in currency minor units, from the selected services' prices when the record was created. Null when the price is unknown or the record was created before totals were captured; never a partial sum."
          },
          "reservation_total_currency": {
            "type": [
              "string",
              "null"
            ],
            "description": "Currency frozen in the reservation quote, never inferred from the current business default. Null when not calculated."
          },
          "reservation_total_formatted": {
            "type": "string",
            "description": "Email-ready reservation total with snapshot currency precision and ISO code. Empty when not calculated. Check reservation_total_status; do not replace an empty value with a catalog total.",
            "examples": [
              "110.70 EUR",
              "1234 JPY",
              "1.234 KWD",
              ""
            ]
          },
          "reservation_total_status": {
            "type": "string",
            "enum": [
              "not_calculated",
              "complete"
            ],
            "description": "complete includes an explicit zero/free price. not_calculated means unknown pricing, no priced services or a record created before totals were captured."
          },
          "reservation_pricing": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ReservationPricing"
              },
              {
                "type": "null"
              }
            ],
            "description": "Details of the captured price. Null when no price was captured."
          },
          "payment": {
            "$ref": "#/components/schemas/PaymentAvailability"
          }
        }
      },
      "Booking": {
        "type": "object",
        "required": [
          "id",
          "booking_list_id",
          "booking_list_name",
          "location_id",
          "location_name",
          "status",
          "customer_id",
          "customer_name",
          "phone",
          "email",
          "marketing_consent",
          "party_size",
          "starts_at",
          "ends_at",
          "notes",
          "custom_fields",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "booking_list_id": {
            "type": "string",
            "format": "uuid"
          },
          "booking_list_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "location_id": {
            "type": "string",
            "format": "uuid"
          },
          "location_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "confirmed",
              "declined",
              "cancelled",
              "arrived",
              "show",
              "no_show"
            ],
            "description": "Bookings created through the API are `confirmed`."
          },
          "customer_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "customer_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "marketing_consent": {
            "type": "boolean"
          },
          "party_size": {
            "type": "integer"
          },
          "starts_at": {
            "type": "string",
            "format": "date-time"
          },
          "ends_at": {
            "type": "string",
            "format": "date-time"
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "custom_fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomFieldValue"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "service_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "ID when exactly one service was selected; otherwise null."
          },
          "service_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Name when exactly one service was selected; otherwise null."
          },
          "service_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "services": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BookingService"
            },
            "description": "Selected services ordered by name and ID; empty for bookings without services. Includes services archived after they were selected. Catalog details reflect their current values."
          },
          "selected_services": {
            "type": "string",
            "description": "Email-ready text containing all selected service names separated by commas, in the same order as services. Empty string when no services are selected. Use this field for email templates regardless of selection count.",
            "example": "Consultation, Treatment"
          },
          "reservation_total_minor": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Captured price in currency minor units, from the selected services' prices when the record was created. Null when the price is unknown or the record was created before totals were captured; never a partial sum."
          },
          "reservation_total_currency": {
            "type": [
              "string",
              "null"
            ],
            "description": "Currency frozen in the reservation quote, never inferred from the current business default. Null when not calculated."
          },
          "reservation_total_formatted": {
            "type": "string",
            "description": "Email-ready reservation total with snapshot currency precision and ISO code. Empty when not calculated. Check reservation_total_status; do not replace an empty value with a catalog total.",
            "examples": [
              "110.70 EUR",
              "1234 JPY",
              "1.234 KWD",
              ""
            ]
          },
          "reservation_total_status": {
            "type": "string",
            "enum": [
              "not_calculated",
              "complete"
            ],
            "description": "complete includes an explicit zero/free price. not_calculated means unknown pricing, no priced services or a record created before totals were captured."
          },
          "reservation_pricing": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ReservationPricing"
              },
              {
                "type": "null"
              }
            ],
            "description": "Details of the captured price. Null when no price was captured."
          },
          "payment": {
            "$ref": "#/components/schemas/PaymentAvailability"
          }
        }
      },
      "Customer": {
        "type": "object",
        "required": [
          "id",
          "name",
          "phone",
          "email",
          "marketing_consent",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Primary phone."
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "description": "Primary email."
          },
          "marketing_consent": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "GuestListEntry": {
        "type": "object",
        "required": [
          "id",
          "guest_list_id",
          "guest_list_name",
          "location_id",
          "location_name",
          "event_starts_at",
          "event_ends_at",
          "visit_date",
          "status",
          "previous_status",
          "status_label",
          "customer_id",
          "customer_name",
          "phone",
          "email",
          "party_size",
          "category",
          "tags",
          "notes",
          "showed_at",
          "no_show_at",
          "custom_fields",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "guest_list_id": {
            "type": "string",
            "format": "uuid"
          },
          "guest_list_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "location_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "location_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "event_starts_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "event_ends_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "visit_date": {
            "type": "string",
            "format": "date"
          },
          "status": {
            "type": "string",
            "enum": [
              "confirmed",
              "show",
              "no_show"
            ],
            "description": "`show` = checked in."
          },
          "previous_status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "confirmed",
              "show",
              "no_show",
              null
            ],
            "description": "Status before the change on `guest_list_entry.status_changed`; null elsewhere."
          },
          "status_label": {
            "type": "string",
            "enum": [
              "Confirmed",
              "Showed",
              "No-show"
            ]
          },
          "customer_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "customer_name": {
            "type": "string"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "party_size": {
            "type": "integer",
            "minimum": 1,
            "maximum": 1000
          },
          "category": {
            "type": [
              "string",
              "null"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "showed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "no_show_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "custom_fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomFieldValue"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "service_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "ID when exactly one service was selected; otherwise null."
          },
          "service_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Name when exactly one service was selected; otherwise null."
          },
          "service_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "services": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BookingService"
            },
            "description": "Selected services ordered by name and ID; empty for bookings without services. Includes services archived after they were selected. Catalog details reflect their current values."
          },
          "selected_services": {
            "type": "string",
            "description": "Email-ready text containing all selected service names separated by commas, in the same order as services. Empty string when no services are selected. Use this field for email templates regardless of selection count.",
            "example": "Consultation, Treatment"
          },
          "reservation_total_minor": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Captured price in currency minor units, from the selected services' prices when the record was created. Null when the price is unknown or the record was created before totals were captured; never a partial sum."
          },
          "reservation_total_currency": {
            "type": [
              "string",
              "null"
            ],
            "description": "Currency frozen in the reservation quote, never inferred from the current business default. Null when not calculated."
          },
          "reservation_total_formatted": {
            "type": "string",
            "description": "Email-ready reservation total with snapshot currency precision and ISO code. Empty when not calculated. Check reservation_total_status; do not replace an empty value with a catalog total.",
            "examples": [
              "110.70 EUR",
              "1234 JPY",
              "1.234 KWD",
              ""
            ]
          },
          "reservation_total_status": {
            "type": "string",
            "enum": [
              "not_calculated",
              "complete"
            ],
            "description": "complete includes an explicit zero/free price. not_calculated means unknown pricing, no priced services or a record created before totals were captured."
          },
          "reservation_pricing": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ReservationPricing"
              },
              {
                "type": "null"
              }
            ],
            "description": "Details of the captured price. Null when no price was captured."
          },
          "payment": {
            "$ref": "#/components/schemas/PaymentAvailability"
          }
        }
      },
      "CreateWaitlistEntryRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "waitlist_id"
        ],
        "description": "Follows the list's own field settings, like a staff entry: fields the list does not ask for are ignored, and fields it requires must be present (`400 <field>: This waitlist requires it`). Pick-up order lists ask for no party size.",
        "properties": {
          "waitlist_id": {
            "type": "string",
            "format": "uuid",
            "description": "The waitlist or pick-up order list (from `GET /waitlists`)."
          },
          "customer_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 101,
            "description": "The guest's name, as staff may save it: trimmed, 1-101 characters, Unicode letters (with combining marks) in words separated by single spaces, apostrophes (' or ’) or hyphens. Must start and end with a letter. Pattern (Unicode): `^\\p{L}[\\p{L}\\p{M}]*(?:[ '’-]\\p{L}[\\p{L}\\p{M}]*)*$`.",
            "example": "Marta Silveira"
          },
          "phone": {
            "type": "string",
            "maxLength": 40,
            "description": "Phone number in international format (e.g. `+351912345678`); trimmed. Normalised by WaitQ; a number that cannot be normalised answers `400` (`phone: Use international format, e.g. +351912345678`). Ignored when the list does not ask for a phone."
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 254,
            "description": "Trimmed and lowercased."
          },
          "party_size": {
            "description": "A whole number from 1 to 500, or numeric text (automation tools often send text). Then checked against the list's own party size range. Defaults to 1 when the list asks for a party size.",
            "oneOf": [
              {
                "type": "integer",
                "minimum": 1,
                "maximum": 500
              },
              {
                "type": "string",
                "pattern": "^\\s*[0-9]+(\\.0*)?\\s*$",
                "description": "Numeric text, coerced to a number."
              }
            ]
          },
          "marketing_consent": {
            "type": "boolean",
            "default": false,
            "description": "Counts only on lists that ask for marketing consent; required (true) when the list requires it, and needs a phone or email."
          },
          "order_number": {
            "type": "string",
            "maxLength": 40,
            "description": "Pick-up order lists with manual order numbers only (required there): 1-6 letters or digits, unique (case-insensitive) among orders still waiting or ready, else `409`. Ignored on other lists."
          },
          "custom_fields": {
            "type": "object",
            "description": "Answers to the list's custom fields, keyed by field id (UUID, from the list's `/fields` endpoint). At most 100 keys. Answers are checked with the dashboard's staff rules (type, options, required-for-staff); problems answer `400` with `error.details` as `[{ field_id, label, message }]`. Accepted values: text for `short_text` (up to 200 characters) and `long_text` (up to 5000); a number or numeric string for `number`; `YYYY-MM-DD` for `date` (a date-time's date part is used); an option id or option label (case-insensitive) for `single_select`; an array of option ids/labels, or one comma-separated string, for `multi_select` (a label that itself contains commas is matched whole); `true`/`false` or the strings `true`/`false`/`yes`/`no`/`1`/`0` for `checkbox`. Answers to archived fields, and archived options, are silently dropped. On plans without custom fields the list has no custom fields.",
            "maxProperties": 100,
            "propertyNames": {
              "format": "uuid"
            },
            "additionalProperties": true,
            "example": {
              "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71": "Terrace",
              "6cf4b6d8-0a2c-4e4f-9b7d-9f1b3d5f7b82": "yes"
            }
          }
        }
      },
      "CreateBookingRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "booking_list_id",
          "starts_at"
        ],
        "description": "Creates a `confirmed` booking with the same slot and capacity checks as staff bookings. Follows the list's own field settings; required fields must be present.",
        "properties": {
          "booking_list_id": {
            "type": "string",
            "format": "uuid",
            "description": "The booking list (from `GET /booking-lists`)."
          },
          "starts_at": {
            "type": "string",
            "description": "Start time. With an offset or `Z` (`2026-10-23T14:30:00+01:00`) it is that instant; without one (`2026-10-23 14:30` or `2026-10-23T14:30[:ss]`) it is the clock time at the booking list's location. Must be an open slot for the party size (see availability), else `409 unavailable`. A local time the clocks skip answers `400`.",
            "examples": [
              "2026-10-30 19:30",
              "2026-10-30T19:30:00+00:00"
            ]
          },
          "customer_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 101,
            "description": "The guest's name, as staff may save it: trimmed, 1-101 characters, Unicode letters (with combining marks) in words separated by single spaces, apostrophes (' or ’) or hyphens. Must start and end with a letter. Pattern (Unicode): `^\\p{L}[\\p{L}\\p{M}]*(?:[ '’-]\\p{L}[\\p{L}\\p{M}]*)*$`.",
            "example": "Marta Silveira"
          },
          "phone": {
            "type": "string",
            "maxLength": 40,
            "description": "Phone number in international format (e.g. `+351912345678`); trimmed. Normalised by WaitQ; a number that cannot be normalised answers `400` (`phone: Use international format, e.g. +351912345678`). Ignored when the list does not ask for a phone."
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 254,
            "description": "Trimmed and lowercased."
          },
          "party_size": {
            "description": "A whole number from 1 to 500, or numeric text (automation tools often send text). Then checked against the list's own party size range. Defaults to 1; always 1 when the list does not ask for a party size.",
            "oneOf": [
              {
                "type": "integer",
                "minimum": 1,
                "maximum": 500
              },
              {
                "type": "string",
                "pattern": "^\\s*[0-9]+(\\.0*)?\\s*$",
                "description": "Numeric text, coerced to a number."
              }
            ]
          },
          "notes": {
            "type": "string",
            "maxLength": 1000,
            "description": "Trimmed."
          },
          "marketing_consent": {
            "type": "boolean",
            "default": false,
            "description": "As for waitlist entries."
          },
          "custom_fields": {
            "type": "object",
            "description": "Answers to the list's custom fields, keyed by field id (UUID, from the list's `/fields` endpoint). At most 100 keys. Answers are checked with the dashboard's staff rules (type, options, required-for-staff); problems answer `400` with `error.details` as `[{ field_id, label, message }]`. Accepted values: text for `short_text` (up to 200 characters) and `long_text` (up to 5000); a number or numeric string for `number`; `YYYY-MM-DD` for `date` (a date-time's date part is used); an option id or option label (case-insensitive) for `single_select`; an array of option ids/labels, or one comma-separated string, for `multi_select` (a label that itself contains commas is matched whole); `true`/`false` or the strings `true`/`false`/`yes`/`no`/`1`/`0` for `checkbox`. Answers to archived fields, and archived options, are silently dropped. On plans without custom fields the list has no custom fields.",
            "maxProperties": 100,
            "propertyNames": {
              "format": "uuid"
            },
            "additionalProperties": true,
            "example": {
              "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71": "Terrace",
              "6cf4b6d8-0a2c-4e4f-9b7d-9f1b3d5f7b82": "yes"
            }
          },
          "service_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "maxItems": 100,
            "description": "Selected service IDs. Required when services are enabled; choose active services assigned to the list, without duplicates, within its selection cap and eight hours total. Omit for lists without services."
          }
        }
      },
      "CreateGuestListEntryRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "guest_list_id",
          "customer_name"
        ],
        "description": "Adds one guest for one date, as staff would. Phone, email, party size and notes count unless the list turns them off; category and tags only when the list turns them on. A guest already on the list for that day (same phone, or same email) answers `409`. Blocked with `403` during an Access Pass's post-event period.",
        "properties": {
          "guest_list_id": {
            "type": "string",
            "format": "uuid",
            "description": "The guest list (from `GET /guest-lists`)."
          },
          "visit_date": {
            "type": "string",
            "description": "`YYYY-MM-DD`, or a date-time whose date part is used as written. Defaults to today at the list's location. Must be from 365 days before to 730 days after today there.",
            "examples": [
              "2026-10-30"
            ]
          },
          "customer_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 101,
            "description": "The guest's name, as staff may save it: trimmed, 1-101 characters, Unicode letters (with combining marks) in words separated by single spaces, apostrophes (' or ’) or hyphens. Must start and end with a letter. Pattern (Unicode): `^\\p{L}[\\p{L}\\p{M}]*(?:[ '’-]\\p{L}[\\p{L}\\p{M}]*)*$`.",
            "example": "Marta Silveira"
          },
          "phone": {
            "type": "string",
            "maxLength": 40,
            "description": "Phone number in international format (e.g. `+351912345678`); trimmed. Normalised by WaitQ; a number that cannot be normalised answers `400` (`phone: Use international format, e.g. +351912345678`). Ignored when the list does not ask for a phone."
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 320,
            "description": "Trimmed and lowercased."
          },
          "party_size": {
            "description": "A whole number from 1 to 1000, or numeric text (automation tools often send text). Then checked against the list's own party size range. Then within the list's range (default 1-50). Stored as 1 when omitted.",
            "oneOf": [
              {
                "type": "integer",
                "minimum": 1,
                "maximum": 1000
              },
              {
                "type": "string",
                "pattern": "^\\s*[0-9]+(\\.0*)?\\s*$",
                "description": "Numeric text, coerced to a number."
              }
            ]
          },
          "category": {
            "type": "string",
            "maxLength": 40,
            "description": "Trimmed."
          },
          "tags": {
            "description": "A list of tags, or one string split on `,`, `;` or `|`. Trimmed and de-duplicated; at most 20 tags of up to 40 characters each.",
            "oneOf": [
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              {
                "type": "string"
              }
            ]
          },
          "notes": {
            "type": "string",
            "maxLength": 5000,
            "description": "Trimmed."
          },
          "custom_fields": {
            "type": "object",
            "description": "Answers to the list's custom fields, keyed by field id (UUID, from the list's `/fields` endpoint). At most 100 keys. Answers are checked with the dashboard's staff rules (type, options, required-for-staff); problems answer `400` with `error.details` as `[{ field_id, label, message }]`. Accepted values: text for `short_text` (up to 200 characters) and `long_text` (up to 5000); a number or numeric string for `number`; `YYYY-MM-DD` for `date` (a date-time's date part is used); an option id or option label (case-insensitive) for `single_select`; an array of option ids/labels, or one comma-separated string, for `multi_select` (a label that itself contains commas is matched whole); `true`/`false` or the strings `true`/`false`/`yes`/`no`/`1`/`0` for `checkbox`. Answers to archived fields, and archived options, are silently dropped. On plans without custom fields the list has no custom fields.",
            "maxProperties": 100,
            "propertyNames": {
              "format": "uuid"
            },
            "additionalProperties": true,
            "example": {
              "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71": "Terrace",
              "6cf4b6d8-0a2c-4e4f-9b7d-9f1b3d5f7b82": "yes"
            }
          }
        }
      },
      "SubscribeRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "event",
          "target_url"
        ],
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "waitlist_entry.created",
              "waitlist_entry.status_changed",
              "booking.created",
              "booking.confirmed",
              "customer.created",
              "guest_list_entry.created",
              "guest_list_entry.status_changed"
            ]
          },
          "target_url": {
            "type": "string",
            "format": "uri",
            "minLength": 1,
            "maxLength": 2048,
            "description": "Where deliveries are POSTed. Must be `https`, on the default port, without credentials, with a public hostname (not an IP address, not `localhost` or `.local`/`.internal`/`.lan`/`.home.arpa`-style names) that exists and resolves only to public addresses. Checked again on every delivery."
          },
          "location_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Only send events of this location (one of the business's, else `404`). Ignored for `customer.created`."
          }
        }
      },
      "Subscription": {
        "type": "object",
        "required": [
          "id",
          "event",
          "target_url",
          "location_id",
          "signing_secret",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "event": {
            "type": "string",
            "enum": [
              "waitlist_entry.created",
              "waitlist_entry.status_changed",
              "booking.created",
              "booking.confirmed",
              "customer.created",
              "guest_list_entry.created",
              "guest_list_entry.status_changed"
            ]
          },
          "target_url": {
            "type": "string",
            "format": "uri",
            "description": "As stored (normalised URL)."
          },
          "location_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "signing_secret": {
            "type": "string",
            "description": "`whsec_` plus 43 URL-safe base64 characters. Used to verify `X-WaitQ-Signature`."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "UnsubscribeResult": {
        "type": "object",
        "required": [
          "id",
          "deleted"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "deleted": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "EventEnvelope": {
        "type": "object",
        "required": [
          "event_id",
          "event_type",
          "occurred_at"
        ],
        "properties": {
          "event_id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique per event (same value as the `X-WaitQ-Event-Id` header). In samples it is the record's id."
          },
          "event_type": {
            "type": "string",
            "enum": [
              "waitlist_entry.created",
              "waitlist_entry.status_changed",
              "booking.created",
              "booking.confirmed",
              "customer.created",
              "guest_list_entry.created",
              "guest_list_entry.status_changed"
            ]
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the event happened. In samples: the record's `created_at` (created events) or `updated_at` (status events)."
          }
        }
      },
      "WaitlistEntryEvent": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WaitlistEntry"
          },
          {
            "$ref": "#/components/schemas/EventEnvelope"
          }
        ]
      },
      "BookingEvent": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Booking"
          },
          {
            "$ref": "#/components/schemas/EventEnvelope"
          }
        ]
      },
      "CustomerEvent": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Customer"
          },
          {
            "$ref": "#/components/schemas/EventEnvelope"
          }
        ]
      },
      "GuestListEntryEvent": {
        "allOf": [
          {
            "$ref": "#/components/schemas/GuestListEntry"
          },
          {
            "$ref": "#/components/schemas/EventEnvelope"
          }
        ]
      },
      "BookingService": {
        "type": "object",
        "required": [
          "id",
          "name",
          "duration_minutes",
          "price_cents",
          "currency"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "duration_minutes": {
            "type": "integer"
          },
          "price_cents": {
            "type": [
              "integer",
              "null"
            ],
            "description": "The service's current price, in minor currency units. Null when no price is set. For what the customer was quoted, use the record's `reservation_total_*`."
          },
          "currency": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "BookingServiceSelection": {
        "type": "object",
        "required": [
          "enabled",
          "max_selections",
          "options"
        ],
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "max_selections": {
            "type": "integer",
            "minimum": 1
          },
          "options": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BookingService"
            },
            "description": "Active services assigned to this business and booking list."
          }
        }
      },
      "ReservationPricing": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "source",
          "quote_version",
          "currency_exponent",
          "subtotal_minor",
          "tax_minor",
          "amount_due_minor",
          "quoted_balance_due_minor"
        ],
        "properties": {
          "source": {
            "type": "string",
            "const": "reservation_snapshot"
          },
          "quote_version": {
            "type": "integer",
            "const": 2
          },
          "currency_exponent": {
            "type": "integer",
            "minimum": 0,
            "maximum": 3
          },
          "subtotal_minor": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Reserved for future use; currently always `null`."
          },
          "tax_minor": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Reserved for future use; currently always `null`."
          },
          "amount_due_minor": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Reserved for future use; currently always `null`."
          },
          "quoted_balance_due_minor": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Reserved for future use; currently always `null`."
          }
        }
      },
      "PaymentAvailability": {
        "type": "object",
        "additionalProperties": true,
        "required": [
          "availability"
        ],
        "properties": {
          "availability": {
            "type": "string",
            "examples": [
              "not_available"
            ],
            "description": "Reserved for future use; currently always `not_available`. Other values may be added later."
          }
        },
        "description": "Reserved for future use."
      },
      "OrderListSummary": {
        "type": "object",
        "required": [
          "id",
          "name",
          "location_id",
          "kind",
          "order_numbers",
          "accepts_api_creates",
          "requires_service_selection",
          "max_service_selections"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "location_id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "pickup_orders"
            ],
            "description": "Always `pickup_orders` for order lists."
          },
          "order_numbers": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "automatic",
              "manual",
              null
            ],
            "description": "How a pick-up order list numbers orders; `null` on ordinary waitlists."
          },
          "accepts_api_creates": {
            "type": "boolean",
            "description": "`true` when `POST /orders` accepts orders for this list, including lists with services. The form's requirements and service limits still apply."
          },
          "public_form_path": {
            "type": [
              "string",
              "null"
            ],
            "description": "Relative public order form path, /order/{display_token}. Use it for customer links and QR codes. This is a public list token, not the private list ID.",
            "example": "/order/00000000-0000-4000-8000-000000000001"
          },
          "requires_service_selection": {
            "type": "boolean",
            "description": "Whether creation requires a non-empty service_ids selection. Discover options through /order-lists/{id}/fields."
          },
          "max_service_selections": {
            "type": "integer",
            "minimum": 1,
            "description": "Configured maximum number of selected services."
          }
        }
      },
      "OrderListFields": {
        "allOf": [
          {
            "type": "object",
            "required": [
              "kind",
              "order_numbers",
              "service_selection"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "pickup_orders"
                ]
              },
              "order_numbers": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "automatic",
                  "manual",
                  null
                ]
              },
              "service_selection": {
                "$ref": "#/components/schemas/OrderServiceSelection"
              }
            }
          },
          {
            "$ref": "#/components/schemas/ListFields"
          }
        ]
      },
      "CreateOrderRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "order_list_id"
        ],
        "description": "Follows the order list form returned by GET /order-lists/{id}/fields. Manual lists require order_number; automatic lists assign it. The customer isn't notified. The list's contact and custom-field requirements apply. Service-enabled lists accept service_ids, validated against their service_selection options and limits.",
        "properties": {
          "customer_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 101,
            "description": "The guest's name, as staff may save it: trimmed, 1-101 characters, Unicode letters (with combining marks) in words separated by single spaces, apostrophes (' or ’) or hyphens. Must start and end with a letter. Pattern (Unicode): `^\\p{L}[\\p{L}\\p{M}]*(?:[ '’-]\\p{L}[\\p{L}\\p{M}]*)*$`.",
            "example": "Marta Silveira"
          },
          "phone": {
            "type": "string",
            "maxLength": 40,
            "description": "Phone number in international format (e.g. `+351912345678`); trimmed. Normalised by WaitQ; a number that cannot be normalised answers `400` (`phone: Use international format, e.g. +351912345678`). Ignored when the list does not ask for a phone."
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 254,
            "description": "Trimmed and lowercased."
          },
          "party_size": {
            "description": "A whole number from 1 to 500, or numeric text (automation tools often send text). Then checked against the list's own party size range. Defaults to 1 when the list asks for a party size.",
            "oneOf": [
              {
                "type": "integer",
                "minimum": 1,
                "maximum": 500
              },
              {
                "type": "string",
                "pattern": "^\\s*[0-9]+(\\.0*)?\\s*$",
                "description": "Numeric text, coerced to a number."
              }
            ]
          },
          "marketing_consent": {
            "type": "boolean",
            "default": false,
            "description": "Counts only on lists that ask for marketing consent; required (true) when the list requires it, and needs a phone or email."
          },
          "order_number": {
            "type": "string",
            "maxLength": 40,
            "description": "Pick-up order lists with manual order numbers only (required there): 1-6 letters or digits, unique (case-insensitive) among orders still waiting or ready, else `409`. Ignored on other lists."
          },
          "custom_fields": {
            "type": "object",
            "description": "Answers to the list's custom fields, keyed by field id (UUID, from the list's `/fields` endpoint). At most 100 keys. Answers are checked with the dashboard's staff rules (type, options, required-for-staff); problems answer `400` with `error.details` as `[{ field_id, label, message }]`. Accepted values: text for `short_text` (up to 200 characters) and `long_text` (up to 5000); a number or numeric string for `number`; `YYYY-MM-DD` for `date` (a date-time's date part is used); an option id or option label (case-insensitive) for `single_select`; an array of option ids/labels, or one comma-separated string, for `multi_select` (a label that itself contains commas is matched whole); `true`/`false` or the strings `true`/`false`/`yes`/`no`/`1`/`0` for `checkbox`. Answers to archived fields, and archived options, are silently dropped. On plans without custom fields the list has no custom fields.",
            "maxProperties": 100,
            "propertyNames": {
              "format": "uuid"
            },
            "additionalProperties": true,
            "example": {
              "5be3a5c7-9f1b-4d3e-8a6c-8e0a2c4e6a71": "Terrace",
              "6cf4b6d8-0a2c-4e4f-9b7d-9f1b3d5f7b82": "yes"
            }
          },
          "order_list_id": {
            "type": "string",
            "format": "uuid",
            "description": "An order list returned by GET /order-lists. Queue lists are rejected with 404."
          },
          "service_ids": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Selected active service IDs from GET /order-lists/{id}/fields. Required and non-empty when service_selection.enabled is true; omit or send [] when disabled. IDs must be unique and available to this business and list. The configured max_selections and maximum combined duration of 480 minutes are enforced. Prices and the saved reservation total are calculated on the server; no caller-supplied amount is accepted."
          }
        }
      },
      "Order": {
        "type": "object",
        "description": "A pick-up order.",
        "required": [
          "id",
          "order_list_id",
          "order_list_name",
          "location_id",
          "location_name",
          "status",
          "previous_status",
          "customer_id",
          "customer_name",
          "phone",
          "email",
          "marketing_consent",
          "party_size",
          "order_number",
          "order_number_is_automatic",
          "list_kind",
          "status_label",
          "display_number",
          "custom_fields",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "location_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "location_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "waiting",
              "ready",
              "picked_up",
              "cancelled",
              "not_picked_up",
              "archived"
            ],
            "description": "Order status: `waiting`, `ready`, `picked_up`, `cancelled`, `not_picked_up` or `archived`. In `waitlist_entry.*` webhooks, orders use the queue statuses `notified` (ready), `seated` (picked up) and `no_show` (not picked up)."
          },
          "previous_status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "waiting",
              "ready",
              "picked_up",
              "cancelled",
              "not_picked_up",
              "archived",
              null
            ],
            "description": "Status before the latest change. Order status: `waiting`, `ready`, `picked_up`, `cancelled`, `not_picked_up` or `archived`. In `waitlist_entry.*` webhooks, orders use the queue statuses `notified` (ready), `seated` (picked up) and `no_show` (not picked up)."
          },
          "customer_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "customer_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "marketing_consent": {
            "type": "boolean",
            "description": "The linked customer's marketing consent (false without a customer)."
          },
          "party_size": {
            "type": [
              "integer",
              "null"
            ]
          },
          "order_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "order_number_is_automatic": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "list_kind": {
            "type": "string",
            "enum": [
              "pickup_orders"
            ]
          },
          "status_label": {
            "type": [
              "string",
              "null"
            ],
            "description": "Status in the list's own words. Waitlists: Waiting, Called, Checked in, Cancelled, No-show, Archived. Pick-up lists: Waiting, Ready for pickup, Picked up, Cancelled, Not picked up, Archived."
          },
          "display_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "The number guests see: the ticket or generated order number with the list's one-letter prefix, or a manual order number as typed."
          },
          "custom_fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomFieldValue"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "service_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "ID when exactly one service was selected; otherwise null."
          },
          "service_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Name when exactly one service was selected; otherwise null."
          },
          "service_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "services": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BookingService"
            },
            "description": "Selected services ordered by name and ID; empty for bookings without services. Includes services archived after they were selected. Catalog details reflect their current values."
          },
          "selected_services": {
            "type": "string",
            "description": "Email-ready text containing all selected service names separated by commas, in the same order as services. Empty string when no services are selected. Use this field for email templates regardless of selection count.",
            "example": "Consultation, Treatment"
          },
          "reservation_total_minor": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Captured price in currency minor units, from the selected services' prices when the record was created. Null when the price is unknown or the record was created before totals were captured; never a partial sum."
          },
          "reservation_total_currency": {
            "type": [
              "string",
              "null"
            ],
            "description": "Currency frozen in the reservation quote, never inferred from the current business default. Null when not calculated."
          },
          "reservation_total_formatted": {
            "type": "string",
            "description": "Email-ready reservation total with snapshot currency precision and ISO code. Empty when not calculated. Check reservation_total_status; do not replace an empty value with a catalog total.",
            "examples": [
              "110.70 EUR",
              "1234 JPY",
              "1.234 KWD",
              ""
            ]
          },
          "reservation_total_status": {
            "type": "string",
            "enum": [
              "not_calculated",
              "complete"
            ],
            "description": "complete includes an explicit zero/free price. not_calculated means unknown pricing, no priced services or a record created before totals were captured."
          },
          "reservation_pricing": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ReservationPricing"
              },
              {
                "type": "null"
              }
            ],
            "description": "Details of the captured price. Null when no price was captured."
          },
          "payment": {
            "$ref": "#/components/schemas/PaymentAvailability"
          },
          "order_list_id": {
            "type": "string",
            "format": "uuid"
          },
          "order_list_name": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "OrderServiceSelection": {
        "type": "object",
        "required": [
          "enabled",
          "max_selections",
          "options"
        ],
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "max_selections": {
            "type": "integer",
            "minimum": 1
          },
          "options": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BookingService"
            },
            "description": "Active services available to this business and order list."
          }
        }
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Makes the create safe to retry; see Idempotency in the API description. Scoped to the API key.",
        "schema": {
          "type": "string",
          "maxLength": 255
        },
        "example": "order-58213-create"
      },
      "IdempotencyWindow": {
        "name": "Idempotency-Window",
        "in": "header",
        "required": false,
        "description": "How long (whole seconds) the Idempotency-Key answers with its first result: at least 600, at most 86400 (the default). Only used with `Idempotency-Key`.",
        "schema": {
          "type": "string",
          "pattern": "^[0-9]+$"
        },
        "example": "900"
      },
      "LocationFilter": {
        "name": "location_id",
        "in": "query",
        "required": false,
        "description": "Only lists of this location. Must be a UUID if present (empty is ignored).",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      }
    },
    "headers": {
      "CacheControl": {
        "description": "Always `private, no-store`.",
        "schema": {
          "type": "string",
          "const": "private, no-store"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "IdempotentReplayed": {
        "description": "`true` when this is the stored response of an earlier request with the same `Idempotency-Key`.",
        "schema": {
          "type": "string",
          "const": "true"
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, malformed, unknown or revoked API key.",
        "headers": {
          "Cache-Control": {
            "$ref": "#/components/headers/CacheControl"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "unauthorized": {
                "value": {
                  "error": {
                    "code": "unauthorized",
                    "message": "Missing or invalid API key"
                  }
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The business's plan does not include integrations (`plan_required`), or the module this endpoint needs (`forbidden`).",
        "headers": {
          "Cache-Control": {
            "$ref": "#/components/headers/CacheControl"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "plan_required": {
                "value": {
                  "error": {
                    "code": "plan_required",
                    "message": "Integrations require a WaitQ Business plan."
                  }
                }
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests for this key, or too many invalid keys from this address.",
        "headers": {
          "Cache-Control": {
            "$ref": "#/components/headers/CacheControl"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "per_key": {
                "value": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Try again shortly."
                  }
                }
              },
              "failed_auth": {
                "value": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many invalid API keys from this address. Try again shortly."
                  }
                }
              }
            }
          }
        }
      },
      "CreateRateLimited": {
        "description": "Too many requests for this key, too many invalid keys from this address, or the business's create limit (60/minute, 5,000/UTC day across its keys) was reached.",
        "headers": {
          "Cache-Control": {
            "$ref": "#/components/headers/CacheControl"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "per_key": {
                "value": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Too many requests. Try again shortly."
                  }
                }
              },
              "business_per_minute": {
                "value": {
                  "error": {
                    "code": "rate_limited",
                    "message": "This business has reached the API limit of 60 creates per minute, across all its API keys. Try again shortly.",
                    "details": {
                      "limit": "business_creates_per_minute",
                      "max": 60
                    }
                  }
                }
              },
              "business_per_day": {
                "value": {
                  "error": {
                    "code": "rate_limited",
                    "message": "This business has reached the API limit of 5,000 creates per day (UTC), across all its API keys. Try again after midnight UTC.",
                    "details": {
                      "limit": "business_creates_per_day",
                      "max": 5000
                    }
                  }
                }
              }
            }
          }
        }
      },
      "InternalError": {
        "description": "Unexpected server error. For creates, the record may have been written: use an Idempotency-Key to retry safely.",
        "headers": {
          "Cache-Control": {
            "$ref": "#/components/headers/CacheControl"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "internal_error": {
                "value": {
                  "error": {
                    "code": "internal_error",
                    "message": "Something went wrong. Please try again."
                  }
                }
              }
            }
          }
        }
      },
      "IdempotencyConflict": {
        "description": "The Idempotency-Key was already used with a different request.",
        "headers": {
          "Cache-Control": {
            "$ref": "#/components/headers/CacheControl"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "idempotency_conflict": {
                "value": {
                  "error": {
                    "code": "idempotency_conflict",
                    "message": "This Idempotency-Key was already used with a different request"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
