{
  "openapi": "3.1.0",
  "info": {
    "title": "Bookr API",
    "version": "1.0.0",
    "description": "Manage a Bookr business from your own website or tools. Authenticate with a key from Business → Developer in the app (or mybookr.app/developer). Keys carry scopes; the API never takes a card payment — the customer pay-and-confirm step stays inside Bookr's booking page or embed.",
    "contact": {
      "name": "Bookr",
      "url": "https://mybookr.app/developers",
      "email": "support@mybookr.app"
    },
    "termsOfService": "https://mybookr.app/api-terms"
  },
  "servers": [
    {
      "url": "https://mybookr.app/api/v1"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "bk_live_…",
        "description": "Authorization: Bearer bk_live_…"
      }
    }
  },
  "tags": [
    {
      "name": "me"
    },
    {
      "name": "business"
    },
    {
      "name": "services"
    },
    {
      "name": "hours"
    },
    {
      "name": "staff"
    },
    {
      "name": "locations"
    },
    {
      "name": "availability"
    },
    {
      "name": "slots"
    },
    {
      "name": "bookings"
    },
    {
      "name": "blocks"
    },
    {
      "name": "customers"
    },
    {
      "name": "walkins"
    },
    {
      "name": "messages"
    },
    {
      "name": "reviews"
    },
    {
      "name": "insights"
    }
  ],
  "x-sections": [
    {
      "key": "catalog",
      "label": "Services, hours & availability",
      "hint": "Services, opening hours, staff and locations lists, busy ranges, free slots, blocked time.",
      "read": "read:catalog",
      "write": "write:catalog"
    },
    {
      "key": "bookings",
      "label": "Bookings & walk-ins",
      "hint": "Create (unpaid), cancel, reschedule, no-show, arrived; the walk-in queue. No card is ever taken.",
      "read": "read:bookings",
      "write": "write:bookings"
    },
    {
      "key": "customers",
      "label": "Customers",
      "hint": "Names, emails, phones, your notes; block or unblock. Never on a public website.",
      "read": "read:customers",
      "write": "write:customers",
      "pii": true
    },
    {
      "key": "messages",
      "label": "Messages",
      "hint": "Read threads with customers and reply as the business. Never on a public website.",
      "read": "read:messages",
      "write": "write:messages",
      "pii": true
    },
    {
      "key": "reviews",
      "label": "Reviews",
      "hint": "Read reviews and reply to them.",
      "read": "read:reviews",
      "write": "write:reviews"
    },
    {
      "key": "staff",
      "label": "Staff",
      "hint": "Add, change or remove team members and what they can do.",
      "read": null,
      "write": "write:staff"
    },
    {
      "key": "insights",
      "label": "Earnings & payouts",
      "hint": "Revenue by period and the payout ledger. Money — never on a public website.",
      "read": "read:insights",
      "write": null,
      "pii": true
    },
    {
      "key": "business",
      "label": "Your page & availability switch",
      "hint": "About, announcement, FAQ, badges, arrangement, social links; turn bookings on or off with a message.",
      "read": null,
      "write": "write:business"
    }
  ],
  "x-scopes": [
    "read:catalog",
    "read:bookings",
    "read:customers",
    "read:messages",
    "read:reviews",
    "read:insights",
    "write:catalog",
    "write:bookings",
    "write:customers",
    "write:messages",
    "write:reviews",
    "write:staff",
    "write:business"
  ],
  "x-mcp": {
    "url": "https://mybookr.app/mcp",
    "transport": "streamable-http",
    "auth": "Authorization: Bearer bk_live_…"
  },
  "paths": {
    "/me": {
      "get": {
        "operationId": "me",
        "summary": "Who am I",
        "description": "The key's name, scopes, business and plan. Use it to check a key works.",
        "tags": [
          "me"
        ],
        "parameters": [],
        "x-scope": null,
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/business": {
      "get": {
        "operationId": "business_get",
        "summary": "Business profile",
        "description": "Public profile, booking rules (notice, horizon, cancellation window), timezone and format. works_at says where the business works: premises (address and map shown to customers), mobile (customers see the town only and give THEIR address when booking) or both. address/postcode are the owner's own record and are never shown to customers for a mobile business.",
        "tags": [
          "business"
        ],
        "parameters": [],
        "x-scope": "read:catalog",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      },
      "patch": {
        "operationId": "business_update",
        "summary": "Update the page",
        "description": "The words and arrangement on the booking page: description (about), announcement, arrival_instructions, confirmation_note, faqs, amenities (badge keys), page_layout, social_links, service_area. Plan gates apply on the page as they do in the app. Nothing here touches money, hours or the address.",
        "tags": [
          "business"
        ],
        "parameters": [],
        "x-scope": "write:business",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "description": {
                    "type": "string",
                    "description": "About (≤280)"
                  },
                  "announcement": {
                    "type": "string",
                    "description": "Notice at the top (≤140)"
                  },
                  "arrival_instructions": {
                    "type": "string",
                    "description": "Before they arrive"
                  },
                  "confirmation_note": {
                    "type": "string",
                    "description": "Added to confirmation emails (≤300)"
                  },
                  "faqs": {
                    "type": "array",
                    "description": "[{q, a}]"
                  },
                  "amenities": {
                    "type": "array",
                    "description": "Badge keys, e.g. [\"pet_friendly\",\"student_discount\"]"
                  },
                  "page_layout": {
                    "type": "object",
                    "description": "{ order: [...], hidden: [...] } over the optional sections"
                  },
                  "social_links": {
                    "type": "object",
                    "description": "{ instagram, tiktok, facebook, x, website, whatsapp, google }"
                  },
                  "service_area": {
                    "type": "string",
                    "description": "Area a mobile business covers (≤60)"
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/services": {
      "get": {
        "operationId": "services_list",
        "summary": "List services",
        "description": "Every service, active and inactive, with price in pence and duration in minutes.",
        "tags": [
          "services"
        ],
        "parameters": [
          {
            "name": "active",
            "in": "query",
            "required": false,
            "description": "Only active (bookable) services",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "x-scope": "read:catalog",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      },
      "post": {
        "operationId": "services_create",
        "summary": "Create a service",
        "description": "Add a bookable service. Price is in pence; duration in minutes (5–480). Plan service limits apply.",
        "tags": [
          "services"
        ],
        "parameters": [],
        "x-scope": "write:catalog",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Name, up to 80 characters"
                  },
                  "price_pence": {
                    "type": "integer",
                    "description": "Price in pence: at least 500 (£5), or 0 for a free service"
                  },
                  "duration_min": {
                    "type": "integer",
                    "description": "Duration in minutes"
                  },
                  "description": {
                    "type": "string",
                    "description": "Up to 300 characters"
                  },
                  "buffer_min": {
                    "type": "integer",
                    "description": "Gap after the appointment, minutes"
                  },
                  "active": {
                    "type": "boolean",
                    "description": "Bookable now (default true)"
                  }
                },
                "required": [
                  "name",
                  "price_pence",
                  "duration_min"
                ]
              }
            }
          }
        }
      }
    },
    "/services/{id}": {
      "get": {
        "operationId": "services_get",
        "summary": "Get a service",
        "description": "One service by id.",
        "tags": [
          "services"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "read:catalog",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      },
      "patch": {
        "operationId": "services_update",
        "summary": "Update a service",
        "description": "Change name, price, duration, description, buffer or active.",
        "tags": [
          "services"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "write:catalog",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Up to 80 characters"
                  },
                  "price_pence": {
                    "type": "integer",
                    "description": "Pence"
                  },
                  "duration_min": {
                    "type": "integer",
                    "description": "Minutes"
                  },
                  "description": {
                    "type": "string",
                    "description": "Up to 300 characters"
                  },
                  "buffer_min": {
                    "type": "integer",
                    "description": "Minutes"
                  },
                  "active": {
                    "type": "boolean",
                    "description": "Bookable"
                  }
                },
                "required": []
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "services_deactivate",
        "summary": "Deactivate a service",
        "description": "Hides it from the booking page. Existing bookings keep it. Nothing is deleted — the same as the app.",
        "tags": [
          "services"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "write:catalog",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/hours": {
      "get": {
        "operationId": "hours_get",
        "summary": "Opening hours",
        "description": "Seven rows, day_of_week 0 = Sunday. Times are HH:MM in the business timezone.",
        "tags": [
          "hours"
        ],
        "parameters": [],
        "x-scope": "read:catalog",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      },
      "put": {
        "operationId": "hours_set",
        "summary": "Set opening hours",
        "description": "Upsert any subset of days. Each item: day_of_week (0–6, 0 = Sunday), is_open, open_time, close_time (HH:MM).",
        "tags": [
          "hours"
        ],
        "parameters": [],
        "x-scope": "write:catalog",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "hours": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    },
                    "description": "Array of {day_of_week, is_open, open_time, close_time}"
                  }
                },
                "required": [
                  "hours"
                ]
              }
            }
          }
        }
      }
    },
    "/staff": {
      "get": {
        "operationId": "staff_list",
        "summary": "List staff",
        "description": "Team members who can be booked. Pass staff_id to slots and bookings.create to book one of them.",
        "tags": [
          "staff"
        ],
        "parameters": [],
        "x-scope": "read:catalog",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      },
      "post": {
        "operationId": "staff_create",
        "summary": "Add a team member",
        "description": "Creates a staff member customers can pick. Plan staff caps are enforced by the database (409 when full). Optionally assign services.",
        "tags": [
          "staff"
        ],
        "parameters": [],
        "x-scope": "write:staff",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Display name"
                  },
                  "role": {
                    "type": "string",
                    "description": "Role or title shown on the page"
                  },
                  "service_ids": {
                    "type": "array",
                    "description": "Services this person offers (uuids)"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/locations": {
      "get": {
        "operationId": "locations_list",
        "summary": "List locations",
        "description": "Branches, for multi-location businesses.",
        "tags": [
          "locations"
        ],
        "parameters": [],
        "x-scope": "read:catalog",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/availability": {
      "get": {
        "operationId": "availability_busy",
        "summary": "Busy ranges",
        "description": "BUSY time ranges from now for `days` days (bookings, blocks, time off, holiday, horizon). For free times use slots.",
        "tags": [
          "availability"
        ],
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "1–90, default 30",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "staff_id",
            "in": "query",
            "required": false,
            "description": "One team member",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "read:catalog",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/slots": {
      "get": {
        "operationId": "slots_list",
        "summary": "Free slots for a day",
        "description": "Free start times for a service on one date, computed with the booking page's own rules (hours, duration, notice, horizon, busy ranges). `starts_at` are the same times as ISO instants — pass one to bookings.create, or open the booking frame at it with Bookr.open({service, start}).",
        "tags": [
          "slots"
        ],
        "parameters": [
          {
            "name": "service_id",
            "in": "query",
            "required": true,
            "description": "Service to book",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "date",
            "in": "query",
            "required": true,
            "description": "YYYY-MM-DD in the business timezone",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "staff_id",
            "in": "query",
            "required": false,
            "description": "One team member",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "read:catalog",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/bookings": {
      "get": {
        "operationId": "bookings_list",
        "summary": "List bookings",
        "description": "Bookings in a window (default: from now, 30 days). Customer details need read:customers.",
        "tags": [
          "bookings"
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "ISO start (default now)",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "ISO end (default from + 30 days)",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "confirmed | pending | completed | cancelled | no_show",
            "schema": {
              "type": "string",
              "enum": [
                "confirmed",
                "pending",
                "completed",
                "cancelled",
                "no_show"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "1–500, default 200",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "x-scope": "read:bookings",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      },
      "post": {
        "operationId": "bookings_create",
        "summary": "Create a booking (unpaid)",
        "description": "Adds a confirmed booking the way the owner does by hand in the app: no card is taken. 409 when the business is at capacity for that time (an appointment business's capacity is its number of active staff), the named staff member is already booked, or the time is blocked. end_at defaults to start_at + service duration; total_pence to the service price.",
        "tags": [
          "bookings"
        ],
        "parameters": [],
        "x-scope": "write:bookings",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "service_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Service"
                  },
                  "start_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO start"
                  },
                  "customer_address": {
                    "type": "string",
                    "description": "Where the business goes. REQUIRED when the business works_at is mobile, or the service is marked is_mobile; ignored otherwise."
                  },
                  "end_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO end (default start + duration)"
                  },
                  "customer_name": {
                    "type": "string",
                    "description": "Customer name"
                  },
                  "customer_email": {
                    "type": "string",
                    "format": "email",
                    "description": "Customer email (confirmation goes here)"
                  },
                  "customer_phone": {
                    "type": "string",
                    "description": "Customer phone"
                  },
                  "notes": {
                    "type": "string",
                    "description": "Note from the customer"
                  },
                  "staff_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Team member"
                  },
                  "total_pence": {
                    "type": "integer",
                    "description": "Override price in pence"
                  },
                  "party_size": {
                    "type": "integer",
                    "description": "For businesses that book by party"
                  }
                },
                "required": [
                  "service_id",
                  "start_at"
                ]
              }
            }
          }
        }
      }
    },
    "/bookings/{id}": {
      "get": {
        "operationId": "bookings_get",
        "summary": "Get a booking",
        "description": "One booking by id.",
        "tags": [
          "bookings"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "read:bookings",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/bookings/{id}/cancel": {
      "post": {
        "operationId": "bookings_cancel",
        "summary": "Cancel a booking",
        "description": "Cancels as the business. A paid booking is refunded by Bookr's normal rules; the customer is told.",
        "tags": [
          "bookings"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "write:bookings",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "description": "Why (shown to the customer)"
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/bookings/{id}/reschedule": {
      "post": {
        "operationId": "bookings_reschedule",
        "summary": "Reschedule a booking",
        "description": "Move it to a new start (end defaults to keep the same length). The new slot must be free.",
        "tags": [
          "bookings"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "write:bookings",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "start_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "New ISO start"
                  },
                  "end_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "New ISO end"
                  }
                },
                "required": [
                  "start_at"
                ]
              }
            }
          }
        }
      }
    },
    "/bookings/{id}/no-show": {
      "post": {
        "operationId": "bookings_no_show",
        "summary": "Mark no-show",
        "description": "The customer did not turn up. Applies the business's no-show fee rule if one is set.",
        "tags": [
          "bookings"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "write:bookings",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/bookings/{id}/arrived": {
      "post": {
        "operationId": "bookings_arrived",
        "summary": "Mark arrived",
        "description": "Check the customer in (or undo with arrived=false).",
        "tags": [
          "bookings"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "write:bookings",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "arrived": {
                    "type": "boolean",
                    "description": "Default true"
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/bookings/{id}/undo": {
      "post": {
        "operationId": "bookings_undo",
        "summary": "Undo completed / no-show",
        "description": "Puts a completed or no-show booking back to confirmed.",
        "tags": [
          "bookings"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "write:bookings",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/blocks": {
      "get": {
        "operationId": "blocks_list",
        "summary": "List time blocks",
        "description": "Owner-blocked time (lunch, admin, days off) from now.",
        "tags": [
          "blocks"
        ],
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "1–365, default 60",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "x-scope": "read:bookings",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      },
      "post": {
        "operationId": "blocks_create",
        "summary": "Block time",
        "description": "Make a range unbookable.",
        "tags": [
          "blocks"
        ],
        "parameters": [],
        "x-scope": "write:catalog",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "start_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO start"
                  },
                  "end_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO end"
                  },
                  "reason": {
                    "type": "string",
                    "description": "Shown only to the owner"
                  }
                },
                "required": [
                  "start_at",
                  "end_at"
                ]
              }
            }
          }
        }
      }
    },
    "/blocks/{id}": {
      "patch": {
        "operationId": "blocks_update",
        "summary": "Change a block",
        "description": "Move or rename a time block.",
        "tags": [
          "blocks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "write:catalog",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "start_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO start"
                  },
                  "end_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO end"
                  },
                  "reason": {
                    "type": "string",
                    "description": "Owner note"
                  }
                },
                "required": [
                  "start_at",
                  "end_at"
                ]
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "blocks_delete",
        "summary": "Remove a block",
        "description": "The time becomes bookable again.",
        "tags": [
          "blocks"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "write:catalog",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/customers": {
      "get": {
        "operationId": "customers_list",
        "summary": "List customers",
        "description": "Everyone who has booked here or is on the client list: visit counts, first and last visit, lifetime spend, blocked flag, private notes. Optional q filters by name, email or phone.",
        "tags": [
          "customers"
        ],
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search text",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "1–1000, default 200",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "x-scope": "read:customers",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/customers/{id}": {
      "get": {
        "operationId": "customers_get",
        "summary": "Get a customer",
        "description": "One customer with the owner's private notes and their bookings at this business.",
        "tags": [
          "customers"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "read:customers",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      },
      "patch": {
        "operationId": "customers_update",
        "summary": "Update a customer",
        "description": "Set the owner's private notes, or block / unblock them from booking.",
        "tags": [
          "customers"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "write:customers",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "notes": {
                    "type": "string",
                    "description": "Private notes (owner only)"
                  },
                  "blocked": {
                    "type": "boolean",
                    "description": "Block from booking"
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/walkins": {
      "get": {
        "operationId": "walkins_list",
        "summary": "Walk-in queue",
        "description": "Who is waiting right now (walk-in businesses). Names need read:customers.",
        "tags": [
          "walkins"
        ],
        "parameters": [],
        "x-scope": "read:bookings",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      },
      "post": {
        "operationId": "walkins_add",
        "summary": "Add to the walk-in queue",
        "description": "Join someone to the queue at the counter.",
        "tags": [
          "walkins"
        ],
        "parameters": [],
        "x-scope": "write:bookings",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "customer_name": {
                    "type": "string",
                    "description": "Name"
                  },
                  "customer_phone": {
                    "type": "string",
                    "description": "Phone"
                  },
                  "customer_email": {
                    "type": "string",
                    "format": "email",
                    "description": "Email"
                  },
                  "service_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Service"
                  }
                },
                "required": [
                  "customer_name"
                ]
              }
            }
          }
        }
      }
    },
    "/walkins/{id}": {
      "patch": {
        "operationId": "walkins_update",
        "summary": "Edit a walk-in",
        "description": "Correct the name or phone of someone in the queue.",
        "tags": [
          "walkins"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "write:bookings",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Name"
                  },
                  "phone": {
                    "type": "string",
                    "description": "Phone"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        }
      }
    },
    "/messages": {
      "get": {
        "operationId": "messages_list",
        "summary": "List messages",
        "description": "The business's message threads with customers, newest first: every message carries its booking id, who sent it (business or customer) and when. Filter by booking_id for one thread.",
        "tags": [
          "messages"
        ],
        "parameters": [
          {
            "name": "booking_id",
            "in": "query",
            "required": false,
            "description": "One booking's thread",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Max rows (default 100)",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "x-scope": "read:messages",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      },
      "post": {
        "operationId": "messages_send",
        "summary": "Send a message",
        "description": "Replies to a customer on one booking, as the business. Bookr delivers it (email and, where enabled, push) and applies the daily cap the app has.",
        "tags": [
          "messages"
        ],
        "parameters": [],
        "x-scope": "write:messages",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "booking_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The booking whose customer to message"
                  },
                  "body": {
                    "type": "string",
                    "description": "The message (≤2000 chars)"
                  }
                },
                "required": [
                  "booking_id",
                  "body"
                ]
              }
            }
          }
        }
      }
    },
    "/reviews": {
      "get": {
        "operationId": "reviews_list",
        "summary": "List reviews",
        "description": "Every review of the business with its rating, text, the owner's reply and dates. Hidden reviews are included with hidden_at set.",
        "tags": [
          "reviews"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Max rows (default 100)",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "x-scope": "read:reviews",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/reviews/{id}/reply": {
      "post": {
        "operationId": "reviews_reply",
        "summary": "Reply to a review",
        "description": "Sets or replaces the owner's public reply on a review. Shown on the booking page under the review.",
        "tags": [
          "reviews"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "write:reviews",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reply": {
                    "type": "string",
                    "description": "The reply (≤1000 chars)"
                  }
                },
                "required": [
                  "reply"
                ]
              }
            }
          }
        }
      }
    },
    "/staff/{id}": {
      "patch": {
        "operationId": "staff_update",
        "summary": "Change a team member",
        "description": "Name, role, active flag, and which services they offer.",
        "tags": [
          "staff"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "write:staff",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Display name"
                  },
                  "role": {
                    "type": "string",
                    "description": "Role or title"
                  },
                  "active": {
                    "type": "boolean",
                    "description": "Shown to customers"
                  },
                  "service_ids": {
                    "type": "array",
                    "description": "Replace their services (uuids)"
                  }
                },
                "required": []
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "staff_remove",
        "summary": "Remove a team member",
        "description": "Removes them from the page and the rota. Their past bookings keep their name.",
        "tags": [
          "staff"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Record id",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-scope": "write:staff",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/insights": {
      "get": {
        "operationId": "insights_summary",
        "summary": "Earnings summary",
        "description": "Revenue for a period from completed and confirmed bookings (pence), booking counts by status, and the payout ledger totals: paid out, and held/still to come.",
        "tags": [
          "insights"
        ],
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Start (ISO). Default: 30 days ago",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "End (ISO). Default: now",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ],
        "x-scope": "read:insights",
        "x-readOnly": true,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        }
      }
    },
    "/business/availability": {
      "post": {
        "operationId": "business_availability",
        "summary": "Turn bookings on or off",
        "description": "The same switch as the app's \"Taking bookings\". taking_bookings=false stops every booking path (waitlists stay open). Give a message customers will read and, optionally, back_on (ISO date) so it resumes by itself. taking_bookings=true clears both.",
        "tags": [
          "business"
        ],
        "parameters": [],
        "x-scope": "write:business",
        "x-readOnly": false,
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Bad request"
          },
          "401": {
            "description": "Missing or invalid key"
          },
          "402": {
            "description": "Plan without API access"
          },
          "403": {
            "description": "Key lacks the scope, or the record is not this business's"
          },
          "404": {
            "description": "Not found"
          },
          "409": {
            "description": "Slot taken"
          },
          "422": {
            "description": "Refused by a business rule"
          },
          "429": {
            "description": "Rate limited"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "taking_bookings": {
                    "type": "boolean",
                    "description": "true = on, false = off"
                  },
                  "message": {
                    "type": "string",
                    "description": "What customers read while off (≤160)"
                  },
                  "back_on": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When bookings resume (off until then; resumes automatically)"
                  }
                },
                "required": [
                  "taking_bookings"
                ]
              }
            }
          }
        }
      }
    }
  }
}
