{
  "openapi": "3.1.0",
  "info": {
    "title": "Bluefie public APIs",
    "version": "2026-09-30",
    "description": "Two REST APIs.\n\nThe Projects API on https://bluefie.com reads and changes a workspace's boards, cards and comments. Every call carries a workspace key as a bearer token; an owner or an admin mints one in Settings, then Integrations. Success is { \"data\": ..., \"meta\": { \"nextCursor\": ... } } (meta on lists only). Failure is { \"error\": { \"code\": ..., \"message\": ... } }. Rate limit: 120 requests per minute per key. Every answer after the key is checked, refusals included, carries RateLimit-Policy and RateLimit; over the limit, the answer is 429 with Retry-After in seconds.\n\nThe Booking API on https://sites.bluefie.com and on every website Bluefie hosts checks bookable times, prices published services and requests an appointment, with no key. Failure is { \"error\": \"one sentence\" }.\n\nAgents: the MCP server at https://bluefie.com/api/mcp is the other door, and https://bluefie.com/agents.md says how to use it.",
    "contact": {
      "name": "Bluefie",
      "email": "info@bluefie.com",
      "url": "https://bluefie.com/developers"
    }
  },
  "externalDocs": {
    "description": "Developer docs",
    "url": "https://bluefie.com/developers"
  },
  "servers": [
    {
      "url": "https://bluefie.com",
      "description": "Projects API. Every path without servers of its own lives here."
    }
  ],
  "tags": [
    {
      "name": "Boards",
      "description": "Projects boards and their stages."
    },
    {
      "name": "Cards",
      "description": "Cards on a board, and what is said on them."
    },
    {
      "name": "Booking",
      "description": "Appointments on the websites Bluefie hosts. Anonymous."
    }
  ],
  "paths": {
    "/api/public/v1/boards": {
      "get": {
        "operationId": "listBoards",
        "summary": "List boards",
        "description": "Every board on the workspace the key belongs to. Archived boards are left out unless includeArchived=1. Private boards are never listed. Newest first. Page with limit (1 to 100, default 50) and the opaque cursor from meta.nextCursor; the last page has a null cursor. Rate limit: 120 requests per minute per key. Every answer after the key is checked, refusals included, carries RateLimit-Policy and RateLimit; over the limit, the answer is 429 with Retry-After in seconds.",
        "tags": [
          "Boards"
        ],
        "security": [
          {
            "workspaceKey": [
              "projects:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "includeArchived",
            "in": "query",
            "description": "Send 1 to include archived boards. Anything else leaves them out.",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of boards.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Board"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ListMeta"
                    }
                  }
                },
                "examples": {
                  "page": {
                    "summary": "Two boards and a next page",
                    "value": {
                      "data": [
                        {
                          "id": "b21d9d2e-2f4b-4a77-9c31-2f4b4a779c31",
                          "name": "Onboarding",
                          "description": "New customers",
                          "color": "#1f48ff",
                          "archived_at": null,
                          "created_at": "2026-08-21T10:02:44.001Z"
                        },
                        {
                          "id": "6f1c0f2a-1c62-4e33-9b8a-0f2a1c624e33",
                          "name": "Client work",
                          "description": "Everything we owe a customer",
                          "color": "#1f48ff",
                          "archived_at": null,
                          "created_at": "2026-08-19T09:14:02.113Z"
                        }
                      ],
                      "meta": {
                        "nextCursor": "eyJ0IjoiMjAyNi0wOC0xOVQwOToxNDowMi4xMTNaIiwiaSI6IjZmMWMwZjJhLTFjNjItNGUzMy05YjhhLTBmMmExYzYyNGUzMyJ9"
                      }
                    }
                  },
                  "lastPage": {
                    "summary": "The last page",
                    "value": {
                      "data": [
                        {
                          "id": "6f1c0f2a-1c62-4e33-9b8a-0f2a1c624e33",
                          "name": "Client work",
                          "description": "Everything we owe a customer",
                          "color": "#1f48ff",
                          "archived_at": null,
                          "created_at": "2026-08-19T09:14:02.113Z"
                        }
                      ],
                      "meta": {
                        "nextCursor": null
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A limit or a cursor that is not right.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "limit": {
                    "summary": "limit out of range",
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "You can ask for between 1 and 100 at a time."
                      }
                    }
                  },
                  "cursor": {
                    "summary": "A cursor that is not ours",
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "That cursor is not one of ours. Start the list again without it."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ReadForbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "post": {
        "operationId": "createBoard",
        "summary": "Create a board",
        "description": "A new board with the four default stages (To do, In progress, Review, Done) already on it, so a card can be created straight away. Rate limit: 120 requests per minute per key. Every answer after the key is checked, refusals included, carries RateLimit-Policy and RateLimit; over the limit, the answer is 429 with Retry-After in seconds.",
        "tags": [
          "Boards"
        ],
        "security": [
          {
            "workspaceKey": [
              "projects:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateBoardRequest"
              },
              "examples": {
                "board": {
                  "summary": "A board",
                  "value": {
                    "name": "Onboarding",
                    "description": "New customers"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The board, with its stages.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BoardWithColumns"
                    }
                  }
                },
                "examples": {
                  "created": {
                    "summary": "A new board",
                    "value": {
                      "data": {
                        "id": "b21d9d2e-2f4b-4a77-9c31-2f4b4a779c31",
                        "name": "Onboarding",
                        "description": "New customers",
                        "color": "#1f48ff",
                        "archived_at": null,
                        "created_at": "2026-08-21T10:02:44.001Z",
                        "columns": [
                          {
                            "id": "0a5b0000-0000-4000-8000-000000000001",
                            "name": "To do",
                            "position": 1
                          },
                          {
                            "id": "1b6c0000-0000-4000-8000-000000000002",
                            "name": "In progress",
                            "position": 2
                          },
                          {
                            "id": "2c7d0000-0000-4000-8000-000000000003",
                            "name": "Review",
                            "position": 3
                          },
                          {
                            "id": "3d8e0000-0000-4000-8000-000000000004",
                            "name": "Done",
                            "position": 4
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body is not right. The message says which part.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "name": {
                    "summary": "No name",
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "Give the board a name."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/WriteForbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/public/v1/boards/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/BoardId"
        }
      ],
      "get": {
        "operationId": "getBoard",
        "summary": "Get a board and its stages",
        "description": "One board and its stages, in order. The stage ids are what a new card needs. Rate limit: 120 requests per minute per key. Every answer after the key is checked, refusals included, carries RateLimit-Policy and RateLimit; over the limit, the answer is 429 with Retry-After in seconds.",
        "tags": [
          "Boards"
        ],
        "security": [
          {
            "workspaceKey": [
              "projects:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The board.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BoardWithColumns"
                    }
                  }
                },
                "examples": {
                  "board": {
                    "summary": "A board and its stages",
                    "value": {
                      "data": {
                        "id": "6f1c0f2a-1c62-4e33-9b8a-0f2a1c624e33",
                        "name": "Client work",
                        "description": "Everything we owe a customer",
                        "color": "#1f48ff",
                        "archived_at": null,
                        "created_at": "2026-08-19T09:14:02.113Z",
                        "columns": [
                          {
                            "id": "0a5b0000-0000-4000-8000-000000000001",
                            "name": "To do",
                            "position": 1
                          },
                          {
                            "id": "1b6c0000-0000-4000-8000-000000000002",
                            "name": "In progress",
                            "position": 2
                          },
                          {
                            "id": "2c7d0000-0000-4000-8000-000000000003",
                            "name": "Review",
                            "position": 3
                          },
                          {
                            "id": "3d8e0000-0000-4000-8000-000000000004",
                            "name": "Done",
                            "position": 4
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ReadForbidden"
          },
          "404": {
            "description": "No board with that id that this key may see.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "board": {
                    "summary": "Unknown or private board",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "There is no board with that id on this workspace."
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/public/v1/boards/{id}/cards": {
      "parameters": [
        {
          "$ref": "#/components/parameters/BoardId"
        }
      ],
      "get": {
        "operationId": "listBoardCards",
        "summary": "List the cards on a board",
        "description": "The cards on one board. Narrow it with columnId and status. Newest first. Page with limit (1 to 100, default 50) and the opaque cursor from meta.nextCursor; the last page has a null cursor. Rate limit: 120 requests per minute per key. Every answer after the key is checked, refusals included, carries RateLimit-Policy and RateLimit; over the limit, the answer is 429 with Retry-After in seconds.",
        "tags": [
          "Cards"
        ],
        "security": [
          {
            "workspaceKey": [
              "projects:read"
            ]
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "name": "columnId",
            "in": "query",
            "description": "Only the cards in this stage.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Only the cards with this status.",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "in_progress",
                "done",
                "archived"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of cards.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "data",
                    "meta"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Card"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/ListMeta"
                    }
                  }
                },
                "examples": {
                  "page": {
                    "summary": "The last page of in-progress cards",
                    "value": {
                      "data": [
                        {
                          "id": "9e4a77bd-3c18-4f0a-8d55-3c184f0a8d55",
                          "board_id": "6f1c0f2a-1c62-4e33-9b8a-0f2a1c624e33",
                          "column_id": "1b6c0000-0000-4000-8000-000000000002",
                          "position": 3,
                          "title": "Draft the launch email",
                          "description": null,
                          "status": "in_progress",
                          "priority": "high",
                          "due_date": "2026-08-30",
                          "due_at": null,
                          "due_tz": null,
                          "labels": [
                            "launch"
                          ],
                          "assignee_user_id": null,
                          "delegated_agent_role": null,
                          "created_at": "2026-08-20T11:00:00.000Z",
                          "updated_at": "2026-08-20T11:00:00.000Z",
                          "completed_at": null
                        }
                      ],
                      "meta": {
                        "nextCursor": null
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A filter, a limit or a cursor that is not right.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "status": {
                    "summary": "An unknown status",
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "status has to be one of open, in_progress, done, archived."
                      }
                    }
                  },
                  "columnId": {
                    "summary": "A columnId that is not an id",
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "columnId has to be the id of a column on this board."
                      }
                    }
                  },
                  "limit": {
                    "summary": "limit out of range",
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "You can ask for between 1 and 100 at a time."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ReadForbidden"
          },
          "404": {
            "description": "No board with that id that this key may see.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "board": {
                    "summary": "Unknown or private board",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "There is no board with that id on this workspace."
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/public/v1/cards": {
      "post": {
        "operationId": "createCard",
        "summary": "Create a card",
        "description": "A new card at the end of the stage named. The stage has to be on the board named, and the board cannot be archived. The card is written by nobody: no person is named as its author or assignee, and its history records which key made it. Rate limit: 120 requests per minute per key. Every answer after the key is checked, refusals included, carries RateLimit-Policy and RateLimit; over the limit, the answer is 429 with Retry-After in seconds.",
        "tags": [
          "Cards"
        ],
        "security": [
          {
            "workspaceKey": [
              "projects:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCardRequest"
              },
              "examples": {
                "card": {
                  "summary": "A card with a due time",
                  "value": {
                    "boardId": "6f1c0f2a-1c62-4e33-9b8a-0f2a1c624e33",
                    "columnId": "0a5b0000-0000-4000-8000-000000000001",
                    "title": "Build failed on main",
                    "description": "The 10:04 run went red.",
                    "priority": "urgent",
                    "dueDate": "2026-08-22",
                    "dueTime": "15:00",
                    "dueTz": "Europe/London",
                    "labels": [
                      "ci"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The card.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Card"
                    }
                  }
                },
                "examples": {
                  "created": {
                    "summary": "A new card",
                    "value": {
                      "data": {
                        "id": "44b1c8e0-90f2-4c1e-9a3d-90f24c1e9a3d",
                        "board_id": "6f1c0f2a-1c62-4e33-9b8a-0f2a1c624e33",
                        "column_id": "0a5b0000-0000-4000-8000-000000000001",
                        "position": 4,
                        "title": "Build failed on main",
                        "description": "The 10:04 run went red.",
                        "status": "open",
                        "priority": "urgent",
                        "due_date": "2026-08-22",
                        "due_at": "2026-08-22T14:00:00.000Z",
                        "due_tz": "Europe/London",
                        "labels": [
                          "ci"
                        ],
                        "assignee_user_id": null,
                        "delegated_agent_role": null,
                        "created_at": "2026-08-21T10:04:19.220Z",
                        "updated_at": "2026-08-21T10:04:19.220Z",
                        "completed_at": null
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body is not right, or the board is archived.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "title": {
                    "summary": "No title",
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "Give the card a title."
                      }
                    }
                  },
                  "dueTime": {
                    "summary": "A time in the wrong shape",
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "Send dueTime as HH:mm, like 15:00."
                      }
                    }
                  },
                  "archived": {
                    "summary": "An archived board",
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "That board is archived, so nothing new can be added to it. Restore it first."
                      }
                    }
                  },
                  "timesOff": {
                    "summary": "A time on a workspace without times yet",
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "Times on cards are not switched on for this workspace yet."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/WriteForbidden"
          },
          "404": {
            "description": "No such board for this key, or no such stage on it.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "board": {
                    "summary": "Unknown or private board",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "There is no board with that id on this workspace."
                      }
                    }
                  },
                  "column": {
                    "summary": "A stage from another board",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "There is no column with that id on this board."
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/public/v1/cards/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CardId"
        }
      ],
      "get": {
        "operationId": "getCard",
        "summary": "Get a card and its thread",
        "description": "One card and its whole conversation, oldest comment first. Authors come back as display names, never as email addresses. Rate limit: 120 requests per minute per key. Every answer after the key is checked, refusals included, carries RateLimit-Policy and RateLimit; over the limit, the answer is 429 with Retry-After in seconds.",
        "tags": [
          "Cards"
        ],
        "security": [
          {
            "workspaceKey": [
              "projects:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "The card.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CardWithComments"
                    }
                  }
                },
                "examples": {
                  "card": {
                    "summary": "A card with two comments",
                    "value": {
                      "data": {
                        "id": "44b1c8e0-90f2-4c1e-9a3d-90f24c1e9a3d",
                        "board_id": "6f1c0f2a-1c62-4e33-9b8a-0f2a1c624e33",
                        "column_id": "0a5b0000-0000-4000-8000-000000000001",
                        "position": 4,
                        "title": "Build failed on main",
                        "description": "The 10:04 run went red.",
                        "status": "open",
                        "priority": "urgent",
                        "due_date": "2026-08-22",
                        "due_at": "2026-08-22T14:00:00.000Z",
                        "due_tz": "Europe/London",
                        "labels": [
                          "ci"
                        ],
                        "assignee_user_id": null,
                        "delegated_agent_role": null,
                        "created_at": "2026-08-21T10:04:19.220Z",
                        "updated_at": "2026-08-21T10:04:19.220Z",
                        "completed_at": null,
                        "comments": [
                          {
                            "id": "c0ffee00-0000-4000-8000-000000000001",
                            "body": "Looking at it now.",
                            "author_name": "Dana Reyes",
                            "created_at": "2026-08-21T10:09:00.000Z"
                          },
                          {
                            "id": "c0ffee00-0000-4000-8000-000000000002",
                            "body": "Deploy 481 is live.",
                            "author_name": "API",
                            "created_at": "2026-08-21T10:33:40.512Z"
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ReadForbidden"
          },
          "404": {
            "description": "No card with that id that this key may see.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "card": {
                    "summary": "Unknown card, or a card on a private board",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "There is no card with that id on this workspace."
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "patch": {
        "operationId": "updateCard",
        "summary": "Change a card",
        "description": "Change what a script is allowed to change. columnId moves the card to the end of another stage on the same board. A time needs a date; dueTime null takes the time off and leaves the date. Rate limit: 120 requests per minute per key. Every answer after the key is checked, refusals included, carries RateLimit-Policy and RateLimit; over the limit, the answer is 429 with Retry-After in seconds.",
        "tags": [
          "Cards"
        ],
        "security": [
          {
            "workspaceKey": [
              "projects:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCardRequest"
              },
              "examples": {
                "done": {
                  "summary": "Mark it done and move it to the Done stage",
                  "value": {
                    "status": "done",
                    "columnId": "3d8e0000-0000-4000-8000-000000000004"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The card as it is now.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Card"
                    }
                  }
                },
                "examples": {
                  "done": {
                    "summary": "Done, at the end of the Done stage",
                    "value": {
                      "data": {
                        "id": "44b1c8e0-90f2-4c1e-9a3d-90f24c1e9a3d",
                        "board_id": "6f1c0f2a-1c62-4e33-9b8a-0f2a1c624e33",
                        "column_id": "3d8e0000-0000-4000-8000-000000000004",
                        "position": 9,
                        "title": "Build failed on main",
                        "description": "The 10:04 run went red.",
                        "status": "done",
                        "priority": "urgent",
                        "due_date": "2026-08-22",
                        "due_at": "2026-08-22T14:00:00.000Z",
                        "due_tz": "Europe/London",
                        "labels": [
                          "ci"
                        ],
                        "assignee_user_id": null,
                        "delegated_agent_role": null,
                        "created_at": "2026-08-21T10:04:19.220Z",
                        "updated_at": "2026-08-21T10:31:02.880Z",
                        "completed_at": "2026-08-21T10:31:02.880Z"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body is not right. The message says which part.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "unknownField": {
                    "summary": "A field this endpoint does not change",
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "This endpoint does not change assigneeUserId."
                      }
                    }
                  },
                  "empty": {
                    "summary": "Nothing to change",
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "Name at least one thing to change."
                      }
                    }
                  },
                  "timesOff": {
                    "summary": "A time on a workspace without times yet",
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "Times on cards are not switched on for this workspace yet."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/WriteForbidden"
          },
          "404": {
            "description": "No such card for this key, or no such stage on its board.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "card": {
                    "summary": "Unknown card, or a card on a private board",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "There is no card with that id on this workspace."
                      }
                    }
                  },
                  "column": {
                    "summary": "A stage from another board",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "There is no column with that id on this card's board."
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/public/v1/cards/{id}/comments": {
      "parameters": [
        {
          "$ref": "#/components/parameters/CardId"
        }
      ],
      "post": {
        "operationId": "createCardComment",
        "summary": "Comment on a card",
        "description": "Say something on a card. It appears in the thread signed as API, and the card's history records which key said it. Nobody is notified. Rate limit: 120 requests per minute per key. Every answer after the key is checked, refusals included, carries RateLimit-Policy and RateLimit; over the limit, the answer is 429 with Retry-After in seconds.",
        "tags": [
          "Cards"
        ],
        "security": [
          {
            "workspaceKey": [
              "projects:write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCommentRequest"
              },
              "examples": {
                "comment": {
                  "summary": "A comment",
                  "value": {
                    "body": "Deploy 481 is live."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The comment.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Comment"
                    }
                  }
                },
                "examples": {
                  "created": {
                    "summary": "A comment from the API",
                    "value": {
                      "data": {
                        "id": "c0ffee00-0000-4000-8000-000000000002",
                        "body": "Deploy 481 is live.",
                        "author_name": "API",
                        "created_at": "2026-08-21T10:33:40.512Z"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body is not right.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "empty": {
                    "summary": "An empty comment",
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "Write something for the card to say."
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/WriteForbidden"
          },
          "404": {
            "description": "No card with that id that this key may see.",
            "headers": {
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "card": {
                    "summary": "Unknown card, or a card on a private board",
                    "value": {
                      "error": {
                        "code": "not_found",
                        "message": "There is no card with that id on this workspace."
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ServerError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/api/public/v1/availability": {
      "servers": [
        {
          "url": "https://sites.bluefie.com",
          "description": "Booking API, on the websites Bluefie hosts. Anonymous: no key, no session. The same paths also answer on every hosted site's own address, <slug>.sites.bluefie.com, and on the custom domains customers connect. The site field says which website is meant, whichever host is called."
        }
      ],
      "get": {
        "operationId": "getAvailability",
        "summary": "Bookable times on a site",
        "description": "The same grid the site's own booking widget shows, grouped by day, with the real opening hours beside the start times. Covers tomorrow through advanceDays ahead. Limits: 60 requests in a burst per caller address, refilling 1 a second, and 120 reads per minute per site. Over either, the answer is 429 with Retry-After in seconds.",
        "tags": [
          "Booking"
        ],
        "security": [],
        "parameters": [
          {
            "name": "site",
            "in": "query",
            "required": true,
            "description": "Which website: its subdomain (demo) or its custom domain (www.example.com). A pasted scheme, path or port is ignored.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 253,
              "description": "Which website: its subdomain (demo) or its custom domain (www.example.com). A pasted scheme, path or port is ignored."
            }
          },
          {
            "name": "date",
            "in": "query",
            "description": "Keep one day, YYYY-MM-DD. A date outside the booking window answers an empty days list.",
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The grid. A site that takes no bookings this way answers bookable false and no days.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Availability"
                },
                "examples": {
                  "open": {
                    "summary": "A site taking bookings (a real day lists every start time)",
                    "value": {
                      "site": {
                        "slug": "demo",
                        "name": "Demo Dental",
                        "url": "https://demo.sites.bluefie.com"
                      },
                      "timezone": "Europe/London",
                      "slotMinutes": 30,
                      "advanceDays": 7,
                      "bookable": true,
                      "days": [
                        {
                          "date": "2026-10-01",
                          "open": "09:00",
                          "close": "17:00",
                          "times": [
                            {
                              "time": "09:00",
                              "available": true
                            },
                            {
                              "time": "09:30",
                              "available": false
                            },
                            {
                              "time": "10:00",
                              "available": true
                            }
                          ]
                        }
                      ]
                    }
                  },
                  "closed": {
                    "summary": "A site that takes no bookings this way",
                    "value": {
                      "site": {
                        "slug": "demo",
                        "name": "Demo Dental",
                        "url": "https://demo.sites.bluefie.com"
                      },
                      "timezone": "UTC",
                      "slotMinutes": 30,
                      "advanceDays": 7,
                      "bookable": false,
                      "days": []
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No site, or a date in the wrong shape.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookingError"
                },
                "examples": {
                  "site": {
                    "summary": "No site",
                    "value": {
                      "error": "Pass site=<subdomain or custom domain> to say which website."
                    }
                  },
                  "date": {
                    "summary": "A date in the wrong shape",
                    "value": {
                      "error": "date must be written YYYY-MM-DD."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/SiteNotFound"
          },
          "429": {
            "$ref": "#/components/responses/BookingRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/BookingServerError"
          }
        }
      }
    },
    "/api/public/v1/quote": {
      "servers": [
        {
          "url": "https://sites.bluefie.com",
          "description": "Booking API, on the websites Bluefie hosts. Anonymous: no key, no session. The same paths also answer on every hosted site's own address, <slug>.sites.bluefie.com, and on the custom domains customers connect. The site field says which website is meant, whichever host is called."
        }
      ],
      "post": {
        "operationId": "createQuote",
        "summary": "Price services on a site",
        "description": "A price for services the site publishes, from the same list and prices its public page shows. Prices are as published, with no currency and no tax; the business confirms the final amount. Limits: 60 requests in a burst per caller address, refilling 1 a second, and 120 reads per minute per site. Over either, the answer is 429 with Retry-After in seconds.",
        "tags": [
          "Booking"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              },
              "examples": {
                "quote": {
                  "summary": "Two services",
                  "value": {
                    "site": "demo",
                    "services": [
                      {
                        "name": "Check-up"
                      },
                      {
                        "name": "Whitening",
                        "quantity": 2
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The quote.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                },
                "examples": {
                  "priced": {
                    "summary": "Every line has a plain price",
                    "value": {
                      "site": {
                        "slug": "demo",
                        "name": "Demo Dental",
                        "url": "https://demo.sites.bluefie.com"
                      },
                      "items": [
                        {
                          "name": "Check-up",
                          "quantity": 1,
                          "priceText": "$120",
                          "unitPrice": 120,
                          "lineTotal": 120
                        },
                        {
                          "name": "Whitening",
                          "quantity": 2,
                          "priceText": "$250",
                          "unitPrice": 250,
                          "lineTotal": 500
                        }
                      ],
                      "total": 620,
                      "currency": null,
                      "note": "Prices are as published on the site; the business confirms the final amount."
                    }
                  },
                  "unpriced": {
                    "summary": "A line with no plain price, so no total",
                    "value": {
                      "site": {
                        "slug": "demo",
                        "name": "Demo Dental",
                        "url": "https://demo.sites.bluefie.com"
                      },
                      "items": [
                        {
                          "name": "Implants",
                          "quantity": 1,
                          "priceText": "From $900",
                          "unitPrice": null,
                          "lineTotal": null
                        }
                      ],
                      "total": null,
                      "currency": null,
                      "note": "Prices are as published on the site; the business confirms the final amount."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "The body is not JSON, or does not validate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookingError"
                },
                "examples": {
                  "json": {
                    "summary": "Not JSON",
                    "value": {
                      "error": "Invalid JSON body"
                    }
                  },
                  "body": {
                    "summary": "No services",
                    "value": {
                      "error": "Invalid request",
                      "issues": [
                        "services: Array must contain at least 1 element(s)"
                      ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/SiteNotFound"
          },
          "422": {
            "description": "A service the site does not publish. available lists the ones it does.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookingError"
                },
                "examples": {
                  "unknown": {
                    "summary": "An unknown service",
                    "value": {
                      "error": "These services are not on the site: Massage",
                      "available": [
                        "Check-up",
                        "Whitening",
                        "Implants"
                      ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/BookingRateLimited"
          },
          "500": {
            "$ref": "#/components/responses/BookingServerError"
          }
        }
      }
    },
    "/api/public/v1/bookings": {
      "servers": [
        {
          "url": "https://sites.bluefie.com",
          "description": "Booking API, on the websites Bluefie hosts. Anonymous: no key, no session. The same paths also answer on every hosted site's own address, <slug>.sites.bluefie.com, and on the custom domains customers connect. The site field says which website is meant, whichever host is called."
        }
      ],
      "post": {
        "operationId": "createBooking",
        "summary": "Request an appointment on a site",
        "description": "Books through the same pipeline as the site's own booking widget, so the business sees it exactly like a booking made on the page, and confirms it. The Idempotency-Key header is required: the first settled answer (201 booked, or 409 taken) is stored under it, and a retry with the same key and the same body gets that answer again with Idempotent-Replay: true and books nothing more. A 5xx is never stored, so the same key can retry after one. Limits: 60 requests in a burst per caller address, refilling 1 a second, and 30 booking requests per minute per site. Over either, the answer is 429 with Retry-After in seconds.",
        "tags": [
          "Booking"
        ],
        "security": [],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "A stable id for this booking attempt, 1 to 200 characters. Reuse it only to retry the same booking.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200
            },
            "example": "3f2b8c1e-6d4a-4e2f-9b7c-1a2b3c4d5e6f"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingRequest"
              },
              "examples": {
                "booking": {
                  "summary": "A booking with an email address",
                  "value": {
                    "site": "demo",
                    "name": "Pat Visitor",
                    "email": "pat@example.com",
                    "date": "2026-10-01",
                    "time": "10:00",
                    "reason": "Check-up"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Booking requested. On a replay the same body comes back with Idempotent-Replay: true.",
            "headers": {
              "Idempotent-Replay": {
                "$ref": "#/components/headers/IdempotentReplay"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookingCreated"
                },
                "examples": {
                  "booked": {
                    "summary": "Booking requested",
                    "value": {
                      "booking": {
                        "id": "7a1d2c3b-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
                        "status": "pending",
                        "date": "2026-10-01",
                        "time": "10:00",
                        "name": "Pat Visitor"
                      },
                      "message": "Booking requested. The business confirms it and gets in touch using the details given."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No Idempotency-Key, a body that is not JSON, or one that does not validate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookingError"
                },
                "examples": {
                  "key": {
                    "summary": "No Idempotency-Key header",
                    "value": {
                      "error": "Send an Idempotency-Key header, a stable id for this booking attempt, so a retry cannot book twice."
                    }
                  },
                  "json": {
                    "summary": "Not JSON",
                    "value": {
                      "error": "Invalid JSON body"
                    }
                  },
                  "contact": {
                    "summary": "Neither a phone number nor an email address",
                    "value": {
                      "error": "Invalid request",
                      "issues": [
                        "phone: A phone number or an email address is required"
                      ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/SiteNotFound"
          },
          "409": {
            "description": "That time is taken. Stored under the key like a booking, so a replay answers the same.",
            "headers": {
              "Idempotent-Replay": {
                "$ref": "#/components/headers/IdempotentReplay"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookingError"
                },
                "examples": {
                  "taken": {
                    "summary": "The time is taken",
                    "value": {
                      "error": "That time is no longer available."
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "The Idempotency-Key was already used for a different booking. Nothing was booked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookingError"
                },
                "examples": {
                  "reused": {
                    "summary": "A key reused for another booking",
                    "value": {
                      "error": "That Idempotency-Key was already used for a different booking."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/BookingRateLimited"
          },
          "500": {
            "description": "The booking could not be saved. Nothing is stored under the key: retry with it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookingError"
                },
                "examples": {
                  "failed": {
                    "summary": "Not saved",
                    "value": {
                      "error": "The booking could not be saved. Please try again."
                    }
                  },
                  "serverError": {
                    "summary": "Something broke on our side",
                    "value": {
                      "error": "Something went wrong on our side. Please try again."
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "The booking API is not switched on for this site yet. Nothing was booked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookingError"
                },
                "examples": {
                  "notSwitchedOn": {
                    "summary": "Not switched on",
                    "value": {
                      "error": "The booking API is not switched on for this site yet."
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "workspaceKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "blf_live_ followed by 40 lowercase hex characters",
        "description": "A workspace key: Authorization: Bearer blf_live_... An owner or an admin mints one in Settings, then Integrations (https://bluefie.com/dashboard/settings?tab=integrations). It is shown once. projects:read covers every GET, projects:write every POST and PATCH. A key belongs to one workspace and never sees another."
      }
    },
    "schemas": {
      "ErrorCode": {
        "type": "string",
        "enum": [
          "unauthorized",
          "forbidden",
          "rate_limited",
          "invalid_request",
          "not_found",
          "unavailable",
          "server_error"
        ],
        "description": "Switch on this. It never changes meaning."
      },
      "Error": {
        "type": "object",
        "description": "Every Projects API failure.",
        "additionalProperties": false,
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "$ref": "#/components/schemas/ErrorCode"
              },
              "message": {
                "type": "string",
                "description": "One plain sentence for a person to read."
              }
            }
          }
        }
      },
      "ListMeta": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "nextCursor"
        ],
        "properties": {
          "nextCursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque. Pass it back as ?cursor= for the next page. Null on the last page."
          }
        }
      },
      "Board": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "description",
          "color",
          "archived_at",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "color": {
            "type": "string",
            "description": "A CSS colour. #1f48ff when none was set."
          },
          "archived_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the board was archived. Null on a board in use."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Column": {
        "type": "object",
        "description": "A stage on a board.",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "position"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "position": {
            "type": "number"
          }
        }
      },
      "BoardWithColumns": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "name",
          "description",
          "color",
          "archived_at",
          "created_at",
          "columns"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "color": {
            "type": "string",
            "description": "A CSS colour. #1f48ff when none was set."
          },
          "archived_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the board was archived. Null on a board in use."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "columns": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Column"
            },
            "description": "The stages, in order."
          }
        }
      },
      "Card": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "board_id",
          "column_id",
          "position",
          "title",
          "description",
          "status",
          "priority",
          "due_date",
          "due_at",
          "due_tz",
          "labels",
          "assignee_user_id",
          "delegated_agent_role",
          "created_at",
          "updated_at",
          "completed_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "board_id": {
            "type": "string",
            "format": "uuid"
          },
          "column_id": {
            "type": "string",
            "format": "uuid",
            "description": "The stage the card is in."
          },
          "position": {
            "type": "number",
            "description": "Order within the stage, lowest first."
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "in_progress",
              "done",
              "archived"
            ]
          },
          "priority": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "low",
              "medium",
              "high",
              "urgent",
              null
            ]
          },
          "due_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "due_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "The instant the card is due. Null on a card with a date and no time."
          },
          "due_tz": {
            "type": [
              "string",
              "null"
            ],
            "description": "The IANA zone to show due_at in. Null on a card with a date and no time."
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "assignee_user_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "delegated_agent_role": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Comment": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "body",
          "author_name",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "body": {
            "type": "string"
          },
          "author_name": {
            "type": "string",
            "description": "A display name, or API for a comment posted with a key. Never an email address."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CardWithComments": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "id",
          "board_id",
          "column_id",
          "position",
          "title",
          "description",
          "status",
          "priority",
          "due_date",
          "due_at",
          "due_tz",
          "labels",
          "assignee_user_id",
          "delegated_agent_role",
          "created_at",
          "updated_at",
          "completed_at",
          "comments"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "board_id": {
            "type": "string",
            "format": "uuid"
          },
          "column_id": {
            "type": "string",
            "format": "uuid",
            "description": "The stage the card is in."
          },
          "position": {
            "type": "number",
            "description": "Order within the stage, lowest first."
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "in_progress",
              "done",
              "archived"
            ]
          },
          "priority": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "low",
              "medium",
              "high",
              "urgent",
              null
            ]
          },
          "due_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date"
          },
          "due_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "The instant the card is due. Null on a card with a date and no time."
          },
          "due_tz": {
            "type": [
              "string",
              "null"
            ],
            "description": "The IANA zone to show due_at in. Null on a card with a date and no time."
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "assignee_user_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "delegated_agent_role": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "comments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Comment"
            },
            "description": "The whole thread, oldest first."
          }
        }
      },
      "CreateBoardRequest": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "description": {
            "type": "string",
            "maxLength": 2000
          },
          "color": {
            "type": "string",
            "maxLength": 32
          }
        }
      },
      "CreateCardRequest": {
        "type": "object",
        "required": [
          "boardId",
          "columnId",
          "title"
        ],
        "properties": {
          "boardId": {
            "type": "string",
            "format": "uuid"
          },
          "columnId": {
            "type": "string",
            "format": "uuid",
            "description": "A stage on that board. GET /boards/{id} lists them."
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20000
          },
          "priority": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "low",
              "medium",
              "high",
              "urgent",
              null
            ]
          },
          "dueDate": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
            "description": "YYYY-MM-DD. Null clears it."
          },
          "dueTime": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^([01]\\d|2[0-3]):([0-5]\\d)$",
            "description": "A wall-clock time, HH:mm, read in dueTz. A time needs a date. Null takes the time off and leaves the date."
          },
          "dueTz": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "An IANA timezone name, like Europe/London, that dueTime is read in."
          },
          "labels": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "string",
              "maxLength": 60
            }
          }
        }
      },
      "UpdateCardRequest": {
        "type": "object",
        "description": "Send only what changes. A field this endpoint does not change is refused by name. It cannot assign a person, delegate to an agent or move a card to another board.",
        "additionalProperties": false,
        "minProperties": 1,
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 300
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 20000
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "in_progress",
              "done",
              "archived"
            ]
          },
          "priority": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "low",
              "medium",
              "high",
              "urgent",
              null
            ]
          },
          "dueDate": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
            "description": "YYYY-MM-DD. Null clears it."
          },
          "dueTime": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^([01]\\d|2[0-3]):([0-5]\\d)$",
            "description": "A wall-clock time, HH:mm, read in dueTz. A time needs a date. Null takes the time off and leaves the date."
          },
          "dueTz": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "An IANA timezone name, like Europe/London, that dueTime is read in."
          },
          "columnId": {
            "type": "string",
            "format": "uuid",
            "description": "Moves the card to the end of another stage on this card's board."
          },
          "labels": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "string",
              "maxLength": 60
            }
          }
        }
      },
      "CreateCommentRequest": {
        "type": "object",
        "required": [
          "body"
        ],
        "properties": {
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20000
          }
        }
      },
      "BookingError": {
        "type": "object",
        "description": "Every Booking API failure: one plain sentence, sometimes with detail beside it.",
        "additionalProperties": false,
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "issues": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "On a 400 for a body that did not validate: up to five, each \"field: what is wrong\"."
          },
          "available": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "On a 422 from /quote: the services the site does publish."
          }
        }
      },
      "SiteSummary": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "slug",
          "name",
          "url"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "The site subdomain."
          },
          "name": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Where a visitor opens the site."
          }
        }
      },
      "AvailabilityDay": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "date",
          "open",
          "close",
          "times"
        ],
        "properties": {
          "date": {
            "type": "string",
            "format": "date"
          },
          "open": {
            "type": [
              "string",
              "null"
            ],
            "description": "HH:MM the business opens that day, in the site timezone."
          },
          "close": {
            "type": [
              "string",
              "null"
            ],
            "description": "HH:MM it closes. An appointment starts before this."
          },
          "times": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "time",
                "available"
              ],
              "properties": {
                "time": {
                  "type": "string",
                  "description": "HH:MM start, in the site timezone."
                },
                "available": {
                  "type": "boolean"
                }
              }
            }
          }
        }
      },
      "Availability": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "site",
          "timezone",
          "slotMinutes",
          "advanceDays",
          "bookable",
          "days"
        ],
        "properties": {
          "site": {
            "$ref": "#/components/schemas/SiteSummary"
          },
          "timezone": {
            "type": "string",
            "description": "The IANA zone every time here is in. UTC when the site has none."
          },
          "slotMinutes": {
            "type": "integer",
            "minimum": 1
          },
          "advanceDays": {
            "type": "integer",
            "minimum": 1,
            "description": "How many days ahead can be booked, starting tomorrow."
          },
          "bookable": {
            "type": "boolean",
            "description": "False when the site takes no bookings this way. days is then empty."
          },
          "days": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AvailabilityDay"
            }
          }
        }
      },
      "QuoteRequest": {
        "type": "object",
        "required": [
          "site",
          "services"
        ],
        "properties": {
          "site": {
            "type": "string",
            "minLength": 1,
            "maxLength": 253,
            "description": "Which website: its subdomain (demo) or its custom domain (www.example.com). A pasted scheme, path or port is ignored."
          },
          "services": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "items": {
              "type": "object",
              "required": [
                "name"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 200,
                  "description": "A service the site publishes. Case and spacing are ignored."
                },
                "quantity": {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 100,
                  "default": 1
                }
              }
            }
          }
        }
      },
      "QuoteLine": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "quantity",
          "priceText",
          "unitPrice",
          "lineTotal"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "The service name as the site publishes it."
          },
          "quantity": {
            "type": "integer"
          },
          "priceText": {
            "type": [
              "string",
              "null"
            ],
            "description": "The price exactly as the owner typed it."
          },
          "unitPrice": {
            "type": [
              "number",
              "null"
            ],
            "description": "Null when the published price is not a plain number, like \"From $50\"."
          },
          "lineTotal": {
            "type": [
              "number",
              "null"
            ]
          }
        }
      },
      "Quote": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "site",
          "items",
          "total",
          "currency",
          "note"
        ],
        "properties": {
          "site": {
            "$ref": "#/components/schemas/SiteSummary"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QuoteLine"
            }
          },
          "total": {
            "type": [
              "number",
              "null"
            ],
            "description": "Null the moment any line has no numeric price, never a partial sum."
          },
          "currency": {
            "type": "null",
            "description": "Always null: a site records no currency."
          },
          "note": {
            "type": "string"
          }
        }
      },
      "BookingRequest": {
        "type": "object",
        "description": "Send a phone number or an email address, or both. A blank one counts as absent.",
        "required": [
          "site",
          "name",
          "date",
          "time"
        ],
        "anyOf": [
          {
            "required": [
              "phone"
            ]
          },
          {
            "required": [
              "email"
            ]
          }
        ],
        "properties": {
          "site": {
            "type": "string",
            "minLength": 1,
            "maxLength": 253,
            "description": "Which website: its subdomain (demo) or its custom domain (www.example.com). A pasted scheme, path or port is ignored."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "email": {
            "anyOf": [
              {
                "type": "string",
                "format": "email",
                "maxLength": 320
              },
              {
                "type": "string",
                "maxLength": 0
              },
              {
                "type": "null"
              }
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^$|^[\\d\\s\\-()+]{7,20}$",
            "description": "Digits, spaces, dashes, brackets and plus, 7 to 20 of them."
          },
          "date": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
            "description": "A date from GET /availability."
          },
          "time": {
            "type": "string",
            "pattern": "^\\d{1,2}:\\d{2}$",
            "description": "A time from GET /availability, HH:MM."
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000
          }
        }
      },
      "BookingCreated": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "booking",
          "message"
        ],
        "properties": {
          "booking": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "status",
              "date",
              "time",
              "name"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "status": {
                "type": "string",
                "description": "pending until the business confirms it."
              },
              "date": {
                "type": "string",
                "format": "date"
              },
              "time": {
                "type": "string",
                "description": "HH:MM."
              },
              "name": {
                "type": "string"
              }
            }
          },
          "message": {
            "type": "string"
          }
        }
      }
    },
    "parameters": {
      "Limit": {
        "name": "limit",
        "in": "query",
        "description": "Rows per page, 1 to 100, 50 when left out. A number outside that range is refused with 400, never quietly clamped.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 50
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "description": "Opaque. Pass meta.nextCursor from the page before, unchanged. A cursor that is not one of ours is refused with 400.",
        "schema": {
          "type": "string"
        }
      },
      "BoardId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "A board id. Any id that is not a board this key may see answers 404.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "CardId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "A card id. Any id that is not a card this key may see answers 404.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      }
    },
    "headers": {
      "RetryAfter": {
        "description": "Seconds to wait before trying again.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      },
      "RateLimitPolicy": {
        "description": "The per-key limit, as draft-ietf-httpapi-ratelimit-headers-11 writes it: the policy named \"key\", q requests per w seconds. Always \"key\";q=120;w=60.",
        "schema": {
          "type": "string",
          "pattern": "^\"key\";q=\\d+;w=\\d+$"
        }
      },
      "RateLimit": {
        "description": "Where this key stands in the current window: r requests left and t seconds until the window resets, for example \"key\";r=87;t=41. On a 429, r is 0 and t equals Retry-After. Left out only on an answer given while the counter could not be read.",
        "schema": {
          "type": "string",
          "pattern": "^\"key\";r=\\d+;t=\\d+$"
        }
      },
      "IdempotentReplay": {
        "description": "Present, and true, when this answer is the stored answer to an earlier request with the same Idempotency-Key.",
        "schema": {
          "type": "string",
          "enum": [
            "true"
          ]
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "The key is missing, malformed, unknown or revoked. Every one of those answers exactly the same, on purpose.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "unauthorized": {
                "summary": "Any key that does not work",
                "value": {
                  "error": {
                    "code": "unauthorized",
                    "message": "That API key is not valid."
                  }
                }
              }
            }
          }
        }
      },
      "ReadForbidden": {
        "description": "A real key without projects:read.",
        "headers": {
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "forbidden": {
                "summary": "A key without read access",
                "value": {
                  "error": {
                    "code": "forbidden",
                    "message": "This key is not allowed to read your projects. Mint a key with read access in Settings, Integrations."
                  }
                }
              }
            }
          }
        }
      },
      "WriteForbidden": {
        "description": "A real key without projects:write.",
        "headers": {
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "forbidden": {
                "summary": "A read-only key",
                "value": {
                  "error": {
                    "code": "forbidden",
                    "message": "This key can read your projects but not change them. Mint a key with write access in Settings, Integrations."
                  }
                }
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "More than 120 requests in a minute on one key, counted in a fixed window.",
        "headers": {
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "rateLimited": {
                "summary": "Over the per-key limit",
                "value": {
                  "error": {
                    "code": "rate_limited",
                    "message": "That is more requests than this key is allowed in a minute. Try again in 42 seconds."
                  }
                }
              }
            }
          }
        }
      },
      "Unavailable": {
        "description": "Projects are not set up on this workspace yet. Retry-After asks for 3600 seconds, because setting Projects up is a step a person takes.",
        "headers": {
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "unavailable": {
                "summary": "A workspace mid-setup",
                "value": {
                  "error": {
                    "code": "unavailable",
                    "message": "Projects are not set up on this workspace yet, so there is nothing here to read."
                  }
                }
              }
            }
          }
        }
      },
      "ServerError": {
        "description": "Our fault. Nothing was changed.",
        "headers": {
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "serverError": {
                "summary": "Something broke on our side",
                "value": {
                  "error": {
                    "code": "server_error",
                    "message": "Something went wrong on our side. Nothing was changed. Please try again."
                  }
                }
              }
            }
          }
        }
      },
      "SiteNotFound": {
        "description": "No published site answers to that address. An unknown address and an unpublished site read the same.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/BookingError"
            },
            "examples": {
              "siteNotFound": {
                "summary": "Unknown or unpublished site",
                "value": {
                  "error": "No published site was found for that address."
                }
              }
            }
          }
        }
      },
      "BookingRateLimited": {
        "description": "Too many requests from this address, or for this site.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/BookingError"
            },
            "examples": {
              "address": {
                "summary": "Over the per-address burst",
                "value": {
                  "error": "Too many requests. Please slow down and try again shortly."
                }
              },
              "site": {
                "summary": "Over the per-site limit",
                "value": {
                  "error": "Too many requests for this site. Please slow down and try again shortly."
                }
              }
            }
          }
        }
      },
      "BookingServerError": {
        "description": "Our fault. Try again.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/BookingError"
            },
            "examples": {
              "serverError": {
                "summary": "Something broke on our side",
                "value": {
                  "error": "Something went wrong on our side. Please try again."
                }
              }
            }
          }
        }
      }
    }
  }
}