{
    "openapi": "3.0.3",
    "info": {
        "title": "UnitOps API",
        "version": "1.0.0",
        "description": "Read-only access to the hire catalogue of the company whose key you are holding, and to how many of a thing are free between two dates. Nothing here can change a record.\n\n## Authentication\n\nEvery request carries `Authorization: Bearer <key>`. Keys are issued by a company administrator from **Settings → API keys**, begin `uo_`, and are shown **once** when created - nobody, including whoever runs the installation, can read one back afterwards. A key that has leaked is revoked and replaced; there is no un-revoke.\n\n## What a key can see\n\nA key belongs to one company and reads only that company's records. There is nothing you can pass to widen that, which is why no endpoint takes a company identifier. Cost data is deliberately absent from every response: what was paid for a piece of kit, what it would cost to replace, internal notes and which shelf a unit sits on are not published, because a key usually ends up with whoever builds the website.\n\n## Rate limiting\n\n120 requests per clock minute, per key. Every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`; a refusal adds `Retry-After`. Refused requests count against the allowance, so a client that ignores 429 and retries at once stays refused.\n\n## Calling this from a browser\n\nYou cannot, and that is deliberate: no CORS headers are sent. A key used in browser code is a key published to every visitor, and it reads a whole catalogue. Call it from your server.\n\n## Paging\n\nList endpoints take `page` and `per_page` (default 50, maximum 100) and answer with a `meta` object saying what you actually got. A `per_page` above the maximum is clamped rather than refused, and `meta` is where you find out that it was.\n\n## Money and dates\n\nMoney is a **string** with two decimal places, with its currency code beside it - a JSON number loses `12.10` to `12.1` on the way through somebody else's language. Dates are `YYYY-MM-DD`. Timestamps are ISO 8601, and are null rather than an epoch when unset.\n\n## Versioning\n\nFields are added to responses without notice; nothing is removed or changed in meaning within `v1`. Ignore what you do not recognise.",
        "termsOfService": "https://unitops.co.uk/terms"
    },
    "servers": [
        {
            "url": "https://unitops.co.uk/api/v1",
            "description": "This UnitOps installation"
        }
    ],
    "security": [
        {
            "ApiKey": []
        }
    ],
    "tags": [
        {
            "name": "Service",
            "description": "Finding out what is here and whether your key works."
        },
        {
            "name": "Equipment",
            "description": "The hire catalogue, and what is free between two dates."
        },
        {
            "name": "Bookings",
            "description": "The diary, and requesting a job from outside."
        },
        {
            "name": "Clients",
            "description": "Who the company hires to."
        },
        {
            "name": "Invoicing",
            "description": "Invoices, estimates and what has been paid."
        },
        {
            "name": "Staff",
            "description": "The team, and what each of them can do."
        }
    ],
    "paths": {
        "/": {
            "get": {
                "tags": [
                    "Service"
                ],
                "operationId": "getIndex",
                "summary": "What this API offers, and whether your key works",
                "description": "The first call to make when setting an integration up: it proves the key is good and lists the endpoints this version has. Behind a key like everything else - an open discovery endpoint would be one more thing on the internet answering questions about this installation.",
                "responses": {
                    "200": {
                        "description": "The service description.",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/XRateLimitLimit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/XRateLimitRemaining"
                            },
                            "X-RateLimit-Reset": {
                                "$ref": "#/components/headers/XRateLimitReset"
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Index"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    },
                    "503": {
                        "$ref": "#/components/responses/Unavailable"
                    }
                }
            }
        },
        "/openapi.json": {
            "get": {
                "tags": [
                    "Service"
                ],
                "operationId": "getOpenApi",
                "summary": "This document",
                "description": "The OpenAPI description you are reading, served by the installation itself so it always describes the version actually running. Import it into a client generator, Postman or Insomnia.",
                "responses": {
                    "200": {
                        "description": "The OpenAPI document.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    },
                    "503": {
                        "$ref": "#/components/responses/Unavailable"
                    }
                }
            }
        },
        "/equipment": {
            "get": {
                "tags": [
                    "Equipment"
                ],
                "operationId": "listEquipment",
                "summary": "The hire catalogue",
                "description": "Products, not physical units. Active products only unless `include_inactive=1`: a catalogue feed wants what can be hired today, and kit retired last year appearing on a website is the failure that default avoids.",
                "parameters": [
                    {
                        "$ref": "#/components/parameters/page"
                    },
                    {
                        "$ref": "#/components/parameters/perPage"
                    },
                    {
                        "name": "search",
                        "in": "query",
                        "required": false,
                        "description": "Matches name, SKU, manufacturer or model. `%` and `_` are searched for literally rather than as wildcards.",
                        "schema": {
                            "type": "string"
                        },
                        "example": "parcan"
                    },
                    {
                        "name": "category_id",
                        "in": "query",
                        "required": false,
                        "description": "Limit to one category. An id belonging to another company matches nothing rather than telling you it exists.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "include_inactive",
                        "in": "query",
                        "required": false,
                        "description": "Set to `1` to include retired products, for reconciling a list against ours.",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "1"
                            ]
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "A page of the catalogue.",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/XRateLimitLimit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/XRateLimitRemaining"
                            },
                            "X-RateLimit-Reset": {
                                "$ref": "#/components/headers/XRateLimitReset"
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/EquipmentList"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    },
                    "503": {
                        "$ref": "#/components/responses/Unavailable"
                    }
                }
            }
        },
        "/equipment/availability": {
            "get": {
                "tags": [
                    "Equipment"
                ],
                "operationId": "getEquipmentAvailability",
                "summary": "How many of one product are free between two dates",
                "description": "Counts confirmed bookings **and** kit lent to partner companies, through the same function the booking screens use - so this and the office cannot answer a customer two different numbers on the same afternoon. Floored at zero: UnitOps warns rather than blocks on overbooking, and a negative number has no business on a website.",
                "parameters": [
                    {
                        "name": "equipment_id",
                        "in": "query",
                        "required": true,
                        "description": "The product to ask about.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "start",
                        "in": "query",
                        "required": true,
                        "description": "First day of the window, inclusive.",
                        "schema": {
                            "type": "string",
                            "format": "date"
                        },
                        "example": "2026-09-01"
                    },
                    {
                        "name": "end",
                        "in": "query",
                        "required": true,
                        "description": "Last day of the window, inclusive.",
                        "schema": {
                            "type": "string",
                            "format": "date"
                        },
                        "example": "2026-09-05"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "What is free over that window.",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/XRateLimitLimit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/XRateLimitRemaining"
                            },
                            "X-RateLimit-Reset": {
                                "$ref": "#/components/headers/XRateLimitReset"
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Availability"
                                }
                            }
                        }
                    },
                    "400": {
                        "$ref": "#/components/responses/InvalidRequest"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    },
                    "503": {
                        "$ref": "#/components/responses/Unavailable"
                    }
                }
            }
        },
        "/bookings": {
            "get": {
                "tags": [
                    "Bookings"
                ],
                "operationId": "listBookings",
                "summary": "The diary",
                "description": "Ordered by start date, newest first, because the question this answers is \"what is on\". Carries no money: what a job was charged for is on its invoice, under a scope somebody grants separately.",
                "security": [
                    {
                        "ApiKey": [
                            "bookings:read"
                        ]
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/page"
                    },
                    {
                        "$ref": "#/components/parameters/perPage"
                    },
                    {
                        "name": "from",
                        "in": "query",
                        "required": false,
                        "description": "Anything still running on or after this day, so a festival that began last week is included.",
                        "schema": {
                            "type": "string",
                            "format": "date"
                        }
                    },
                    {
                        "name": "to",
                        "in": "query",
                        "required": false,
                        "description": "Anything starting on or before this day.",
                        "schema": {
                            "type": "string",
                            "format": "date"
                        }
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "enum": [
                                "quote",
                                "confirmed",
                                "in_progress",
                                "completed",
                                "cancelled"
                            ]
                        }
                    },
                    {
                        "name": "client_id",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "A page of the diary.",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/XRateLimitLimit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/XRateLimitRemaining"
                            },
                            "X-RateLimit-Reset": {
                                "$ref": "#/components/headers/XRateLimitReset"
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/BookingList"
                                }
                            }
                        }
                    },
                    "400": {
                        "$ref": "#/components/responses/InvalidRequest"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    },
                    "503": {
                        "$ref": "#/components/responses/Unavailable"
                    }
                }
            },
            "post": {
                "tags": [
                    "Bookings"
                ],
                "operationId": "requestBooking",
                "summary": "Request a booking",
                "description": "Creates an enquiry as a **quote**. It confirms nothing and **holds no kit**.\n\nThat second part is deliberate and is the thing to understand before using this. A quote in UnitOps holds stock, because a quote is normally something a member of staff wrote down and is standing behind. A request from outside is not, so it arrives with no equipment lines on it - otherwise anybody could empty a company's availability by submitting a form repeatedly. What you ask for is recorded on the booking in words, and somebody at the company puts the kit on it when they accept.\n\n`client_id` is required, because a booking without a customer cannot exist here. A public enquiry form nominates one client for the purpose - \"Website enquiries\" - and puts the real enquirer in `contact`.",
                "security": [
                    {
                        "ApiKey": [
                            "bookings:write"
                        ]
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/BookingRequest"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "The request was recorded as a quote.",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/XRateLimitLimit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/XRateLimitRemaining"
                            },
                            "X-RateLimit-Reset": {
                                "$ref": "#/components/headers/XRateLimitReset"
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/BookingRequestAccepted"
                                }
                            }
                        }
                    },
                    "400": {
                        "$ref": "#/components/responses/InvalidRequest"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    },
                    "503": {
                        "$ref": "#/components/responses/Unavailable"
                    }
                }
            }
        },
        "/bookings/{id}": {
            "get": {
                "tags": [
                    "Bookings"
                ],
                "operationId": "getBooking",
                "summary": "One booking, with its kit and team",
                "description": "The lines the list deliberately leaves off. Team members are named and nothing more: how to contact somebody and what they cost is employment data, and a key is not a person with a reason to see it.",
                "security": [
                    {
                        "ApiKey": [
                            "bookings:read"
                        ]
                    }
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The booking.",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/XRateLimitLimit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/XRateLimitRemaining"
                            },
                            "X-RateLimit-Reset": {
                                "$ref": "#/components/headers/XRateLimitReset"
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/BookingDetail"
                                }
                            }
                        }
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    },
                    "503": {
                        "$ref": "#/components/responses/Unavailable"
                    }
                }
            }
        },
        "/clients": {
            "get": {
                "tags": [
                    "Clients"
                ],
                "operationId": "listClients",
                "summary": "The customer list",
                "description": "Who they are and how to reach them. What the company decided about their credit, terms and discount is a commercial position rather than a contact record, and is not published.",
                "security": [
                    {
                        "ApiKey": [
                            "clients:read"
                        ]
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/page"
                    },
                    {
                        "$ref": "#/components/parameters/perPage"
                    },
                    {
                        "name": "search",
                        "in": "query",
                        "required": false,
                        "description": "Matches name, reference or email.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "include_inactive",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "enum": [
                                "1"
                            ]
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "A page of clients.",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/XRateLimitLimit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/XRateLimitRemaining"
                            },
                            "X-RateLimit-Reset": {
                                "$ref": "#/components/headers/XRateLimitReset"
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ClientList"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    },
                    "503": {
                        "$ref": "#/components/responses/Unavailable"
                    }
                }
            }
        },
        "/clients/{id}": {
            "get": {
                "tags": [
                    "Clients"
                ],
                "operationId": "getClient",
                "summary": "One client",
                "security": [
                    {
                        "ApiKey": [
                            "clients:read"
                        ]
                    }
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The client.",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/XRateLimitLimit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/XRateLimitRemaining"
                            },
                            "X-RateLimit-Reset": {
                                "$ref": "#/components/headers/XRateLimitReset"
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ClientDetail"
                                }
                            }
                        }
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    },
                    "503": {
                        "$ref": "#/components/responses/Unavailable"
                    }
                }
            }
        },
        "/invoices": {
            "get": {
                "tags": [
                    "Invoicing"
                ],
                "operationId": "listInvoices",
                "summary": "Invoices and estimates",
                "description": "Both document types, told apart by `type`. What has been paid is summed from the payments against each document rather than stored, which is the rule the whole module follows - there is no paid flag anywhere and this does not invent one.",
                "security": [
                    {
                        "ApiKey": [
                            "invoices:read"
                        ]
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/page"
                    },
                    {
                        "$ref": "#/components/parameters/perPage"
                    },
                    {
                        "name": "type",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "enum": [
                                "invoice",
                                "estimate"
                            ]
                        }
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "enum": [
                                "draft",
                                "sent",
                                "paid",
                                "cancelled",
                                "accepted",
                                "declined"
                            ]
                        }
                    },
                    {
                        "name": "client_id",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "A page of documents.",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/XRateLimitLimit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/XRateLimitRemaining"
                            },
                            "X-RateLimit-Reset": {
                                "$ref": "#/components/headers/XRateLimitReset"
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/InvoiceList"
                                }
                            }
                        }
                    },
                    "400": {
                        "$ref": "#/components/responses/InvalidRequest"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    },
                    "503": {
                        "$ref": "#/components/responses/Unavailable"
                    }
                }
            }
        },
        "/invoices/{id}": {
            "get": {
                "tags": [
                    "Invoicing"
                ],
                "operationId": "getInvoice",
                "summary": "One document, with its lines",
                "security": [
                    {
                        "ApiKey": [
                            "invoices:read"
                        ]
                    }
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The document.",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/XRateLimitLimit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/XRateLimitRemaining"
                            },
                            "X-RateLimit-Reset": {
                                "$ref": "#/components/headers/XRateLimitReset"
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/InvoiceDetail"
                                }
                            }
                        }
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    },
                    "503": {
                        "$ref": "#/components/responses/Unavailable"
                    }
                }
            }
        },
        "/staff": {
            "get": {
                "tags": [
                    "Staff"
                ],
                "operationId": "listStaff",
                "summary": "The team, and what each of them can do",
                "description": "**No contact details, ever** - no email, no phone, no emergency contact. These are the company's own employees and the thing a key ends up pasted into is a website. What this answers is the rota question: who is there and what are they qualified for.",
                "security": [
                    {
                        "ApiKey": [
                            "staff:read"
                        ]
                    }
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/page"
                    },
                    {
                        "$ref": "#/components/parameters/perPage"
                    },
                    {
                        "name": "include_inactive",
                        "in": "query",
                        "required": false,
                        "description": "Include people whose employment here is not active.",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "1"
                            ]
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "A page of team members.",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/XRateLimitLimit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/XRateLimitRemaining"
                            },
                            "X-RateLimit-Reset": {
                                "$ref": "#/components/headers/XRateLimitReset"
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/StaffList"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    },
                    "503": {
                        "$ref": "#/components/responses/Unavailable"
                    }
                }
            }
        },
        "/equipment/{id}": {
            "get": {
                "tags": [
                    "Equipment"
                ],
                "operationId": "getEquipment",
                "summary": "One product, with a count of its units by condition",
                "description": "The counts and not the units themselves: which asset numbers a company owns and which shelf each is on is inventory detail with no bearing on what can be hired.",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The product.",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/XRateLimitLimit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/XRateLimitRemaining"
                            },
                            "X-RateLimit-Reset": {
                                "$ref": "#/components/headers/XRateLimitReset"
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/EquipmentDetail"
                                }
                            }
                        }
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/PaymentRequired"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    },
                    "503": {
                        "$ref": "#/components/responses/Unavailable"
                    }
                }
            }
        }
    },
    "components": {
        "securitySchemes": {
            "ApiKey": {
                "type": "http",
                "scheme": "bearer",
                "description": "A key from Settings → API keys, sent as `Authorization: Bearer uo_...`."
            }
        },
        "schemas": {
            "Error": {
                "type": "object",
                "required": [
                    "error"
                ],
                "properties": {
                    "error": {
                        "type": "object",
                        "required": [
                            "code",
                            "message"
                        ],
                        "properties": {
                            "code": {
                                "type": "string",
                                "description": "Stable and machine-readable. Switch on this.",
                                "enum": [
                                    "invalid_request",
                                    "unauthorized",
                                    "payment_required",
                                    "forbidden",
                                    "insufficient_scope",
                                    "origin_not_allowed",
                                    "not_found",
                                    "rate_limited",
                                    "server_error",
                                    "not_in_sandbox",
                                    "unavailable"
                                ]
                            },
                            "message": {
                                "type": "string",
                                "description": "For whoever is reading the response while writing the integration. May be reworded at any time."
                            }
                        }
                    }
                }
            },
            "Meta": {
                "type": "object",
                "description": "What you actually got, which is not always what you asked for.",
                "required": [
                    "page",
                    "per_page",
                    "total",
                    "pages"
                ],
                "properties": {
                    "page": {
                        "type": "integer",
                        "example": 1
                    },
                    "per_page": {
                        "type": "integer",
                        "example": 50
                    },
                    "total": {
                        "type": "integer",
                        "description": "Records matching the filter, across all pages."
                    },
                    "pages": {
                        "type": "integer"
                    }
                }
            },
            "Money": {
                "type": "string",
                "description": "Two decimal places, as a string. See the note on money above.",
                "example": "45.00"
            },
            "Index": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "name": {
                                "type": "string",
                                "example": "UnitOps API"
                            },
                            "version": {
                                "type": "string",
                                "example": "v1"
                            },
                            "documentation": {
                                "type": "string",
                                "format": "uri"
                            },
                            "rate_limit_per_minute": {
                                "type": "integer",
                                "example": 120
                            },
                            "endpoints": {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                }
                            }
                        }
                    }
                }
            },
            "Equipment": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "sku": {
                        "type": "string",
                        "example": "PAR64-1KW"
                    },
                    "name": {
                        "type": "string",
                        "example": "Par 64 1kW"
                    },
                    "description": {
                        "type": "string",
                        "nullable": true
                    },
                    "manufacturer": {
                        "type": "string",
                        "nullable": true
                    },
                    "model": {
                        "type": "string",
                        "nullable": true
                    },
                    "category": {
                        "type": "object",
                        "nullable": true,
                        "description": "Null where the product is in no category.",
                        "properties": {
                            "id": {
                                "type": "integer"
                            },
                            "name": {
                                "type": "string"
                            }
                        }
                    },
                    "quantity_total": {
                        "type": "integer",
                        "description": "How many the company owns. Not how many are free - ask /equipment/availability for that."
                    },
                    "rates": {
                        "type": "object",
                        "description": "Hire rates in the company's own currency.",
                        "properties": {
                            "currency": {
                                "type": "string",
                                "example": "GBP"
                            },
                            "day": {
                                "$ref": "#/components/schemas/Money"
                            },
                            "week": {
                                "$ref": "#/components/schemas/Money"
                            },
                            "month": {
                                "$ref": "#/components/schemas/Money"
                            }
                        }
                    },
                    "is_active": {
                        "type": "boolean",
                        "description": "False for a retired product, which only appears with `include_inactive=1`."
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    }
                }
            },
            "EquipmentDetail": {
                "type": "object",
                "properties": {
                    "data": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/Equipment"
                            },
                            {
                                "type": "object",
                                "properties": {
                                    "units": {
                                        "type": "object",
                                        "description": "Physical units by condition. Counts only; the units themselves are not published.",
                                        "properties": {
                                            "available": {
                                                "type": "integer"
                                            },
                                            "in_use": {
                                                "type": "integer"
                                            },
                                            "maintenance": {
                                                "type": "integer"
                                            },
                                            "retired": {
                                                "type": "integer"
                                            }
                                        }
                                    }
                                }
                            }
                        ]
                    }
                }
            },
            "EquipmentList": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Equipment"
                        }
                    },
                    "meta": {
                        "$ref": "#/components/schemas/Meta"
                    }
                }
            },
            "ClientRef": {
                "type": "object",
                "nullable": true,
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "name": {
                        "type": "string"
                    }
                }
            },
            "Booking": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "reference": {
                        "type": "string",
                        "example": "EV-0042"
                    },
                    "name": {
                        "type": "string"
                    },
                    "description": {
                        "type": "string",
                        "nullable": true
                    },
                    "status": {
                        "type": "string",
                        "enum": [
                            "quote",
                            "confirmed",
                            "in_progress",
                            "completed",
                            "cancelled"
                        ]
                    },
                    "type": {
                        "type": "string",
                        "nullable": true
                    },
                    "categories": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        },
                        "description": "The company\\x27s own labels for the booking - Production, Installation. Defined per company, unlike type."
                    },
                    "client": {
                        "$ref": "#/components/schemas/ClientRef"
                    },
                    "dates": {
                        "type": "object",
                        "description": "start and end are the job; setup and strike are when the kit is actually out, which is the wider window.",
                        "properties": {
                            "start": {
                                "type": "string",
                                "format": "date",
                                "nullable": true
                            },
                            "end": {
                                "type": "string",
                                "format": "date",
                                "nullable": true
                            },
                            "setup": {
                                "type": "string",
                                "format": "date",
                                "nullable": true
                            },
                            "strike": {
                                "type": "string",
                                "format": "date",
                                "nullable": true
                            }
                        }
                    },
                    "venue": {
                        "type": "object",
                        "properties": {
                            "name": {
                                "type": "string",
                                "nullable": true
                            },
                            "address": {
                                "type": "string",
                                "nullable": true
                            }
                        }
                    },
                    "notes": {
                        "type": "string",
                        "nullable": true,
                        "description": "The customer-facing note. Internal notes are never published."
                    },
                    "is_recurring": {
                        "type": "boolean",
                        "description": "One of a repeating run."
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    }
                }
            },
            "BookingList": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Booking"
                        }
                    },
                    "meta": {
                        "$ref": "#/components/schemas/Meta"
                    }
                }
            },
            "BookingDetail": {
                "type": "object",
                "properties": {
                    "data": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/Booking"
                            },
                            {
                                "type": "object",
                                "properties": {
                                    "equipment": {
                                        "type": "array",
                                        "items": {
                                            "type": "object",
                                            "properties": {
                                                "equipment_id": {
                                                    "type": "integer",
                                                    "nullable": true,
                                                    "description": "Null for a one-off line that is not in the catalogue - something sub-hired or bought in for this job. Its name is the description it was given."
                                                },
                                                "sku": {
                                                    "type": "string"
                                                },
                                                "name": {
                                                    "type": "string"
                                                },
                                                "quantity": {
                                                    "type": "integer"
                                                }
                                            }
                                        }
                                    },
                                    "crew": {
                                        "type": "array",
                                        "description": "Names only.",
                                        "items": {
                                            "type": "object",
                                            "properties": {
                                                "id": {
                                                    "type": "integer"
                                                },
                                                "name": {
                                                    "type": "string"
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        ]
                    }
                }
            },
            "BookingRequest": {
                "type": "object",
                "required": [
                    "client_id",
                    "name",
                    "start",
                    "end"
                ],
                "properties": {
                    "client_id": {
                        "type": "integer",
                        "description": "An existing client. A booking cannot exist without one, and this endpoint deliberately cannot create one."
                    },
                    "name": {
                        "type": "string",
                        "maxLength": 255
                    },
                    "start": {
                        "type": "string",
                        "format": "date"
                    },
                    "end": {
                        "type": "string",
                        "format": "date"
                    },
                    "venue_name": {
                        "type": "string",
                        "nullable": true
                    },
                    "contact": {
                        "type": "string",
                        "description": "Who is asking, where that is not the client - the actual enquirer from a website form. Free text, written onto the booking for a person to read."
                    },
                    "notes": {
                        "type": "string",
                        "description": "Anything else worth saying."
                    },
                    "equipment": {
                        "type": "array",
                        "description": "What is being asked for. Every id is checked, and **none of it is allocated** - it is written onto the booking in words for somebody to action.",
                        "items": {
                            "type": "object",
                            "required": [
                                "equipment_id",
                                "quantity"
                            ],
                            "properties": {
                                "equipment_id": {
                                    "type": "integer"
                                },
                                "quantity": {
                                    "type": "integer",
                                    "minimum": 1
                                }
                            }
                        }
                    }
                }
            },
            "BookingRequestAccepted": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "integer"
                            },
                            "reference": {
                                "type": "string"
                            },
                            "status": {
                                "type": "string",
                                "example": "quote"
                            },
                            "name": {
                                "type": "string"
                            },
                            "client_id": {
                                "type": "integer"
                            },
                            "dates": {
                                "type": "object",
                                "properties": {
                                    "start": {
                                        "type": "string",
                                        "format": "date"
                                    },
                                    "end": {
                                        "type": "string",
                                        "format": "date"
                                    }
                                }
                            },
                            "requested_equipment": {
                                "type": "array",
                                "description": "Echoed back so you can show the enquirer what was understood. Not an allocation.",
                                "items": {
                                    "type": "object",
                                    "properties": {
                                        "equipment_id": {
                                            "type": "integer"
                                        },
                                        "sku": {
                                            "type": "string"
                                        },
                                        "name": {
                                            "type": "string"
                                        },
                                        "quantity": {
                                            "type": "integer"
                                        }
                                    }
                                }
                            },
                            "holds_stock": {
                                "type": "boolean",
                                "description": "Always false. Present so an integration can say so to whoever is waiting."
                            },
                            "message": {
                                "type": "string"
                            }
                        }
                    }
                }
            },
            "Client": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "reference": {
                        "type": "string",
                        "example": "CL-0007"
                    },
                    "name": {
                        "type": "string"
                    },
                    "contact_name": {
                        "type": "string",
                        "nullable": true
                    },
                    "email": {
                        "type": "string",
                        "nullable": true
                    },
                    "phone": {
                        "type": "string",
                        "nullable": true
                    },
                    "website": {
                        "type": "string",
                        "nullable": true
                    },
                    "address": {
                        "type": "string",
                        "nullable": true
                    },
                    "tax_number": {
                        "type": "string",
                        "nullable": true
                    },
                    "is_active": {
                        "type": "boolean"
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    }
                }
            },
            "ClientList": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Client"
                        }
                    },
                    "meta": {
                        "$ref": "#/components/schemas/Meta"
                    }
                }
            },
            "ClientDetail": {
                "type": "object",
                "properties": {
                    "data": {
                        "$ref": "#/components/schemas/Client"
                    }
                }
            },
            "Invoice": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "type": {
                        "type": "string",
                        "enum": [
                            "invoice",
                            "estimate"
                        ]
                    },
                    "number": {
                        "type": "string",
                        "nullable": true,
                        "description": "Null while it is a draft: a number is issued when it is sent."
                    },
                    "status": {
                        "type": "string"
                    },
                    "client": {
                        "$ref": "#/components/schemas/ClientRef"
                    },
                    "booking_id": {
                        "type": "integer",
                        "nullable": true
                    },
                    "reference": {
                        "type": "string",
                        "nullable": true,
                        "description": "The client's own reference or PO number."
                    },
                    "date": {
                        "type": "string",
                        "format": "date",
                        "nullable": true
                    },
                    "due_date": {
                        "type": "string",
                        "format": "date",
                        "nullable": true
                    },
                    "valid_until": {
                        "type": "string",
                        "format": "date",
                        "nullable": true
                    },
                    "currency": {
                        "type": "string",
                        "example": "GBP"
                    },
                    "amounts": {
                        "type": "object",
                        "description": "All strings, in the document's own currency, frozen when it was issued.",
                        "properties": {
                            "subtotal": {
                                "$ref": "#/components/schemas/Money"
                            },
                            "discount": {
                                "$ref": "#/components/schemas/Money"
                            },
                            "tax_rate": {
                                "$ref": "#/components/schemas/Money"
                            },
                            "tax": {
                                "$ref": "#/components/schemas/Money"
                            },
                            "total": {
                                "$ref": "#/components/schemas/Money"
                            },
                            "paid": {
                                "$ref": "#/components/schemas/Money"
                            },
                            "outstanding": {
                                "$ref": "#/components/schemas/Money"
                            }
                        }
                    },
                    "sent_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    }
                }
            },
            "InvoiceList": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Invoice"
                        }
                    },
                    "meta": {
                        "$ref": "#/components/schemas/Meta"
                    }
                }
            },
            "InvoiceDetail": {
                "type": "object",
                "properties": {
                    "data": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/Invoice"
                            },
                            {
                                "type": "object",
                                "properties": {
                                    "lines": {
                                        "type": "array",
                                        "items": {
                                            "type": "object",
                                            "properties": {
                                                "description": {
                                                    "type": "string"
                                                },
                                                "quantity": {
                                                    "type": "number"
                                                },
                                                "days": {
                                                    "type": "number",
                                                    "description": "How many of `unit`: 3 days, 2 weeks. Always 1 for `each` and `job`. A whole number is sent as an integer."
                                                },
                                                "unit": {
                                                    "type": "string",
                                                    "enum": [
                                                        "day",
                                                        "week",
                                                        "hour",
                                                        "job",
                                                        "each"
                                                    ],
                                                    "description": "What `unit_price` is per. The amount is quantity x days x unit_price whatever this is."
                                                },
                                                "section": {
                                                    "type": "string",
                                                    "nullable": true,
                                                    "description": "The heading the line is grouped under on the document, or null. Lines are listed in document order: unsectioned first, then each section in turn."
                                                },
                                                "unit_price": {
                                                    "$ref": "#/components/schemas/Money"
                                                },
                                                "charge_percent": {
                                                    "type": "number",
                                                    "nullable": true,
                                                    "description": "Set on a charge priced as a percentage of the other lines - a 10% damage waiver. `unit_price` and `amount` are then the figure that percentage came to."
                                                },
                                                "vat_rate": {
                                                    "type": "number",
                                                    "description": "The VAT rate, percent, this line is charged at - its own where it has one, else the document tax_rate. 0 on an exempt line."
                                                },
                                                "vat_exempt": {
                                                    "type": "boolean",
                                                    "description": "True when the line is exempt from VAT, as distinct from zero-rated. Both carry no VAT, but a VAT return counts zero-rated sales and not exempt ones."
                                                },
                                                "amount": {
                                                    "$ref": "#/components/schemas/Money"
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        ]
                    }
                }
            },
            "StaffMember": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "reference": {
                        "type": "string",
                        "nullable": true,
                        "example": "ST-0003"
                    },
                    "name": {
                        "type": "string"
                    },
                    "role": {
                        "type": "string",
                        "description": "This company's own role name."
                    },
                    "job_title": {
                        "type": "string",
                        "nullable": true
                    },
                    "status": {
                        "type": "string",
                        "example": "active"
                    },
                    "start_date": {
                        "type": "string",
                        "format": "date",
                        "nullable": true
                    },
                    "skills": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        }
                    }
                }
            },
            "StaffList": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/StaffMember"
                        }
                    },
                    "meta": {
                        "$ref": "#/components/schemas/Meta"
                    }
                }
            },
            "Availability": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "equipment_id": {
                                "type": "integer"
                            },
                            "sku": {
                                "type": "string"
                            },
                            "name": {
                                "type": "string"
                            },
                            "start": {
                                "type": "string",
                                "format": "date"
                            },
                            "end": {
                                "type": "string",
                                "format": "date"
                            },
                            "total": {
                                "type": "integer",
                                "description": "How many the company owns."
                            },
                            "committed": {
                                "type": "integer",
                                "description": "How many are spoken for over that window, counting bookings and kit lent to partners."
                            },
                            "available": {
                                "type": "integer",
                                "description": "total less committed, floored at zero."
                            }
                        }
                    }
                }
            }
        },
        "responses": {
            "InvalidRequest": {
                "description": "`invalid_request` - The request was malformed. A required parameter is missing, or a date is not YYYY-MM-DD, or the end of a window is before its start.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "code": "invalid_request",
                                "message": "The request was malformed."
                            }
                        }
                    }
                }
            },
            "Unauthorized": {
                "description": "`unauthorized` - No usable key was presented. The Authorization header was missing, malformed, or carried a key that is unknown or has been revoked. A revoked key cannot be un-revoked; issue a new one.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "code": "unauthorized",
                                "message": "No usable key was presented."
                            }
                        }
                    }
                }
            },
            "PaymentRequired": {
                "description": "`payment_required` - The subscription has lapsed. The key is good and the company it belongs to is not currently paying for UnitOps. Nothing about the key needs changing; the account does.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "code": "payment_required",
                                "message": "The subscription has lapsed."
                            }
                        }
                    }
                }
            },
            "Forbidden": {
                "description": "One of:\n\n- `forbidden` - The company has not bought this module. The key is good and the company does not hold the module this endpoint reads. Distinct from 401 on purpose: nothing the caller can do to their key will fix it.\n- `insufficient_scope` - The key was not given this scope. The key is good and the company holds the module, but this scope was not ticked when the key was issued. Scopes are fixed at issue - there is no editing one, because a key whose powers can grow is a key nobody can reason about from the fact that it once existed. Issue a new key with the scope on it.\n- `origin_not_allowed` - A publishable key, used from a site it does not answer for. The request came from a browser on an origin that is not on this key's allowlist. Add the origin to the key, or use the right key. Only ever seen by browser callers: a server sends no Origin header and is not checked against the list.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "code": "forbidden",
                                "message": "The company has not bought this module."
                            }
                        }
                    }
                }
            },
            "NotFound": {
                "description": "`not_found` - No such record, or no such endpoint. One answer covers a record that does not exist and a record belonging to another company. That is deliberate - telling the two apart would make this a way of counting a competitor's stock.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "code": "not_found",
                                "message": "No such record, or no such endpoint."
                            }
                        }
                    }
                }
            },
            "RateLimited": {
                "description": "`rate_limited` - The allowance for this minute is spent. Retry-After says how many seconds until the count resets. Refused requests are counted too, so retrying immediately does not help.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "code": "rate_limited",
                                "message": "The allowance for this minute is spent."
                            }
                        }
                    }
                }
            },
            "ServerError": {
                "description": "`server_error` - Something went wrong at our end. A fault in UnitOps rather than anything about the request. Deliberately says nothing further - a stack trace in a JSON body is a disclosure, and this endpoint is reachable by anybody holding any key. Worth retrying once; if it persists, whoever runs the installation has it in their error log.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "code": "server_error",
                                "message": "Something went wrong at our end."
                            }
                        }
                    }
                }
            },
            "NotInSandbox": {
                "description": "`not_in_sandbox` - That endpoint has no sandbox. Only ever seen by a test key. The sandbox covers the catalogue, availability, clients and bookings; invoices and staff have no fixture, because inventing somebody's accounts and somebody's employees to practise against is a lot of made-up data for two endpoints an integration reads once. Said out loud rather than answered with an empty list, which would read as \"this company has no staff\" and send somebody looking for a bug in their own code.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "code": "not_in_sandbox",
                                "message": "That endpoint has no sandbox."
                            }
                        }
                    }
                }
            },
            "Unavailable": {
                "description": "`unavailable` - The API is not available on this installation. This UnitOps installation has not had the API migration applied. Nothing the caller can do; whoever runs it needs to apply migration 038.",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "code": "unavailable",
                                "message": "The API is not available on this installation."
                            }
                        }
                    }
                }
            }
        },
        "parameters": {
            "page": {
                "name": "page",
                "in": "query",
                "required": false,
                "description": "Which page to return, from 1.",
                "schema": {
                    "type": "integer",
                    "minimum": 1,
                    "default": 1
                }
            },
            "perPage": {
                "name": "per_page",
                "in": "query",
                "required": false,
                "description": "How many records per page. Above the maximum is clamped to it, and `meta.per_page` says what was used.",
                "schema": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 100,
                    "default": 50
                }
            }
        },
        "headers": {
            "XRateLimitLimit": {
                "description": "Requests allowed per clock minute for this key.",
                "schema": {
                    "type": "integer",
                    "example": 120
                }
            },
            "XRateLimitRemaining": {
                "description": "How many of this minute's allowance are left, including this request.",
                "schema": {
                    "type": "integer",
                    "example": 119
                }
            },
            "XRateLimitReset": {
                "description": "Seconds until the allowance resets.",
                "schema": {
                    "type": "integer",
                    "example": 24
                }
            }
        }
    }
}