{"openapi":"3.0.3","info":{"title":"Resos REST API","version":"1.2","description":"The Resos REST API allows restaurants and integration partners to manage\nbookings, customers, opening hours, seating areas, custom fields, and guest\nfeedback programmatically.\n\n**Base URL:** `https://api.resos.com/v1`\n\nAll requests **must** use HTTPS. Plain HTTP requests are rejected with\n`400 Request rejected - HTTPS only.`\n\n## Support\n\nFor questions or integration help, contact us at **hi@resos.com**.\n\n## Authentication\n\nThe API uses [HTTP Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication).\nYour API key is the **username** and the **password** is left **blank**.\n\n1. Get your API key from the Resos dashboard: **Settings → API integration**.\n2. Append a colon to the key (the password is empty): `YOUR_API_KEY:`\n3. Base64-encode the result: `base64(\"YOUR_API_KEY:\")` → `eW91ci1hcGkta2V5Og==`\n4. Send it in the `Authorization` header:\n\n```\nAuthorization: Basic eW91ci1hcGkta2V5Og==\n```\n\nMost HTTP clients handle this automatically when you set the username to\nyour API key and leave the password empty.\n\n**Example with cURL:**\n\n```bash\ncurl -u \"YOUR_API_KEY:\" https://api.resos.com/v1/healthcheck\n```\n\n## Rate Limiting\n\nEvery response includes rate-limit headers:\n\n| Header | Description |\n|---|---|\n| `X-RateLimit-Limit` | Maximum requests allowed per window (100) |\n| `X-RateLimit-Remaining` | Remaining requests in the current window |\n| `X-RateLimit-Reset` | ISO 8601 timestamp when the window resets |\n| `Retry-After` | Seconds until the next request is allowed |\n\nThe limit is **100 requests per 60 seconds per IP address**. When exceeded,\nthe API returns `429 Too Many Requests`.\n\n## Flexible Filter (`customQuery`)\n\nSeveral list endpoints accept a `customQuery` query parameter that lets you\nbuild ad-hoc MongoDB-style filters without a dedicated query parameter for\nevery field.\n\n### Syntax\n\nThe value is a **comma-separated** list of filter expressions. Each\nexpression has the form:\n\n```\nfield<operator>value\n```\n\n### Operators\n\n| Operator | Meaning | MongoDB equivalent |\n|---|---|---|\n| `:` | Equals | `$eq` |\n| `>` | Greater than | `$gt` |\n| `<` | Less than | `$lt` |\n| `;` | Contains (regex) | `$regex` |\n\n### Supported value types\n\n- **Strings** – the default type.\n- **Numbers** – numeric values (e.g. `2`, `3.5`) are detected and converted\n  automatically.\n- **Booleans** – `true` / `false` are converted automatically.\n- **Dates** – ISO 8601 datetime strings (e.g. `2026-02-01T00:00:00Z`) and\n  date-only strings (e.g. `2026-02-01`) are supported.\n\n### Examples\n\n| Filter | Meaning |\n|---|---|\n| `status:approved` | Bookings with status \"approved\" |\n| `people:4` | Bookings for 4 guests |\n| `status:approved,people:4` | Combine both |\n| `dateTime>2026-02-01T00:00:00Z` | Bookings after Feb 1 2026 |\n| `guest.phone:+15550100001` | Bookings by phone number |\n| `createdAt<2026-02-20` | Created before Feb 20 2026 |\n| `guest.name;Anders` | Guest name contains \"Anders\" |\n\n## Sort\n\nList endpoints that accept a `sort` parameter use this syntax:\n\n```\nfield:direction[,field2:direction]\n```\n\n- `direction` is `1` (ascending) or `-1` (descending).\n- Multiple fields are comma-separated.\n\n**Example:** `dateTime:-1,status:1`\n\n## Pagination\n\nList endpoints support `skip` and `limit` query parameters.\n\n- `skip` – number of records to skip (default `0`).\n- `limit` – number of records to return (default `100`, max `100`).\n\n## CORS\n\nRequests from browser-based frontend code will be blocked by the API's CORS\npolicy. This is intentional – since every request requires an API key,\ncalling the API directly from the frontend would expose your key in the\npage source, allowing anyone to read it and make requests on your behalf.\n\nThe correct approach is to call the API from your own backend server, which\nstores the API key securely and relays requests and responses to your\nfrontend.\n"},"servers":[{"url":"https://api.resos.com/v1"}],"security":[{"basicAuth":[]}],"tags":[{"name":"Bookings","description":"Create, read, update, and cancel restaurant bookings."},{"name":"Customers","description":"Read customer records."},{"name":"Custom Fields","description":"Read custom field definitions."},{"name":"Guest Feedback","description":"Read guest feedback / reviews."},{"name":"Tables","description":"Read seating areas with their tables and combinations."},{"name":"Opening Hours","description":"Read regular and special opening hours."},{"name":"Booking Flow","description":"Availability endpoints for building a booking flow – available dates,\ntimes, and tables.\n"},{"name":"Health Check","description":"Simple liveness / API key validation endpoint."}],"paths":{"/bookings":{"get":{"operationId":"listBookings","tags":["Bookings"],"summary":"List bookings","description":"Returns an array of bookings. Supports date-range filtering, pagination,\nsorting, the flexible filter, and a convenience flag to return only\nconfirmed bookings.\n","parameters":[{"$ref":"#/components/parameters/fromDateTime"},{"$ref":"#/components/parameters/toDateTime"},{"$ref":"#/components/parameters/skip"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/sort"},{"$ref":"#/components/parameters/customQuery"},{"name":"onlyConfirmed","in":"query","required":false,"description":"When `true`, only bookings with status `approved`, `arrived`,\n`seated`, or `left` are returned.\n","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Array of booking objects.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Booking"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}},"post":{"operationId":"createBooking","tags":["Bookings"],"summary":"Create a booking","description":"Creates a new booking.\n\nYou must provide either `date` + `time` (in the restaurant's local\ntimezone) **or** `dateTime` (UTC). If `dateTime` is provided it takes\nprecedence.\n\nThe API performs spam protection – if the same email, phone, or IP\ncreates more than the configured limit (default 10) of bookings within\n24 hours, the request may be silently throttled.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingCreate"},"examples":{"simple":{"summary":"Simple booking","value":{"people":2,"date":"2026-06-15","time":"19:00","guest":{"name":"Zana","email":"zana@example.com","phone":"+15550100001","notificationEmail":true,"notificationSms":true},"source":"api","languageCode":"en","status":"request","comment":"Window seat preferred"}},"withCustomFields":{"summary":"Booking with custom fields","value":{"date":"2026-09-18","time":"21:00","people":4,"duration":120,"tables":["NJioneCC7BoWC6j5x"],"customFields":[{"_id":"7A6MEXJXKvwKiHQQC","name":"Dietary requirements","label":"Dietary requirements","value":[{"_id":"JEL35LoHBN7hosrhv","name":"Vegetarian","value":true},{"_id":"2Xo6qRgXcnxsfwWHr","name":"Gluten free","value":true}]},{"_id":"Bzeb7ErdBAJuLWZQ5","name":"Occasion","label":"Occasion","value":"iSDmDNr56zHPMiueP","multipleChoiceValueName":"Birthday"},{"_id":"eGozPT7oKkds8Xgmo","name":"Seating preference","label":"Seating preference","value":"hDTYK8rZwk2KPYSqA","multipleChoiceValueName":"Window"},{"_id":"tqZhoWtva48cKPLkE","name":"Allergies","label":"Allergies","value":"Peanuts, shellfish"},{"_id":"wMWTRFQeZgdoDoKvX","name":"Number of highchairs","label":"Highchairs needed","value":"2"}],"guest":{"name":"Izaro","email":"izaro@example.com","phone":"+15550100002","notificationSms":false,"notificationEmail":true},"status":"request","source":"website","comment":"Celebrating a birthday, window table would be great","note":"VIP guest, ensure table is ready early","referrer":"https://www.some-website.com","languageCode":"en"}}}}}},"responses":{"200":{"description":"The created booking object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/bookings/{id}":{"get":{"operationId":"getBooking","tags":["Bookings"],"summary":"Get a booking by ID","parameters":[{"$ref":"#/components/parameters/resourceId"}],"responses":{"200":{"description":"The booking object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}},"put":{"operationId":"updateBooking","tags":["Bookings"],"summary":"Update a booking","description":"Updates an existing booking. Only include the fields you want to change.\n\n**Date/time handling:**\n- If you pass `dateTime`, the local `date` and `time` are derived\n  automatically from the restaurant's timezone.\n- If you pass `date` and/or `time`, the UTC `dateTime` is derived\n  automatically.\n- Avoid passing both `dateTime` and `date`/`time` simultaneously.\n\n**Table locking:** If a booking's tables have been locked\n(`tableLockedAt` is set), table changes are rejected.\n\nReturns `true` on success.\n","parameters":[{"$ref":"#/components/parameters/resourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingUpdate"},"example":{"dateTime":"2026-06-04 14:00","people":2,"duration":120,"guest":{"name":"Cassia","email":"cassia@example.com","phone":"+15550100003"},"metadata":{"metadata1":1},"status":"approved","source":"website"}}}},"responses":{"200":{"description":"`true` if the update succeeded.\n","content":{"application/json":{"schema":{"type":"boolean"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/bookings/{id}/cancel":{"put":{"operationId":"cancelBooking","tags":["Bookings"],"summary":"Cancel a booking","description":"Sets the booking status to `canceled` and records a `canceledAt`\ntimestamp.\n","parameters":[{"$ref":"#/components/parameters/resourceId"}],"responses":{"200":{"description":"The updated booking object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/bookings/{id}/comment":{"post":{"operationId":"addBookingComment","tags":["Bookings"],"summary":"Add a comment to a booking","description":"Adds a guest-facing or internal comment to a booking.\n\n**Limits:** Maximum 25 comments per booking, 5 per minute.\n","parameters":[{"$ref":"#/components/parameters/resourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text","role","sendNotification"],"properties":{"text":{"type":"string","minLength":1,"maxLength":2000,"description":"Comment text."},"role":{"type":"string","enum":["user","restaurant","system"],"description":"Who is posting the comment."},"sendNotification":{"type":"string","description":"Whether to send a notification to the guest. Passed as a\nJSON-encoded boolean string (`\"true\"` or `\"false\"`).\n","example":"true"},"eventType":{"type":"string","nullable":true,"description":"Optional event type for the comment.","enum":["statusNotification","approved","canceled","arrived","left","seated","message","declined","no_show","pending","warning","deleted","request","waitlist","confirm","table_locked","table_unlocked","closed"]}}}}}},"responses":{"200":{"description":"The result of adding the comment.","content":{"application/json":{"schema":{}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/bookings/{id}/restaurantNote":{"post":{"operationId":"addBookingRestaurantNote","tags":["Bookings"],"summary":"Add a restaurant note to a booking","description":"Adds an internal (restaurant-only) note to a booking. These notes are\nnot visible to the guest.\n","parameters":[{"$ref":"#/components/parameters/resourceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"text":{"type":"string","minLength":1,"maxLength":2000,"description":"Note text."}}}}}},"responses":{"200":{"description":"The result of adding the note.","content":{"application/json":{"schema":{}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/customers":{"get":{"operationId":"listCustomers","tags":["Customers"],"summary":"List customers","description":"Returns an array of customer records. Supports pagination and the\nflexible filter.\n","parameters":[{"$ref":"#/components/parameters/skip"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/customQuery"}],"responses":{"200":{"description":"Array of customer objects.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Customer"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/customers/{id}":{"get":{"operationId":"getCustomer","tags":["Customers"],"summary":"Get a customer by ID","parameters":[{"$ref":"#/components/parameters/resourceId"}],"responses":{"200":{"description":"The customer object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/customFields":{"get":{"operationId":"listCustomFields","tags":["Custom Fields"],"summary":"List custom fields","description":"Returns all custom field definitions.\n","responses":{"200":{"description":"Array of custom field objects.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CustomField"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/customFields/{id}":{"get":{"operationId":"getCustomField","tags":["Custom Fields"],"summary":"Get a custom field by ID","parameters":[{"$ref":"#/components/parameters/resourceId"}],"responses":{"200":{"description":"The custom field object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomField"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/guestFeedback":{"get":{"operationId":"listGuestFeedback","tags":["Guest Feedback"],"summary":"List guest feedback","description":"Returns an array of guest feedback entries. Supports date-range\nfiltering (by `booking.dateTime`), pagination, sorting, and the\nflexible filter.\n","parameters":[{"$ref":"#/components/parameters/fromDateTime"},{"$ref":"#/components/parameters/toDateTime"},{"$ref":"#/components/parameters/skip"},{"$ref":"#/components/parameters/limit"},{"$ref":"#/components/parameters/sort"},{"$ref":"#/components/parameters/customQuery"},{"name":"onlySubmitted","in":"query","required":false,"description":"When `true`, only feedback that the guest has actually submitted is\nreturned (excludes pending/unfinished feedback forms).\n","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Array of guest feedback objects.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GuestFeedback"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/guestFeedback/{id}":{"get":{"operationId":"getGuestFeedback","tags":["Guest Feedback"],"summary":"Get guest feedback by ID","parameters":[{"$ref":"#/components/parameters/resourceId"}],"responses":{"200":{"description":"The guest feedback object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestFeedback"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/tables":{"get":{"operationId":"listTables","tags":["Tables"],"summary":"List seating areas with tables","description":"Returns all seating areas, each containing its tables and table\ncombinations. Deleted tables and combinations are excluded.\n","responses":{"200":{"description":"Array of seating area objects.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SeatingArea"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/openingHours":{"get":{"operationId":"listOpeningHours","tags":["Opening Hours"],"summary":"List opening hours","description":"Returns all opening hours. Includes both regular weekly hours and\nspecial (date-specific) hours.\n","parameters":[{"name":"type","in":"query","required":false,"description":"Filter by opening hour mode (e.g. `restaurant`, `shop`).\n","schema":{"type":"string"}},{"name":"showDeleted","in":"query","required":false,"description":"Include deleted opening hours.","schema":{"type":"boolean","default":false}},{"name":"onlySpecial","in":"query","required":false,"description":"Return only special (date-specific) opening hours.","schema":{"type":"boolean","default":false}},{"$ref":"#/components/parameters/sort"}],"responses":{"200":{"description":"Array of opening hour objects.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OpeningHour"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/openingHours/{id}":{"get":{"operationId":"getOpeningHour","tags":["Opening Hours"],"summary":"Get an opening hour by ID","parameters":[{"$ref":"#/components/parameters/resourceId"}],"responses":{"200":{"description":"The opening hour object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpeningHour"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/bookingFlow/dates":{"get":{"operationId":"getAvailableDates","tags":["Booking Flow"],"summary":"Get available dates","description":"Returns dates within a range that the restaurant is open for bookings.\nUseful for building a date picker in a booking flow.\n","parameters":[{"name":"fromDate","in":"query","required":false,"description":"Start date (inclusive).","schema":{"type":"string","format":"date","default":"today"},"example":"2026-03-01"},{"name":"toDate","in":"query","required":false,"description":"End date (inclusive). Defaults to 30 days after `fromDate`.\n","schema":{"type":"string","format":"date"},"example":"2026-03-31"}],"responses":{"200":{"description":"Array of date objects with availability information.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/bookingFlow/times":{"get":{"operationId":"getAvailableTimes","tags":["Booking Flow"],"summary":"Get available times for a date","description":"Returns available booking times for a given date and party size.\n","parameters":[{"name":"people","in":"query","required":true,"description":"Number of guests.","schema":{"type":"integer","minimum":1}},{"name":"date","in":"query","required":false,"description":"Date to check availability for.","schema":{"type":"string","format":"date","default":"today"},"example":"2026-03-15"},{"name":"areaId","in":"query","required":false,"description":"Restrict results to a specific seating area.","schema":{"type":"string"}},{"name":"onlyBookableOnline","in":"query","required":false,"description":"Only return times for areas that are bookable online.\n","schema":{"type":"boolean","default":true}}],"responses":{"200":{"description":"Array of available time objects.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/bookingFlow/availableTables":{"get":{"operationId":"getAvailableTables","tags":["Booking Flow"],"summary":"Get available tables for a time slot","description":"Returns specific tables and table combinations available for a given\nparty size and time window.\n","parameters":[{"name":"people","in":"query","required":true,"description":"Number of guests.","schema":{"type":"integer","minimum":1}},{"name":"fromDateTime","in":"query","required":true,"description":"Start of the time window (UTC, ISO 8601).","schema":{"type":"string","format":"date-time"},"example":"2026-03-15T18:00:00.000Z"},{"name":"toDateTime","in":"query","required":true,"description":"End of the time window (UTC, ISO 8601).","schema":{"type":"string","format":"date-time"},"example":"2026-03-15T20:00:00.000Z"},{"name":"onlyBookableOnline","in":"query","required":false,"description":"Only return tables in areas bookable online.","schema":{"type":"boolean","default":true}},{"name":"returnAllTables","in":"query","required":false,"description":"Return all tables regardless of availability (includes occupied\ntables).\n","schema":{"type":"boolean","default":false}},{"name":"excludeBookingId","in":"query","required":false,"description":"Exclude this booking from the availability calculation. Useful when\nre-assigning tables for an existing booking.\n","schema":{"type":"string"}},{"name":"areaId","in":"query","required":false,"description":"Restrict results to a specific seating area.","schema":{"type":"string"}}],"responses":{"200":{"description":"Array of available table/combination objects.","content":{"application/json":{"schema":{"type":"array","items":{"type":"object"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}},"/healthcheck":{"get":{"operationId":"healthCheck","tags":["Health Check"],"summary":"API health check","description":"Returns a simple status message and your restaurant ID. Useful for\nverifying that your API key works correctly.\n","responses":{"200":{"description":"API is healthy.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"Now we're cooking!"},"restaurantId":{"type":"string","example":"Jf8kuy3gjy5EmAjC4"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimitExceeded"}}}}},"components":{"securitySchemes":{"basicAuth":{"type":"http","scheme":"basic","description":"HTTP Basic Authentication. Use your API key as the **username** and\nleave the **password** empty. The `username:password` string is\nBase64-encoded and sent in the `Authorization` header.\n\n```\nAuthorization: Basic <base64(apiKey:)>\n```\n\nGet your API key from the Resos dashboard under\n**Settings → API integration**.\n\nFor help, contact **hi@resos.com**.\n"}},"parameters":{"resourceId":{"name":"id","in":"path","required":true,"description":"Resource ID.","schema":{"type":"string"}},"fromDateTime":{"name":"fromDateTime","in":"query","required":false,"description":"Start of the date range (inclusive). ISO 8601 format.\n","schema":{"type":"string","format":"date-time"},"example":"2026-01-01T00:00:00.000Z"},"toDateTime":{"name":"toDateTime","in":"query","required":false,"description":"End of the date range (inclusive). ISO 8601 format.\n","schema":{"type":"string","format":"date-time"},"example":"2026-01-31T23:59:59.999Z"},"skip":{"name":"skip","in":"query","required":false,"description":"Number of records to skip.","schema":{"type":"integer","minimum":0,"default":0}},"limit":{"name":"limit","in":"query","required":false,"description":"Number of records to return. Maximum 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":100}},"sort":{"name":"sort","in":"query","required":false,"description":"Sort specification. Format: `field:direction[,field2:direction]` where\ndirection is `1` (ascending) or `-1` (descending).\n","schema":{"type":"string"},"example":"dateTime:-1"},"customQuery":{"name":"customQuery","in":"query","required":false,"description":"Flexible filter expression. See the **Flexible Filter** section in the\nAPI description for syntax details.\n","schema":{"type":"string"},"example":"status:approved,people:4"}},"responses":{"BadRequest":{"description":"Bad Request – validation error, missing parameter, or malformed path.","content":{"text/plain":{"schema":{"type":"string"},"example":"Bad request: Field 'limit' is limited to max 100."}}},"Unauthorized":{"description":"Unauthorized – missing or invalid API key.","content":{"text/plain":{"schema":{"type":"string"},"example":"Unauthorized: API key could not be validated"}}},"NotFound":{"description":"Not Found – the requested resource does not exist.","content":{"text/plain":{"schema":{"type":"string"},"example":"Not Found"}}},"UnprocessableEntity":{"description":"Unprocessable Entity – validation error in the request body.\n","content":{"text/plain":{"schema":{"type":"string"},"example":"Match error: Expected string, got number"}}},"RateLimitExceeded":{"description":"Too Many Requests – rate limit exceeded (100 requests / 60 seconds).\n","headers":{"Retry-After":{"schema":{"type":"number"},"description":"Seconds to wait before retrying."},"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Maximum requests per window."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Remaining requests in window."},"X-RateLimit-Reset":{"schema":{"type":"string","format":"date-time"},"description":"When the window resets."}},"content":{"text/plain":{"schema":{"type":"string"},"example":"Too many requests! Please wait 42 seconds"}}}},"schemas":{"Booking":{"type":"object","description":"A restaurant booking / reservation.","properties":{"_id":{"type":"string","description":"Unique booking identifier.","example":"wR9y4kQmTPnHx6vZd"},"restaurantId":{"type":"string","description":"The restaurant this booking belongs to.","example":"Jf8kuy3gjy5EmAjC4"},"internalId":{"type":"string","format":"uuid","description":"System-generated UUID.","example":"a1b2c3d4-e5f6-7890-abcd-ef1234567890"},"date":{"type":"string","format":"date","description":"Local date in the restaurant's timezone (YYYY-MM-DD). The Resos\n\"day\" runs 05:00 → 05:00.\n","example":"2026-06-15"},"time":{"type":"string","description":"Local time in the restaurant's timezone (HH:mm).","example":"19:00"},"dateTime":{"type":"string","format":"date-time","description":"UTC date and time of the booking.","example":"2026-06-15T17:00:00.000Z"},"endDateTime":{"type":"string","format":"date-time","description":"UTC end time (dateTime + duration).","example":"2026-06-15T19:00:00.000Z"},"people":{"type":"integer","description":"Number of guests."},"status":{"$ref":"#/components/schemas/BookingStatus"},"source":{"$ref":"#/components/schemas/BookingSource"},"referrer":{"type":"string","nullable":true,"description":"Referrer URL or identifier.","example":"https://www.some-website.com"},"duration":{"type":"integer","description":"Duration in minutes.","example":120},"languageCode":{"$ref":"#/components/schemas/LanguageCode"},"guest":{"$ref":"#/components/schemas/Guest"},"customer":{"$ref":"#/components/schemas/EmbeddedCustomer"},"tables":{"type":"array","description":"Assigned tables.","items":{"$ref":"#/components/schemas/EmbeddedTable"}},"openingHourId":{"type":"string","nullable":true,"description":"The opening hour this booking falls under.","example":"G7hJkL9mNpQrStUvW"},"openingHourName":{"type":"string","nullable":true,"description":"Name of the matched opening hour.","example":"Dinner"},"comment":{"type":"string","nullable":true,"description":"Legacy guest comment field.","example":"Window seat preferred"},"comments":{"type":"array","description":"Conversation comments on the booking. Client-only comments are\nfiltered out.\n","items":{"$ref":"#/components/schemas/Comment"}},"restaurantNotes":{"type":"array","description":"Internal notes (deleted notes are filtered out).","items":{"$ref":"#/components/schemas/RestaurantNote"}},"metadata":{"type":"object","nullable":true,"additionalProperties":true,"description":"Arbitrary key-value metadata.","example":{"externalId":"12345","source_campaign":"summer2026"}},"customFields":{"type":"array","nullable":true,"description":"Custom field values for this booking.","items":{"type":"object","properties":{"_id":{"type":"string","description":"Custom field definition ID.","example":"tqZhoWtva48cKPLkE"},"value":{"description":"The value for this custom field (type varies).","example":"Peanuts, shellfish"}}}},"payment":{"$ref":"#/components/schemas/BookingPayment"},"notifications":{"type":"array","description":"Notification history. Filtered to exclude restaurant-sent and\nresent notifications.\n","items":{"$ref":"#/components/schemas/Notification"}},"restaurant":{"type":"object","description":"Embedded restaurant summary.","properties":{"name":{"type":"string","example":"The Siren's Galley"},"address":{"type":"string","example":"Oriath Main Street 42"},"slug":{"type":"string","example":"the-sirens-galley"},"email":{"type":"string","example":"info@example.com"},"phone":{"type":"string","example":"+15550100009"},"languageCode":{"type":"string","example":"en"}}},"google":{"type":"object","nullable":true,"description":"Google reservation data (if booked via Reserve with Google)."},"sendNotification":{"type":"boolean","description":"Whether notifications are sent for this booking."},"waitlist":{"type":"object","nullable":true,"description":"Waitlist data (present when status is `waitlist`).","properties":{"addedAt":{"type":"string","format":"date-time","example":"2026-06-15T16:30:00.000Z"},"preferredAreaId":{"type":"string","nullable":true,"example":"Xk9mNpQrStUvWxYz2"}}},"declineMessage":{"type":"string","nullable":true,"description":"Message provided if the booking was declined.","example":"Unfortunately we are fully booked at this time."},"tableLockedAt":{"type":"string","format":"date-time","nullable":true,"description":"When the table assignment was locked.","example":"2026-06-15T16:45:00.000Z"},"tableLockedBy":{"type":"string","nullable":true,"description":"User who locked the table assignment.","example":"rT5kP2mN8vXwYz3Qa"},"seenByRestaurant":{"description":"When the restaurant first saw this booking.","oneOf":[{"type":"string","format":"date-time"},{"type":"boolean"}],"nullable":true,"example":"2026-06-15T16:35:00.000Z"},"createdAt":{"type":"string","format":"date-time","example":"2026-06-14T10:23:45.000Z"},"createdBy":{"type":"string","description":"User ID or `system`.","example":"system"},"createdByUsername":{"type":"string","nullable":true,"example":"Niko"},"updatedAt":{"type":"string","format":"date-time","example":"2026-06-14T12:00:00.000Z"},"updatedBy":{"type":"string","nullable":true,"example":"rT5kP2mN8vXwYz3Qa"},"activityAt":{"type":"string","format":"date-time","description":"Timestamp of the last activity on this booking.","example":"2026-06-14T12:00:00.000Z"},"canceledAt":{"type":"string","format":"date-time","nullable":true},"deletedAt":{"type":"string","format":"date-time","nullable":true},"declinedAt":{"type":"string","format":"date-time","nullable":true}}},"BookingCreate":{"type":"object","description":"Request body for creating a booking.","required":["people","guest"],"properties":{"people":{"type":"integer","minimum":1,"description":"Number of guests."},"date":{"type":"string","format":"date","description":"Local date in the restaurant's timezone (YYYY-MM-DD). Required\nunless `dateTime` is provided.\n","example":"2026-06-15"},"time":{"type":"string","description":"Local time in the restaurant's timezone (HH:mm). Required unless\n`dateTime` is provided.\n","example":"19:00"},"dateTime":{"type":"string","description":"UTC datetime in `YYYY-MM-DD HH:mm` format. Alternative to\n`date` + `time`.\n","example":"2026-06-15 17:00"},"guest":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"Guest name.","example":"Izaro"},"email":{"type":"string","format":"email","description":"Guest email address.","example":"izaro@example.com"},"phone":{"type":"string","description":"Guest phone number with country code prefix.\n","example":"+15550100001"},"notificationSms":{"type":"boolean","description":"Send SMS notifications to the guest."},"notificationEmail":{"type":"boolean","description":"Send email notifications to the guest."},"receiveNewsletter":{"type":"boolean","description":"Subscribe the guest to the newsletter."},"termsAccepted":{"type":"boolean","description":"Whether the guest accepted terms and conditions."}}},"status":{"$ref":"#/components/schemas/BookingStatus"},"source":{"type":"string","description":"Booking source. Defaults to `other` if not provided.\n","default":"other","enum":["website","phone","walkin","email","message","other","google","facebook","instagram","api","import"]},"referrer":{"type":"string","description":"Referrer URL or identifier.","example":"https://www.some-website.com"},"languageCode":{"$ref":"#/components/schemas/LanguageCode"},"comment":{"type":"string","description":"Guest comment.","example":"Window seat preferred"},"note":{"type":"string","description":"Restaurant note.","example":"VIP guest, comp dessert"},"tables":{"type":"array","description":"Array of table IDs or table objects to assign.","items":{"oneOf":[{"type":"string"},{"type":"object"}]}},"areaId":{"type":"string","description":"Seating area ID. If provided without specific tables, the system\nwill auto-assign tables from this area.\n","example":"Xk9mNpQrStUvWxYz2"},"duration":{"type":"integer","description":"Booking duration in minutes.","example":120},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary key-value metadata.","example":{"externalId":"abc-123","source":"partner-app"}},"customFields":{"type":"array","description":"Custom field values to set on the booking. Each entry corresponds\nto one active custom field. The object shape varies by field type:\n\n**Text and number fields:**\n- `_id` – custom field ID (from the list endpoint)\n- `name` – internal name of the custom field\n- `label` – display label of the custom field\n- `value` – the value: any text string, or a numeric value\n\n**Single-choice fields (radio / dropdown):**\n- `_id` – custom field ID\n- `name` – internal name of the custom field\n- `label` – display label of the custom field\n- `value` – the ID of the selected choice (not the custom field ID)\n- `multipleChoiceValueName` – the name of the selected choice\n\n**Multiple-choice field (checkboxes):**\n- `_id` – custom field ID\n- `name` – internal name of the custom field\n- `label` – display label of the custom field\n- `value` – an array of choice objects, each with:\n  - `_id` – the ID of the choice\n  - `name` – the name of the choice\n  - `value` – `true` if checked, `false` if not\n","items":{"type":"object","required":["_id","name","label","value"],"properties":{"_id":{"type":"string","description":"Custom field ID (from the list endpoint)."},"name":{"type":"string","description":"Internal name of the custom field."},"label":{"type":"string","description":"Display label of the custom field."},"value":{"description":"The value. Type depends on the field type: a string for text\nfields, a number for number fields, a choice ID string for\nradio/dropdown fields, or an array of choice objects for\nmultiple-choice fields.\n","oneOf":[{"type":"string"},{"type":"number"},{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string","description":"Choice ID."},"name":{"type":"string","description":"Choice name."},"value":{"type":"boolean","description":"Whether this choice is checked."}}}}]},"multipleChoiceValueName":{"type":"string","description":"Name of the selected choice. Only used for single-choice\nfields (radio / dropdown).\n"}}}},"payment":{"type":"object","description":"Payment details for prepayment or no-show fee.","properties":{"active":{"type":"boolean"},"type":{"type":"string","enum":["prepayment","noshow","manual"]},"amount":{"type":"number","example":50},"gateway":{"type":"string","example":"stripe"},"currency":{"type":"string","example":"EUR"}}},"openingHourId":{"type":"string","description":"Opening hour ID to associate with this booking.","example":"G7hJkL9mNpQrStUvW"},"customer":{"type":"object","description":"Customer data to link or create."},"google":{"type":"object","description":"Google reservation data."}}},"BookingUpdate":{"type":"object","description":"Request body for updating a booking. Only include fields you want to\nchange. All fields are optional.\n","properties":{"date":{"type":"string","format":"date","description":"Local date (YYYY-MM-DD) in the restaurant's timezone.","example":"2026-06-20"},"time":{"type":"string","description":"Local time (HH:mm) in the restaurant's timezone.","example":"20:00"},"dateTime":{"type":"string","description":"UTC datetime in `YYYY-MM-DD HH:mm` format.","example":"2026-06-20 18:00"},"people":{"type":"integer","minimum":1,"description":"Number of guests."},"tables":{"type":"array","description":"Array of table IDs to assign.","items":{"type":"string"},"example":["NJioneCC7BoWC6j5x"]},"duration":{"type":"integer","description":"Duration in minutes.","example":90},"status":{"$ref":"#/components/schemas/BookingStatus"},"source":{"$ref":"#/components/schemas/BookingSource"},"referrer":{"type":"string","example":"https://www.some-website.com"},"languageCode":{"$ref":"#/components/schemas/LanguageCode"},"metadata":{"type":"object","additionalProperties":true,"example":{"externalId":"12345"}},"openingHourId":{"type":"string","example":"G7hJkL9mNpQrStUvW"},"openingHourName":{"type":"string","example":"Dinner"},"canceledAt":{"type":"string","format":"date-time","example":"2026-06-20T15:00:00.000Z"},"deletedAt":{"type":"string","format":"date-time","example":"2026-06-20T15:00:00.000Z"},"declinedAt":{"type":"string","format":"date-time","example":"2026-06-20T15:00:00.000Z"},"customer":{"type":"object","description":"Customer data to update."},"guest":{"type":"object","properties":{"name":{"type":"string","example":"Cassia"},"email":{"type":"string","format":"email","example":"cassia@example.com"},"phone":{"type":"string","example":"+15550100003"}}},"customFields":{"type":"array","items":{"type":"object","properties":{"_id":{"type":"string","example":"tqZhoWtva48cKPLkE"},"value":{"description":"Custom field value.","example":"Peanuts"}}}}}},"BookingStatus":{"type":"string","enum":["request","declined","approved","arrived","seated","left","no_show","canceled","deleted","waitlist","confirm","closed"],"description":"Booking lifecycle status.\n\n| Status | Description |\n|---|---|\n| `request` | Pending approval by the restaurant |\n| `approved` | Confirmed by the restaurant |\n| `confirm` | Awaiting guest confirmation |\n| `waitlist` | On the waitlist |\n| `arrived` | Guest has arrived |\n| `seated` | Guest is seated |\n| `left` | Guest has left |\n| `no_show` | Guest did not show up |\n| `canceled` | Canceled |\n| `declined` | Declined by the restaurant |\n| `deleted` | Soft-deleted |\n| `closed` | Closed |\n"},"BookingSource":{"type":"string","enum":["website","phone","walkin","email","message","other","google","facebook","instagram","api","import"],"description":"Where the booking originated."},"LanguageCode":{"type":"string","enum":["da","de","el","en","es","fi","fr","it","nl","no","pl","pt","sq","sv","uk"],"description":"ISO language code. Supported languages: Danish (da), German (de),\nGreek (el), English (en), Spanish (es), Finnish (fi), French (fr),\nItalian (it), Dutch (nl), Norwegian (no), Polish (pl), Portuguese (pt),\nAlbanian (sq), Swedish (sv), Ukrainian (uk).\n"},"Guest":{"type":"object","description":"Guest contact information embedded in a booking.","properties":{"name":{"type":"string","example":"Alva"},"email":{"type":"string","format":"email","example":"alva@example.com"},"phone":{"type":"string","description":"Phone with country code prefix.","example":"+15550100004"},"notificationSms":{"type":"boolean"},"notificationEmail":{"type":"boolean"},"receiveNewsletter":{"type":"boolean"},"termsAccepted":{"type":"boolean"}}},"EmbeddedCustomer":{"type":"object","description":"Customer summary embedded in a booking.","properties":{"_id":{"type":"string","example":"pQ3rS7tUvWxYz1Ab2"},"name":{"type":"string","example":"Sirus"},"tags":{"type":"array","items":{"type":"string"},"example":["VIP","regular"]},"note":{"type":"string","nullable":true,"example":"Prefers corner table"},"isFlagged":{"type":"boolean"},"flagNote":{"type":"string","nullable":true},"flaggedAt":{"type":"string","format":"date-time","nullable":true},"totalBookings":{"type":"integer"},"latestBooking":{"type":"object","nullable":true,"properties":{"_id":{"type":"string","example":"wR9y4kQmTPnHx6vZd"},"dateTime":{"type":"string","format":"date-time","example":"2026-06-10T17:00:00.000Z"}}}}},"EmbeddedTable":{"type":"object","description":"Table summary embedded in a booking.","properties":{"_id":{"type":"string","example":"NJioneCC7BoWC6j5x"},"name":{"type":"string","example":"Table 5"},"seatsMin":{"type":"integer"},"seatsMax":{"type":"integer"},"area":{"type":"object","properties":{"_id":{"type":"string","example":"Xk9mNpQrStUvWxYz2"},"name":{"type":"string","example":"Terrace"}}}}},"Comment":{"type":"object","description":"A comment on a booking.","properties":{"_id":{"type":"string","example":"cM4nO8pQrStUvWx1Y"},"text":{"type":"string","example":"We look forward to seeing you!"},"role":{"type":"string","enum":["user","restaurant","system"],"example":"restaurant"},"eventType":{"type":"string","nullable":true,"example":"message"},"createdAt":{"type":"string","format":"date-time","example":"2026-06-14T11:00:00.000Z"}}},"RestaurantNote":{"type":"object","description":"An internal restaurant note on a booking.","properties":{"_id":{"type":"string","example":"nO8pQrStUvWx1Yz2A"},"text":{"type":"string","example":"Guest requested birthday cake"},"createdAt":{"type":"string","format":"date-time","example":"2026-06-14T11:05:00.000Z"},"createdBy":{"type":"string","example":"rT5kP2mN8vXwYz3Qa"}}},"BookingPayment":{"type":"object","nullable":true,"description":"Payment information for prepayment or no-show fees.","properties":{"active":{"type":"boolean","description":"Whether payment is active for this booking."},"type":{"type":"string","enum":["prepayment","noshow","manual"],"description":"Payment type."},"amount":{"type":"number","description":"Payment amount.","example":50},"status":{"type":"string","enum":["pending","settled","authorized","expired"],"description":"Payment status.","example":"authorized"},"gateway":{"type":"string","description":"Payment gateway (e.g. `stripe`).","example":"stripe"},"vatPercentage":{"type":"number","nullable":true,"example":25},"vatAmount":{"type":"number","nullable":true,"example":10},"currency":{"type":"string","description":"ISO currency code.","example":"DKK"},"cancelPolicyHours":{"type":"number","nullable":true,"description":"Hours before the booking when the cancellation policy applies.\n","example":24},"chargedNoShowFee":{"type":"boolean","nullable":true,"description":"Whether a no-show fee was charged."},"createdAt":{"type":"string","format":"date-time","example":"2026-06-14T10:23:45.000Z"},"expiresAt":{"type":"string","format":"date-time","nullable":true,"example":"2026-06-15T17:00:00.000Z"}}},"Notification":{"type":"object","description":"A notification sent for a booking.","properties":{"_id":{"type":"string","example":"qR5sT8uVwXyZ1aB2c"},"messageId":{"type":"string","nullable":true,"example":"SM1234567890abcdef"},"notificationId":{"type":"string","nullable":true,"example":"ntf_abc123"},"type":{"type":"string","description":"Notification channel (e.g. `sms`, `email`).","example":"email"},"status":{"type":"string","example":"sent"},"executeAt":{"type":"string","format":"date-time","nullable":true,"example":"2026-06-15T16:00:00.000Z"},"createdAt":{"type":"string","format":"date-time","example":"2026-06-14T10:24:00.000Z"},"errorCode":{"type":"string","nullable":true},"errorMessage":{"type":"string","nullable":true},"eventType":{"type":"string","nullable":true,"example":"approved"},"subject":{"type":"string","nullable":true,"example":"Your booking is confirmed"},"text":{"type":"string","nullable":true,"example":"Your booking for 4 guests on Jun 15 at 19:00 has been confirmed."},"commentId":{"type":"string","nullable":true},"trigger":{"type":"string","nullable":true,"example":"statusChange"},"resendOfNotificationId":{"type":"string","nullable":true},"reminder":{"type":"boolean","nullable":true}}},"Customer":{"type":"object","description":"A customer record linked to one or more bookings.","properties":{"_id":{"type":"string","example":"pQ3rS7tUvWxYz1Ab2"},"restaurantId":{"type":"string","example":"Jf8kuy3gjy5EmAjC4"},"name":{"type":"string","example":"Kirac"},"emails":{"type":"array","items":{"type":"string","format":"email"},"example":["kirac@example.com"]},"phones":{"type":"array","items":{"type":"string"},"example":["+15550100005"]},"address1":{"type":"string","nullable":true,"example":"Wraeclast Road 7"},"address2":{"type":"string","nullable":true},"postalCode":{"type":"string","nullable":true,"example":"1000"},"city":{"type":"string","nullable":true,"example":"Copenhagen"},"country":{"type":"string","nullable":true,"example":"Denmark"},"tags":{"type":"array","items":{"type":"string"},"example":["VIP","regular"]},"note":{"type":"string","nullable":true,"example":"Prefers quiet table"},"isFlagged":{"type":"boolean"},"flagNote":{"type":"string","nullable":true},"flaggedAt":{"type":"string","format":"date-time","nullable":true},"totalBookings":{"type":"integer"},"latestBooking":{"type":"object","nullable":true,"properties":{"_id":{"type":"string","example":"wR9y4kQmTPnHx6vZd"},"dateTime":{"type":"string","format":"date-time","example":"2026-06-10T17:00:00.000Z"}}},"newsletterConsent":{"type":"boolean","nullable":true},"newsletterIntegrationId":{"type":"string","nullable":true},"importedAt":{"type":"string","format":"date-time","nullable":true},"importedBy":{"type":"string","nullable":true},"deleted":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time","example":"2025-11-20T08:30:00.000Z"},"createdBy":{"type":"string","example":"system"},"updatedAt":{"type":"string","format":"date-time","example":"2026-06-10T17:00:00.000Z"},"updatedBy":{"type":"string","nullable":true,"example":"rT5kP2mN8vXwYz3Qa"}}},"CustomField":{"type":"object","description":"A custom field definition. Custom fields can be attached to bookings to\ncollect additional information (e.g. \"Occasion\", \"Allergies\").\n","properties":{"_id":{"type":"string","example":"Bzeb7ErdBAJuLWZQ5"},"restaurantId":{"type":"string","example":"Jf8kuy3gjy5EmAjC4"},"name":{"type":"string","description":"Internal name / key.","example":"occasion"},"label":{"type":"string","description":"Display label shown to guests.","example":"Occasion"},"type":{"type":"string","description":"Field type (e.g. `string`, `number`, `boolean`, `select`,\n`multiselect`, `date`).\n","example":"select"},"isRequired":{"type":"boolean","description":"Whether this field is required when booking."},"isInternal":{"type":"boolean","description":"Internal fields are only visible to restaurant staff, not guests.\n"},"showAsBadge":{"type":"boolean","description":"Show this field as a badge on the booking in the dashboard."},"badgeColor":{"type":"string","nullable":true,"description":"Badge color (hex or named color).","example":"blue"},"helptext":{"type":"string","nullable":true,"description":"Help text shown to the guest.","example":"Let us know if you are celebrating something special"},"activeFlows":{"type":"array","description":"Which flows use this field (e.g. `[\"booking\"]`).\n","items":{"type":"string"},"example":["booking"]},"multipleChoiceSelections":{"type":"array","nullable":true,"description":"Available options for `select` and `multiselect` field types.\n","items":{"type":"object","properties":{"_id":{"type":"string","example":"iSDmDNr56zHPMiueP"},"name":{"type":"string","example":"Birthday"}}}},"sortIndex":{"type":"integer","description":"Display order."},"defaultOnAllOpeningHours":{"type":"boolean","nullable":true,"description":"When true, this custom field is active on all opening hours by\ndefault.\n"},"createdAt":{"type":"string","format":"date-time","example":"2025-09-01T12:00:00.000Z"},"createdBy":{"type":"string","example":"rT5kP2mN8vXwYz3Qa"},"updatedAt":{"type":"string","format":"date-time","example":"2025-09-15T14:00:00.000Z"},"updatedBy":{"type":"string","nullable":true,"example":"rT5kP2mN8vXwYz3Qa"},"deleted":{"type":"boolean"}}},"GuestFeedback":{"type":"object","description":"A guest feedback / review entry associated with a booking.","properties":{"_id":{"type":"string","example":"fB3gH7jKlMnOpQr1S"},"restaurantId":{"type":"string","example":"Jf8kuy3gjy5EmAjC4"},"type":{"type":"string","description":"The feedback type.","example":"booking"},"customerId":{"type":"string","nullable":true,"description":"Linked customer ID.","example":"pQ3rS7tUvWxYz1Ab2"},"submitted":{"type":"boolean","description":"`true` once the guest has completed and submitted the feedback form.\n"},"recommended":{"type":"boolean","nullable":true,"description":"Whether the guest would recommend the restaurant."},"food":{"type":"integer","minimum":1,"maximum":5,"nullable":true,"description":"Food rating (1–5)."},"service":{"type":"integer","minimum":1,"maximum":5,"nullable":true,"description":"Service rating (1–5)."},"value":{"type":"integer","minimum":1,"maximum":5,"nullable":true,"description":"Value-for-money rating (1–5)."},"atmosphere":{"type":"integer","minimum":1,"maximum":5,"nullable":true,"description":"Atmosphere rating (1–5)."},"message":{"type":"string","nullable":true,"description":"Free-text feedback from the guest.","example":"Wonderful evening, the food was exceptional!"},"booking":{"type":"object","nullable":true,"description":"Embedded booking snapshot.","properties":{"_id":{"type":"string","example":"wR9y4kQmTPnHx6vZd"},"userId":{"type":"string","nullable":true},"people":{"type":"integer"},"dateTime":{"type":"string","format":"date-time","example":"2026-06-10T17:00:00.000Z"},"endDateTime":{"type":"string","format":"date-time","nullable":true,"example":"2026-06-10T19:00:00.000Z"},"guest":{"$ref":"#/components/schemas/Guest"},"tables":{"type":"array","items":{"$ref":"#/components/schemas/EmbeddedTable"}},"status":{"type":"string","example":"left"},"source":{"type":"string","example":"website"},"createdAt":{"type":"string","format":"date-time","example":"2026-06-09T08:00:00.000Z"}}},"createdAt":{"type":"string","format":"date-time","example":"2026-06-11T10:00:00.000Z"}}},"SeatingArea":{"type":"object","description":"A seating area in the restaurant (e.g. \"Terrace\", \"Main Hall\"). Contains\ntables and table combinations.\n","properties":{"_id":{"type":"string","example":"Xk9mNpQrStUvWxYz2"},"restaurantId":{"type":"string","example":"Jf8kuy3gjy5EmAjC4"},"name":{"type":"string","example":"Terrace"},"bookable":{"type":"boolean","description":"Whether the area accepts bookings."},"bookableOnline":{"type":"boolean","description":"Whether the area accepts online bookings."},"bookingPriority":{"type":"integer","description":"Priority for automatic table assignment. Lower = higher priority.\n"},"note":{"type":"string","nullable":true,"description":"Guest-facing note about this area.","example":"Outdoor seating, weather permitting"},"internalNote":{"type":"string","nullable":true,"description":"Internal note visible only to staff.","example":"Heaters available in storage room"},"tables":{"type":"array","description":"Tables in this area (deleted tables are excluded).","items":{"$ref":"#/components/schemas/Table"}},"combinations":{"type":"array","description":"Table combinations in this area (deleted combinations are excluded).\n","items":{"$ref":"#/components/schemas/TableCombination"}}}},"Table":{"type":"object","description":"A single table within a seating area.","properties":{"_id":{"type":"string","example":"NJioneCC7BoWC6j5x"},"name":{"type":"string","description":"Display name (e.g. \"Table 1\", \"A3\").","example":"Table 5"},"seatsMin":{"type":"integer","description":"Minimum number of seats."},"seatsMax":{"type":"integer","description":"Maximum number of seats."},"internalNote":{"type":"string","nullable":true,"example":"Near the window"}}},"TableCombination":{"type":"object","description":"A combination of tables that can be joined together for larger parties.\n","properties":{"_id":{"type":"string","example":"cD4eF8gHiJkLmNp1Q"},"name":{"type":"string","nullable":true,"example":"Tables 5+6"},"tableIds":{"type":"array","items":{"type":"string"},"description":"IDs of the tables in this combination.","example":["NJioneCC7BoWC6j5x","mK2lN5oP8qRsTuVw3"]},"seatsMin":{"type":"integer"},"seatsMax":{"type":"integer"}}},"OpeningHour":{"type":"object","description":"An opening hour rule. Regular hours have a `day` (1–7, Monday–Sunday).\nSpecial hours have `special: true` and a specific `date` (or date range\nwith `endDate`).\n","properties":{"_id":{"type":"string","example":"G7hJkL9mNpQrStUvW"},"restaurantId":{"type":"string","example":"Jf8kuy3gjy5EmAjC4"},"day":{"type":"integer","minimum":1,"maximum":7,"nullable":true,"description":"Day of week (1 = Monday, 7 = Sunday). `null` for special hours.\n","example":5},"date":{"type":"string","format":"date","nullable":true,"description":"Specific date for special opening hours.","example":"2026-12-24"},"endDate":{"type":"string","format":"date","nullable":true,"description":"End date for period-range special hours.","example":"2026-12-26"},"name":{"type":"string","nullable":true,"description":"Optional display name (e.g. \"Lunch\", \"Christmas Eve\").","example":"Dinner"},"isOpen":{"type":"boolean","description":"Whether the restaurant is open during this period."},"open":{"type":"integer","description":"Opening time in **minutes from midnight** (e.g. 1020 = 17:00).\n","example":1020},"close":{"type":"integer","description":"Closing time in **minutes from midnight** (e.g. 1380 = 23:00).\nCan exceed 1440 for after-midnight closing.\n","example":1380},"open_sec":{"type":"integer","nullable":true,"description":"Opening time in seconds from midnight (alternative format).","example":61200},"close_sec":{"type":"integer","nullable":true,"description":"Closing time in seconds from midnight (alternative format).","example":82800},"mode":{"type":"string","nullable":true,"description":"The mode this opening hour applies to (e.g. `restaurant`, `shop`).\n","example":"restaurant"},"special":{"type":"boolean","description":"Whether this is a special (date-specific) opening hour."},"range":{"type":"string","nullable":true,"enum":["single","interval","period"],"description":"Range type for special hours. `single` = one date, `interval` =\nrecurring on certain days, `period` = date range (`date` to\n`endDate`).\n","example":"single"},"maxBookings":{"type":"integer","nullable":true,"description":"Maximum number of bookings for the entire opening period."},"maxGuests":{"type":"integer","nullable":true,"description":"Maximum total guests for the entire opening period."},"maxBookingsInterval":{"type":"integer","nullable":true,"description":"Maximum bookings per seating interval."},"maxGuestsInterval":{"type":"integer","nullable":true,"description":"Maximum guests per seating interval."},"onlyCountOnlineBookings":{"type":"boolean","nullable":true,"description":"Only count online bookings toward the max limits.\n"},"note":{"type":"string","nullable":true,"description":"Note displayed to guests when booking this period.","example":"Last seating at 21:30"},"payment":{"type":"object","nullable":true,"description":"Payment settings for bookings during this opening hour.\n","properties":{"active":{"type":"boolean"},"type":{"type":"string","enum":["prepayment","noshow"]},"amount":{"type":"number","example":25},"amountPer":{"type":"string","enum":["per-booking","per-guest"],"description":"Whether the amount is per booking or per guest.","example":"per-guest"},"cancelPolicyHours":{"description":"Hours before booking when free cancellation expires.\n","oneOf":[{"type":"number"},{"type":"string"}],"example":24},"minGuests":{"type":"integer","nullable":true,"description":"Minimum party size for payment to apply.\n"}}},"noshow":{"type":"object","nullable":true,"description":"Legacy no-show payment configuration."},"seating":{"type":"object","nullable":true,"description":"Seating interval configuration.","properties":{"active":{"type":"boolean","description":"Whether seating intervals are active."},"interval":{"type":"integer","description":"Interval between seatings in minutes.","example":15},"duration":{"type":"integer","description":"Default booking duration in minutes.","example":120}}},"areas":{"type":"array","nullable":true,"description":"Seating area IDs this opening hour applies to. Only used when the\nrestaurant has per-area opening hours enabled.\n","items":{"type":"string"},"example":["Xk9mNpQrStUvWxYz2"]},"useWaitlist":{"type":"boolean","nullable":true,"description":"Whether the waitlist is enabled for this opening period."},"useDurationByPeople":{"type":"boolean","nullable":true,"description":"Whether to use party-size-based durations instead of a fixed\nduration.\n"},"durationByPeople":{"type":"array","nullable":true,"description":"Duration rules by party size. Only used when\n`useDurationByPeople` is `true`.\n","items":{"type":"object","properties":{"_id":{"type":"string","example":"dBp1xK7mNqRtYvW3z"},"min":{"type":"integer","description":"Minimum party size (inclusive).","example":1},"max":{"type":"integer","description":"Maximum party size (inclusive).","example":4},"duration":{"type":"integer","description":"Duration in minutes.","example":90}}}},"activeCustomFieldIds":{"type":"array","nullable":true,"description":"Custom field IDs that are active during this opening hour.\n","items":{"type":"string"},"example":["tqZhoWtva48cKPLkE"]},"deleted":{"type":"boolean","nullable":true},"createdAt":{"type":"string","format":"date-time","example":"2026-01-10T09:00:00.000Z"},"createdBy":{"type":"string","nullable":true,"example":"rT5kP2mN8vXwYz3Qa"},"deletedAt":{"type":"string","format":"date-time","nullable":true},"deletedBy":{"type":"string","nullable":true}}}}}}