{
  "openapi": "3.1.0",
  "info": {
    "title": "TableFai partner API",
    "version": "1.0.0",
    "summary": "Availability, holds, bookings and webhooks of a TableFai restaurant.",
    "description": "TableFai's partner API lets a restaurant's own systems — its developer's or agency's website, a POS, an AI agent —\nread the venue's availability and seating, hold tables, book for a guest, read and sync the venue's bookings and cancel\nthem, with the same engine and rules as tablefai.com. There is no commission and no per-booking fee.\n\n**Authentication.** Every call except this document sends `Authorization: Bearer tf_live_…`: a key the venue's owner\nor admin makes under *Venue → Website & links → API keys*. A key belongs to one venue (the venue every call works on),\nis shown once when it is made and can be revoked at any time (then 401). TableFai keeps only its SHA-256.\n\n**Scopes.** `availability:read` (availability, seating), `bookings:read` (the venue's bookings), `bookings:write`\n(holds, bookings, cancellations). Every key reads its venue. A call outside the key's scopes is 403 `scope` with the\nscope it needs in `required`.\n\n**Limits.** 120 requests a minute per key; above it 429 `rate_limited` with `Retry-After` (seconds). Every answer\ncarries `X-RateLimit-Limit` and `X-RateLimit-Remaining`.\n\n**Plan.** The partner API and webhooks are part of TableFai's Pro plan. While the venue's company is on Start,\nevery call answers 402 `plan_required` (`feature: api_keys`, `plan: pro`); the key is kept and works again with Pro.\nWhile the company's subscription is paused or canceled, `GET venue` and `GET bookings…` still answer and every other\ncall is 402 `subscription_inactive` (`status`).\n\n**Answers.** JSON in `{\"data\": …}` (a page adds `meta`); errors are `{\"error\": {\"code\": …}}` with the HTTP status,\nvalidation errors `{\"error\": {\"code\": \"validation\", \"fields\": {\"<field>\": [\"<rule>\"]}}}` (codes, never sentences).\nEvery answer has an `X-Request-Id`. Dates (`date`, `from`, `to`) and times (`time`, `HH:MM`) are the venue's, in its\ntimezone; a booking's moments (`starts_at`, `ends_at`) carry their offset; other timestamps are UTC (`…Z`). Texts the\nrestaurant writes come as `{text, locale}` in the language asked for with `?locale=` (en, sv), else the venue's.\n\n**Booking.** `GET availability` → `GET seating` (when the service lets guests choose) → `POST holds` (the tables are\nkept ten minutes) → `POST bookings` with the hold and the guest. `POST holds` and `POST bookings` take an\n`Idempotency-Key` (a UUID): the same key answers the same hold or booking again; another request under it is 422\n`idempotency_mismatch`. The venue must be published (else 409 `not_published`). The guest gets TableFai's confirmation\nmail with their manage link, as when they book on tablefai.com.\n\n**Webhooks.** The venue's owner or admin adds an https address under *Website & links → Webhooks* and gets its signing\nsecret (`whsec_…`) once. TableFai POSTs `booking.created`, `booking.updated` and `booking.cancelled` (and `ping`) —\nsee `webhooks` — with `TableFai-Event`, `TableFai-Delivery` and `TableFai-Signature: t=<unix seconds>,\nv1=<hex HMAC-SHA256 of \"<t>.<body>\" with the whole secret as the key>`. Check the signature over the raw body and\nrefuse a `t` more than five minutes away. Answer 2xx within 10 seconds; anything else is tried again after 1 min,\n5 min, 30 min, 2 h, 6 h and 24 h, then given up; a webhook failing for three days is turned off. Redirects are not\nfollowed. Deliveries may come more than once and out of order: use the event `id`, and the booking's `version` or\n`updated_at`.",
    "contact": {
      "name": "Tizfai Technologies AB",
      "url": "https://tablefai.com/developers"
    }
  },
  "servers": [
    {
      "url": "https://tablefai.com/api/partner/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "Venue"
    },
    {
      "name": "Availability"
    },
    {
      "name": "Holds"
    },
    {
      "name": "Bookings"
    },
    {
      "name": "Document"
    }
  ],
  "paths": {
    "/venue": {
      "get": {
        "operationId": "getVenue",
        "tags": [
          "Venue"
        ],
        "summary": "The key's venue",
        "description": "Its name, page, timezone, languages, party limits, running services with their seating modes, policies, and the key itself. Any scope.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Locale"
          }
        ],
        "responses": {
          "200": {
            "description": "The venue.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Venue"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/availability": {
      "get": {
        "operationId": "getAvailability",
        "tags": [
          "Availability"
        ],
        "summary": "A date's free times (availability:read)",
        "parameters": [
          {
            "$ref": "#/components/parameters/Date"
          },
          {
            "$ref": "#/components/parameters/Party"
          },
          {
            "$ref": "#/components/parameters/Locale"
          }
        ],
        "responses": {
          "200": {
            "description": "The date's services and slots.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Availability"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "409": {
            "$ref": "#/components/responses/NotPublished"
          },
          "422": {
            "$ref": "#/components/responses/Validation"
          }
        }
      }
    },
    "/seating": {
      "get": {
        "operationId": "getSeating",
        "tags": [
          "Availability"
        ],
        "summary": "The seating choice at a time (availability:read)",
        "parameters": [
          {
            "$ref": "#/components/parameters/Date"
          },
          {
            "$ref": "#/components/parameters/Time"
          },
          {
            "$ref": "#/components/parameters/Party"
          },
          {
            "$ref": "#/components/parameters/Locale"
          }
        ],
        "responses": {
          "200": {
            "description": "By the service's seating mode.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Seating"
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "409": {
            "$ref": "#/components/responses/NotPublished"
          },
          "422": {
            "$ref": "#/components/responses/Validation"
          }
        }
      }
    },
    "/holds": {
      "post": {
        "operationId": "createHold",
        "tags": [
          "Holds"
        ],
        "summary": "Keep tables ten minutes (bookings:write)",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/Locale"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HoldRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The hold.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "hold": {
                          "$ref": "#/components/schemas/Hold"
                        }
                      },
                      "required": [
                        "hold"
                      ],
                      "additionalProperties": false
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/Validation"
          }
        }
      }
    },
    "/holds/{id}": {
      "delete": {
        "operationId": "releaseHold",
        "tags": [
          "Holds"
        ],
        "summary": "Give a hold's tables back (bookings:write)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
              "examples": [
                "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
              ]
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Released (also when there was nothing to release)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/bookings": {
      "get": {
        "operationId": "listBookings",
        "tags": [
          "Bookings"
        ],
        "summary": "The venue's bookings (bookings:read)",
        "description": "50 a page. Without `updated_since` by start; with it, the bookings changed at or after it, in the order they changed (sync by the last `updated_at` you saw).",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "the first date (the venue's)",
            "schema": {
              "type": "string",
              "format": "date",
              "examples": [
                "2026-12-02"
              ]
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "the last date",
            "schema": {
              "type": "string",
              "format": "date",
              "examples": [
                "2026-12-02"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "one status or several, comma-separated",
            "schema": {
              "type": "string",
              "examples": [
                "confirmed,seated"
              ]
            }
          },
          {
            "name": "updated_since",
            "in": "query",
            "required": false,
            "description": "ISO 8601; without a zone UTC",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "$ref": "#/components/parameters/Locale"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of bookings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Booking"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/Meta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "422": {
            "$ref": "#/components/responses/Validation"
          }
        }
      },
      "post": {
        "operationId": "createBooking",
        "tags": [
          "Bookings"
        ],
        "summary": "Book a hold for a guest (bookings:write)",
        "description": "The hold becomes the booking in one transaction (confirmed, or requested in a service in request mode); source api. The guest is mailed the confirmation with their manage link.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/Locale"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The booking.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookingAnswer"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "410": {
            "description": "`hold_expired`: the hold ran out, was released or is not the venue's.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "409": {
            "description": "`not_published`: the venue's page is not published, so it takes no online bookings; `payment_required`: the hold's service takes a card guarantee, a deposit or a prepaid menu, which only TableFai's own booking pages take; `preorder_required`: the service needs a pre-order; or `verification_required`: the service asks a party of this size to verify with BankID (slice 8b), which only TableFai's own booking pages do.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/Validation"
          }
        }
      }
    },
    "/bookings/{reference}": {
      "get": {
        "operationId": "getBooking",
        "tags": [
          "Bookings"
        ],
        "summary": "One booking (bookings:read)",
        "parameters": [
          {
            "$ref": "#/components/parameters/Reference"
          },
          {
            "$ref": "#/components/parameters/Locale"
          }
        ],
        "responses": {
          "200": {
            "description": "The booking.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookingAnswer"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/bookings/{reference}/cancel": {
      "post": {
        "operationId": "cancelBooking",
        "tags": [
          "Bookings"
        ],
        "summary": "Cancel for the venue (bookings:write)",
        "description": "cancelled_venue, from requested or confirmed; a cancelled booking answers as it is. The guest is told by mail. With `version`, only at that version (409 stale).",
        "parameters": [
          {
            "$ref": "#/components/parameters/Reference"
          },
          {
            "$ref": "#/components/parameters/Locale"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "version": {
                    "type": "integer",
                    "minimum": 1
                  }
                },
                "required": [],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The booking, cancelled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookingAnswer"
                }
              }
            },
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimitRemaining"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenApi",
        "tags": [
          "Document"
        ],
        "summary": "This document",
        "security": [],
        "responses": {
          "200": {
            "description": "OpenAPI 3.1.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "booking.created": {
      "post": {
        "operationId": "webhookBookingCreated",
        "summary": "A booking was made (by staff, a guest or a partner)",
        "parameters": [
          {
            "name": "TableFai-Signature",
            "in": "header",
            "required": true,
            "description": "t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's secret>",
            "schema": {
              "type": "string",
              "examples": [
                "t=1764590400, v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
              ]
            }
          },
          {
            "name": "TableFai-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "booking.created",
                "booking.updated",
                "booking.cancelled",
                "ping"
              ]
            }
          },
          {
            "name": "TableFai-Delivery",
            "in": "header",
            "required": true,
            "description": "this delivery's id (an event goes once to each webhook, perhaps more than once)",
            "schema": {
              "type": "string",
              "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
              "examples": [
                "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Delivered. Anything else, or no answer within 10 seconds, is tried again."
          }
        }
      }
    },
    "booking.updated": {
      "post": {
        "operationId": "webhookBookingUpdated",
        "summary": "A booking changed (time, party, tables, status, the guest's running late …)",
        "parameters": [
          {
            "name": "TableFai-Signature",
            "in": "header",
            "required": true,
            "description": "t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's secret>",
            "schema": {
              "type": "string",
              "examples": [
                "t=1764590400, v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
              ]
            }
          },
          {
            "name": "TableFai-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "booking.created",
                "booking.updated",
                "booking.cancelled",
                "ping"
              ]
            }
          },
          {
            "name": "TableFai-Delivery",
            "in": "header",
            "required": true,
            "description": "this delivery's id (an event goes once to each webhook, perhaps more than once)",
            "schema": {
              "type": "string",
              "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
              "examples": [
                "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Delivered. Anything else, or no answer within 10 seconds, is tried again."
          }
        }
      }
    },
    "booking.cancelled": {
      "post": {
        "operationId": "webhookBookingCancelled",
        "summary": "A booking was cancelled (by the guest or the venue)",
        "parameters": [
          {
            "name": "TableFai-Signature",
            "in": "header",
            "required": true,
            "description": "t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's secret>",
            "schema": {
              "type": "string",
              "examples": [
                "t=1764590400, v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
              ]
            }
          },
          {
            "name": "TableFai-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "booking.created",
                "booking.updated",
                "booking.cancelled",
                "ping"
              ]
            }
          },
          {
            "name": "TableFai-Delivery",
            "in": "header",
            "required": true,
            "description": "this delivery's id (an event goes once to each webhook, perhaps more than once)",
            "schema": {
              "type": "string",
              "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
              "examples": [
                "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Delivered. Anything else, or no answer within 10 seconds, is tried again."
          }
        }
      }
    },
    "ping": {
      "post": {
        "operationId": "webhookPing",
        "summary": "A test from the venue's settings",
        "parameters": [
          {
            "name": "TableFai-Signature",
            "in": "header",
            "required": true,
            "description": "t=<unix seconds>, v1=<hex HMAC-SHA256 of \"<t>.<raw body>\" with the webhook's secret>",
            "schema": {
              "type": "string",
              "examples": [
                "t=1764590400, v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
              ]
            }
          },
          {
            "name": "TableFai-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "booking.created",
                "booking.updated",
                "booking.cancelled",
                "ping"
              ]
            }
          },
          {
            "name": "TableFai-Delivery",
            "in": "header",
            "required": true,
            "description": "this delivery's id (an event goes once to each webhook, perhaps more than once)",
            "schema": {
              "type": "string",
              "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
              "examples": [
                "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PingEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Delivered. Anything else, or no answer within 10 seconds, is tried again."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "tf_live_…",
        "description": "A key of the venue: tf_live_ and 43 characters."
      }
    },
    "schemas": {
      "Text": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string"
          },
          "locale": {
            "type": "string",
            "enum": [
              "en",
              "sv"
            ]
          }
        },
        "required": [
          "text",
          "locale"
        ],
        "additionalProperties": false,
        "description": "A text the restaurant wrote, in one language."
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "examples": [
                  "not_found"
                ]
              },
              "fields": {
                "type": "object",
                "additionalProperties": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "description": "validation: the rule codes per field"
              }
            },
            "required": [
              "code"
            ]
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false,
        "description": "An error: a stable code, and for some codes details beside it (fields, required, retry_after, alternatives, from, to)."
      },
      "Address": {
        "type": "object",
        "properties": {
          "street": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "postal_code": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "city": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "country": {
            "type": "string"
          }
        },
        "required": [
          "street",
          "postal_code",
          "city",
          "country"
        ],
        "additionalProperties": false
      },
      "PartyLimits": {
        "type": "object",
        "properties": {
          "min": {
            "type": "integer"
          },
          "max": {
            "type": "integer"
          }
        },
        "required": [
          "min",
          "max"
        ],
        "additionalProperties": false,
        "description": "The party sizes bookable online."
      },
      "Service": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
            ]
          },
          "name": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Text"
              },
              {
                "type": "null"
              }
            ]
          },
          "weekdays": {
            "type": "array",
            "items": {
              "type": "integer",
              "minimum": 1,
              "maximum": 7
            },
            "description": "ISO weekdays, Monday 1"
          },
          "starts": {
            "type": "string",
            "pattern": "^\\d{2}:\\d{2}$",
            "examples": [
              "19:00"
            ]
          },
          "ends": {
            "type": "string",
            "pattern": "^\\d{2}:\\d{2}$",
            "examples": [
              "19:00"
            ]
          },
          "last_seating": {
            "type": "string",
            "pattern": "^\\d{2}:\\d{2}$",
            "examples": [
              "19:00"
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "auto",
              "request"
            ],
            "description": "request: bookings wait for the restaurant (status requested)"
          },
          "seating": {
            "type": "string",
            "enum": [
              "none",
              "area",
              "type",
              "table"
            ],
            "description": "the seating choice a hold may make"
          }
        },
        "required": [
          "id",
          "name",
          "weekdays",
          "starts",
          "ends",
          "last_seating",
          "mode",
          "seating"
        ],
        "additionalProperties": false
      },
      "Venue": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
            ]
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "published": {
            "type": "boolean"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "examples": [
              "https://tablefai.com/r/bryggan"
            ]
          },
          "country": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2"
          },
          "timezone": {
            "type": "string",
            "examples": [
              "Europe/Stockholm"
            ]
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217"
          },
          "default_locale": {
            "type": "string"
          },
          "locales": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "address": {
            "$ref": "#/components/schemas/Address"
          },
          "phone": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "booking_phone": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "email": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "website": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "party_limits": {
            "$ref": "#/components/schemas/PartyLimits"
          },
          "services": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Service"
            }
          },
          "policies": {
            "type": "object",
            "properties": {
              "cancellation_hours": {
                "type": "integer"
              },
              "late_grace_minutes": {
                "type": "integer"
              },
              "no_show_fee_minor": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "deposit_minor": {
                "anyOf": [
                  {
                    "type": "integer"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "text": {
                "anyOf": [
                  {
                    "$ref": "#/components/schemas/Text"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "cancellation_hours",
              "late_grace_minutes",
              "no_show_fee_minor",
              "deposit_minor",
              "text"
            ],
            "additionalProperties": false
          },
          "key": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
                "examples": [
                  "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
                ]
              },
              "name": {
                "type": "string"
              },
              "prefix": {
                "type": "string",
                "description": "the key's first 12 characters"
              },
              "scopes": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "availability:read",
                    "bookings:read",
                    "bookings:write"
                  ]
                }
              }
            },
            "required": [
              "id",
              "name",
              "prefix",
              "scopes"
            ],
            "additionalProperties": false,
            "description": "The key this call was made with."
          }
        },
        "required": [
          "id",
          "name",
          "slug",
          "published",
          "url",
          "country",
          "timezone",
          "currency",
          "default_locale",
          "locales",
          "address",
          "phone",
          "booking_phone",
          "email",
          "website",
          "party_limits",
          "services",
          "policies",
          "key"
        ],
        "additionalProperties": false
      },
      "Slot": {
        "type": "object",
        "properties": {
          "time": {
            "type": "string",
            "pattern": "^\\d{2}:\\d{2}$",
            "examples": [
              "19:00"
            ]
          },
          "available": {
            "type": "boolean"
          },
          "offer": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Offer"
              },
              {
                "type": "null"
              }
            ],
            "description": "The best of the restaurant's own offers this time has (given at the till; payments are unaffected), or null."
          }
        },
        "required": [
          "time",
          "available",
          "offer"
        ],
        "additionalProperties": false
      },
      "Offer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
            ]
          },
          "title": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Text"
              },
              {
                "type": "null"
              }
            ]
          },
          "label": {
            "type": "string",
            "examples": [
              "−20 % on food",
              "−50 kr"
            ]
          }
        },
        "required": [
          "id",
          "title",
          "label"
        ],
        "additionalProperties": false,
        "description": "A restaurant's offer as a time shows it: its title and a label in the language asked for (a percent or an amount off, or the offer's own text)."
      },
      "NextSlot": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date",
            "examples": [
              "2026-12-02"
            ]
          },
          "time": {
            "type": "string",
            "pattern": "^\\d{2}:\\d{2}$",
            "examples": [
              "19:00"
            ]
          }
        },
        "required": [
          "date",
          "time"
        ],
        "additionalProperties": false
      },
      "Availability": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date",
            "examples": [
              "2026-12-02"
            ]
          },
          "party": {
            "type": "integer"
          },
          "party_limits": {
            "$ref": "#/components/schemas/PartyLimits"
          },
          "large_party": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "text": {
                    "anyOf": [
                      {
                        "$ref": "#/components/schemas/Text"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "phone": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "text",
                  "phone"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          },
          "services": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
                  "examples": [
                    "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
                  ]
                },
                "name": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/Text"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "slots": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Slot"
                  }
                },
                "preorder": {
                  "type": "string",
                  "enum": [
                    "off",
                    "optional",
                    "required"
                  ],
                  "description": "Whether the service takes pre-orders (slice 8b). A `required` one cannot be booked through this API: its holds answer 409 `preorder_required`."
                },
                "verification": {
                  "type": "string",
                  "enum": [
                    "none",
                    "bankid"
                  ],
                  "description": "Whether the service asks a party of this size to verify with BankID on TableFai's own pages (Verifierad gäst, slice 8b); such a party cannot be booked through this API: its holds answer 409 `verification_required`."
                },
                "waitlist": {
                  "type": "boolean",
                  "description": "Whether a guest with a party of this size may join the venue's online waitlist for this service on TableFai's own pages (slice 9a): its waitlist is on, the party is within its online sizes, and a service with a guarantee can take payments. This API takes no waitlist requests."
                }
              },
              "required": [
                "id",
                "name",
                "slots",
                "preorder",
                "verification",
                "waitlist"
              ],
              "additionalProperties": false
            }
          },
          "next": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NextSlot"
            },
            "description": "the next three free slots of the 30 days after, when the date has none"
          }
        },
        "required": [
          "date",
          "party",
          "party_limits",
          "large_party",
          "services",
          "next"
        ],
        "additionalProperties": false,
        "description": "The date's open services and their slots by the venue's online rules; nothing but whether a slot is free."
      },
      "Zone": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
            ]
          },
          "name": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Text"
              },
              {
                "type": "null"
              }
            ]
          },
          "color": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "color"
        ],
        "additionalProperties": false
      },
      "TableRef": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
            ]
          },
          "code": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "code"
        ],
        "additionalProperties": false
      },
      "Seating": {
        "description": "What a guest may choose at a date, time and party, by the service's seating mode.",
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "mode": {
                "const": "none"
              },
              "available": {
                "type": "boolean"
              }
            },
            "required": [
              "mode",
              "available"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "mode": {
                "const": "area"
              },
              "available": {
                "type": "boolean"
              },
              "options": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "zone": {
                      "$ref": "#/components/schemas/Zone"
                    },
                    "available": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "zone",
                    "available"
                  ],
                  "additionalProperties": false
                }
              },
              "recommended": {
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "zone": {
                        "$ref": "#/components/schemas/Zone"
                      }
                    },
                    "required": [
                      "zone"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "mode",
              "available",
              "options",
              "recommended"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "mode": {
                "const": "type"
              },
              "available": {
                "type": "boolean"
              },
              "options": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "tag": {
                      "type": "string",
                      "enum": [
                        "window",
                        "booth",
                        "sofa",
                        "quiet",
                        "view",
                        "outdoor",
                        "bar",
                        "high_top",
                        "private",
                        "accessible",
                        "high_chair",
                        "near_entrance"
                      ]
                    },
                    "available": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "tag",
                    "available"
                  ],
                  "additionalProperties": false
                }
              },
              "recommended": {
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "tag": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "tag"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "mode",
              "available",
              "options",
              "recommended"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "mode": {
                "const": "table"
              },
              "available": {
                "type": "boolean"
              },
              "floors": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
                      "examples": [
                        "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
                      ]
                    },
                    "name": {
                      "type": "string"
                    },
                    "width_cm": {
                      "type": "integer"
                    },
                    "height_cm": {
                      "type": "integer"
                    },
                    "layout": {
                      "type": "object",
                      "description": "the published floor plan, guest-safe (schema 1, centimetres)"
                    },
                    "tables": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
                            "examples": [
                              "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
                            ]
                          },
                          "code": {
                            "type": "string"
                          },
                          "state": {
                            "type": "string",
                            "enum": [
                              "free",
                              "taken",
                              "not_choosable"
                            ]
                          },
                          "seats_min": {
                            "type": "integer"
                          },
                          "seats_max": {
                            "type": "integer"
                          },
                          "tags": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "zone": {
                            "anyOf": [
                              {
                                "$ref": "#/components/schemas/Zone"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "code",
                          "state",
                          "seats_min",
                          "seats_max",
                          "tags",
                          "zone"
                        ],
                        "additionalProperties": false
                      }
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "width_cm",
                    "height_cm",
                    "layout",
                    "tables"
                  ],
                  "additionalProperties": false
                }
              },
              "recommended": {
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "table": {
                        "$ref": "#/components/schemas/TableRef"
                      }
                    },
                    "required": [
                      "table"
                    ],
                    "additionalProperties": false
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "mode",
              "available",
              "floors",
              "recommended"
            ],
            "additionalProperties": false
          }
        ]
      },
      "SeatingChoice": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "none",
              "area",
              "type",
              "table"
            ]
          },
          "zone": {
            "type": "string",
            "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
            ],
            "description": "area: the zone"
          },
          "tag": {
            "type": "string",
            "enum": [
              "window",
              "booth",
              "sofa",
              "quiet",
              "view",
              "outdoor",
              "bar",
              "high_top",
              "private",
              "accessible",
              "high_chair",
              "near_entrance"
            ],
            "description": "type: the kind of table"
          },
          "table": {
            "type": "string",
            "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
            ],
            "description": "table: the table"
          }
        },
        "required": [
          "mode"
        ],
        "additionalProperties": false,
        "description": "none lets the restaurant choose; another mode must be the service's own."
      },
      "HoldRequest": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date",
            "examples": [
              "2026-12-02"
            ]
          },
          "time": {
            "type": "string",
            "pattern": "^\\d{2}:\\d{2}$",
            "examples": [
              "19:00"
            ],
            "description": "on the 15-minute grid"
          },
          "party": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50
          },
          "seating": {
            "$ref": "#/components/schemas/SeatingChoice"
          }
        },
        "required": [
          "date",
          "time",
          "party"
        ],
        "additionalProperties": false
      },
      "Hold": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
            ]
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "ten minutes after it was made"
          },
          "table": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/TableRef"
              },
              {
                "type": "null"
              }
            ]
          },
          "zone": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Zone"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "expires_at",
          "table",
          "zone"
        ],
        "additionalProperties": false
      },
      "BookingRequest": {
        "type": "object",
        "properties": {
          "hold": {
            "type": "string",
            "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
            ]
          },
          "guest": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "maxLength": 120
              },
              "phone": {
                "type": "string",
                "maxLength": 40,
                "description": "E.164, or a national number of the venue's country"
              },
              "email": {
                "type": "string",
                "format": "email",
                "maxLength": 190
              },
              "locale": {
                "anyOf": [
                  {
                    "type": "string",
                    "enum": [
                      "en",
                      "sv"
                    ]
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "name",
              "phone",
              "email"
            ],
            "additionalProperties": false
          },
          "occasion": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "birthday",
                  "anniversary",
                  "date_night",
                  "business",
                  "celebration",
                  "other"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "requests": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 2000
              },
              {
                "type": "null"
              }
            ]
          },
          "allergies": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 2000
              },
              {
                "type": "null"
              }
            ]
          },
          "accessibility": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "wheelchair",
                    "step_free",
                    "walking_aid",
                    "hearing",
                    "vision",
                    "service_dog",
                    "high_chair",
                    "pram"
                  ]
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "consent": {
            "type": "object",
            "properties": {
              "terms": {
                "const": true,
                "description": "the guest accepted the booking terms and the venue's policy"
              },
              "marketing": {
                "type": "boolean"
              }
            },
            "required": [
              "terms"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "hold",
          "guest",
          "consent"
        ],
        "additionalProperties": false,
        "description": "The public booking's body; the source is always api."
      },
      "Booking": {
        "type": "object",
        "properties": {
          "reference": {
            "type": "string",
            "pattern": "^TF-[0-9A-HJKMNP-TV-Z]{5}$",
            "examples": [
              "TF-7K3QD"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "requested",
              "confirmed",
              "waitlisted",
              "arrived",
              "seated",
              "finished",
              "no_show",
              "cancelled_guest",
              "cancelled_venue"
            ]
          },
          "date": {
            "type": "string",
            "format": "date",
            "examples": [
              "2026-12-02"
            ],
            "description": "the service's date (a booking after midnight belongs to the evening before)"
          },
          "time": {
            "type": "string",
            "pattern": "^\\d{2}:\\d{2}$",
            "examples": [
              "19:00"
            ]
          },
          "end": {
            "type": "string",
            "pattern": "^\\d{2}:\\d{2}$",
            "examples": [
              "19:00"
            ]
          },
          "minutes": {
            "type": "integer"
          },
          "party": {
            "type": "integer"
          },
          "starts_at": {
            "type": "string",
            "format": "date-time",
            "description": "with the venue's offset"
          },
          "ends_at": {
            "type": "string",
            "format": "date-time"
          },
          "service": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
                    "examples": [
                      "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
                    ]
                  },
                  "name": {
                    "anyOf": [
                      {
                        "$ref": "#/components/schemas/Text"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "id",
                  "name"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          },
          "tables": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TableRef"
            }
          },
          "seating": {
            "type": "object",
            "description": "the guest's seating choice {mode, zone?, tag?, table?}"
          },
          "source": {
            "type": "string",
            "enum": [
              "widget",
              "site_link",
              "qr",
              "instagram",
              "facebook",
              "google",
              "email",
              "marketplace",
              "api",
              "phone",
              "walk_in",
              "staff",
              "app",
              "import"
            ]
          },
          "source_origin": {
            "anyOf": [
              {
                "type": "string",
                "description": "the embedding page's scheme and host (widget bookings)"
              },
              {
                "type": "null"
              }
            ]
          },
          "utm": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "source": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "medium": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "campaign": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "source",
                  "medium",
                  "campaign"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          },
          "guest": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "phone": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "email": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "name",
                  "phone",
                  "email"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          },
          "locale": {
            "type": "string"
          },
          "occasion": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "birthday",
                  "anniversary",
                  "date_night",
                  "business",
                  "celebration",
                  "other"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "accessibility": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "wheelchair",
                "step_free",
                "walking_aid",
                "hearing",
                "vision",
                "service_dog",
                "high_chair",
                "pram"
              ]
            }
          },
          "requests": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "allergies": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "confirmed_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "description": "the guest's \"I'm coming\""
              },
              {
                "type": "null"
              }
            ]
          },
          "late_minutes": {
            "anyOf": [
              {
                "type": "integer",
                "description": "the guest is running late"
              },
              {
                "type": "null"
              }
            ]
          },
          "cancelled_late": {
            "type": "boolean",
            "description": "cancelled by the guest inside the cancellation window"
          },
          "status_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "version": {
            "type": "integer",
            "description": "grows with every change"
          }
        },
        "required": [
          "reference",
          "status",
          "date",
          "time",
          "end",
          "minutes",
          "party",
          "starts_at",
          "ends_at",
          "service",
          "tables",
          "seating",
          "source",
          "source_origin",
          "utm",
          "guest",
          "locale",
          "occasion",
          "accessibility",
          "requests",
          "allergies",
          "confirmed_at",
          "late_minutes",
          "cancelled_late",
          "status_at",
          "created_at",
          "updated_at",
          "version"
        ],
        "additionalProperties": false,
        "description": "A booking as the venue sees it, without its staff's own notes and tags."
      },
      "BookingAnswer": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "booking": {
                "$ref": "#/components/schemas/Booking"
              }
            },
            "required": [
              "booking"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "data"
        ],
        "additionalProperties": false
      },
      "Meta": {
        "type": "object",
        "properties": {
          "page": {
            "type": "integer"
          },
          "pages": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          }
        },
        "required": [
          "page",
          "pages",
          "total"
        ],
        "additionalProperties": false
      },
      "EventVenue": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
            ]
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "slug",
          "name"
        ],
        "additionalProperties": false
      },
      "BookingEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
            ],
            "description": "the event's id: the same for every webhook it goes to"
          },
          "type": {
            "type": "string",
            "enum": [
              "booking.created",
              "booking.updated",
              "booking.cancelled"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "venue": {
            "$ref": "#/components/schemas/EventVenue"
          },
          "data": {
            "type": "object",
            "properties": {
              "booking": {
                "$ref": "#/components/schemas/Booking"
              }
            },
            "required": [
              "booking"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "id",
          "type",
          "created_at",
          "venue",
          "data"
        ],
        "additionalProperties": false
      },
      "PingEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
            ]
          },
          "type": {
            "const": "ping"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "venue": {
            "$ref": "#/components/schemas/EventVenue"
          },
          "data": {
            "type": "object",
            "properties": {
              "webhook": {
                "type": "string",
                "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
                "examples": [
                  "01JC9Y7F3K2M8Q4R6T0V1W2X3Y"
                ]
              }
            },
            "required": [
              "webhook"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "id",
          "type",
          "created_at",
          "venue",
          "data"
        ],
        "additionalProperties": false
      }
    },
    "parameters": {
      "Locale": {
        "name": "locale",
        "in": "query",
        "required": false,
        "description": "the language of the restaurant's texts",
        "schema": {
          "type": "string",
          "enum": [
            "en",
            "sv"
          ]
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "a UUID: the same key answers the same hold or booking again",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "Reference": {
        "name": "reference",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^TF-[0-9A-HJKMNP-TV-Z]{5}$"
        }
      },
      "Date": {
        "name": "date",
        "in": "query",
        "required": true,
        "schema": {
          "type": "string",
          "format": "date",
          "examples": [
            "2026-12-02"
          ]
        }
      },
      "Party": {
        "name": "party",
        "in": "query",
        "required": true,
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 50
        }
      },
      "Time": {
        "name": "time",
        "in": "query",
        "required": true,
        "description": "HH:MM on the 15-minute grid",
        "schema": {
          "type": "string",
          "pattern": "^\\d{2}:\\d{2}$",
          "examples": [
            "19:00"
          ]
        }
      }
    },
    "headers": {
      "RateLimitLimit": {
        "description": "requests a minute for the key",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimitRemaining": {
        "description": "requests left in this minute",
        "schema": {
          "type": "integer"
        }
      },
      "RetryAfter": {
        "description": "seconds until the key may call again",
        "schema": {
          "type": "integer"
        }
      }
    },
    "responses": {
      "Unauthenticated": {
        "description": "No key, an unknown or a revoked key (`unauthenticated`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "headers": {
          "WWW-Authenticate": {
            "schema": {
              "type": "string"
            }
          }
        }
      },
      "Forbidden": {
        "description": "`scope` (the key lacks the scope in `required`) or `company_suspended`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "`rate_limited`: more than 120 requests in a minute (`retry_after` in seconds).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        }
      },
      "NotFound": {
        "description": "`not_found`: not the key's venue's.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Validation": {
        "description": "`validation` with `fields`, or another refusal of the request (`idempotency_mismatch`, `seating_unavailable`, `table_too_large`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotPublished": {
        "description": "`not_published`: the venue's page is not published, so it takes no online bookings.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "`slot_taken` (with `alternatives: [{date, time}]`), `not_published`, `invalid_transition` (with `from`, `to`), `stale`, `payment_required`: the service takes a card guarantee, a deposit or a prepaid menu, which only TableFai's own booking pages take (the guest books there), `preorder_required` (the service needs a pre-order) or `verification_required` (the service asks the party to verify with BankID, slice 8b).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "`plan_required` (`feature`, `plan`: the venue's company is on the Start plan) or `subscription_inactive` (`status`: its subscription is paused or canceled).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}
