{
    "openapi": "3.1.0",
    "info": {
        "title": "Suitiee Channel API",
        "version": "1.0.0",
        "summary": "List a host's units on your platform, read their calendar, and send the bookings back.",
        "description": "The **Suitiee Channel API** is how a booking platform — a marketplace, a regional OTA, a corporate travel tool — sells units that a host manages in Suitiee. You read their listings and their calendars, you create the listing on your own site, and you tell us when a guest books.\n\nSuitiee carries no channel-specific code. Everything below is the same for every platform, which is what lets you integrate without waiting for us to build anything for you.\n\n## The shape of an integration\n\n1. A host on your platform presses **Connect Suitiee**. You send them to `/channel/authorize`; they choose which of their units you may see and approve.\n2. You exchange the code for an access token and a refresh token (`POST /channel/oauth/token`).\n3. `GET /channel/units` → for each unit, `GET /channel/units/{unit}/listing` → create your listing → tell us its id with `PUT /channel/units/{unit}/listing-link`.\n4. `GET /channel/units/{unit}/calendar?from=&to=` → seed your availability, prices and minimum stays.\n5. From then on we POST you signed events when something changes, and you re-read the endpoint the event points at.\n6. A guest books on your platform → `POST /channel/bookings`. The host sees the stay on their Suitiee calendar within the request.\n\n## Three things that will otherwise cost you a day\n\n**An unpriced night is published closed.** `rate: null` always comes with `available: false` and `reason: \"unpriced\"`. Nobody has said what that night costs, so there is no honest price to sell it at. Do not fall back to a default of your own.\n\n**Our events are pointers, not values.** `availability.changed` says *which nights of which unit* moved; it never says what they moved to. Re-read the calendar for the ranges named. This is deliberate: delivery is retried for a day and a half and is not ordered, so an event carrying a value could write an old price over a new one. A pointer cannot. Every calendar and listing event carries `sync_seq`; ignore any event whose `sync_seq` is below one you have already acted on.\n\n**An overlapping booking is accepted, not refused.** By default, if you tell us about a stay on nights that are already taken, we record it and answer `201` with `meta.warnings` — because you have usually already sold it to a guest, and refusing would make the clash invisible to the host instead of making it not happen. A conflict is raised for the host to resolve. If your platform calls us *before* confirming to the guest, ask us to switch your application to strict mode and you will get a `409 NIGHTS_UNAVAILABLE` you can act on.\n\n## Authentication\n\nTwo credentials, and they are used for different things.\n\n**Your application's** client id and secret, over HTTP Basic, authenticate the token endpoint, the revocation endpoint, `GET /channel/connections` and `POST /channel/webhooks/test`. These are questions about your application rather than about one host.\n\n**A connection's access token**, as `Authorization: Bearer …`, authenticates everything else. It resolves to exactly one host and exactly the units they ticked. Access tokens last 30 days and are refreshed with a rotating refresh token; a refresh token is single use, and presenting a spent one revokes the whole connection, because that is what a stolen token looks like.\n\nThere is no account-wide key. A host's consent is the only way to reach a host's data, and they can end it in one click.\n\n## Scopes\n\nAsked for at authorization, shown to the host in their own language, and enforced on every request against **both** the token and the host's current grant — the narrower wins, so a host who narrows a grant is obeyed on your next call rather than in 30 days.\n\n- `units:read` — The name, description, photos, capacity, amenities and address of the units you tick.\n- `calendar:read` — Which nights are free, what you charge for them, and your minimum stay.\n- `bookings:read` — Only stays this platform itself made. Bookings from anywhere else stay private.\n- `bookings:write` — Add a stay when a guest books there, and move or cancel it afterwards.\n- `listings:write` — Tell Suitiee the id of the listing they created, so the two are linked.\n- `units:write` — Send unit details for you to review. Nothing is changed until you accept it.\n- `messages:read` — When you or your operator answer a guest of theirs, the answer is handed to them to deliver. Nothing else about the conversation is shared.\n- `messages:write` — Guests writing on their platform appear in your Suitiee inbox, beside your Booking.com and Airbnb conversations.\n- `calendar:write` — Change what a night costs, your minimum stay, and which nights are open — on your calendar, which every other channel you sell on then follows.\n- `operations:read` — Whether a flat has been cleaned and is ready, and what is booked to be fixed. Never who is doing the work, and never your billing.\n- `maintenance:write` — Raise a maintenance visit when their guest reports something broken. Costs nothing and is quoted to you before any work is charged.\n- `cleaning:write` — Book a clean on your units. THIS SPENDS YOUR BALANCE — each visit is charged to your package or your wallet when it is booked.\n\n## Conventions\n\n- **Base URL** — `https://suitiee.com/api/v1/channel`\n- **Success** — `{ \"data\": …, \"message\": \"…\" }` · **Errors** — `{ \"error\": { \"code\", \"message\", \"details\" } }`\n- **Lists** — `?page=`, `?per_page=` (max 100), `meta` with the page counts\n- **Money** — a decimal string plus an ISO currency, never a JSON number. A rate that has been through a float cannot be reconciled against a payout.\n- **Dates** — nights are `Y-m-d`; datetimes are ISO 8601 with an offset. Stored times are `Asia/Riyadh`.\n- **Ids** — ours are UUIDs (`unit_id`, `connection_id`); yours are opaque strings up to 128 characters.\n- **Idempotency** — send `Idempotency-Key` on every write. The same key with the same body returns the first result; the same key with a different body is a `409`.\n- Every response carries `X-Suitiee-Api-Version: 1` and `X-Request-Id`, including 401s, 422s and 429s.\n\n## Rate limits\n\n120 requests a minute per connection; 10 a minute per address on the token endpoint. A `429` carries `Retry-After`.\n\n## Sandbox\n\nYour application starts in test mode. Everything behaves as it does live — consent, tokens, calendars, bookings — but every row it creates is marked as test data, nothing is billed and no cleaner is dispatched. Going live keeps the same client id, the same secret and the same URLs.\n\n## Webhooks\n\nWe POST to one URL you give us, signed with HMAC-SHA256 over `\"{timestamp}.{body}\"`:\n\n```\nX-Suitiee-Event: availability.changed\nX-Suitiee-Event-Id: 3f2504e0-…          ← deduplicate on this\nX-Suitiee-Timestamp: 1789231200          ← refuse anything older than your own window\nX-Suitiee-Signature-Version: 2\nX-Suitiee-Callback-Signature: sha256=…\n```\n\nAnswer any `2xx`. We retry nine times over about a day and a half; after 24 hours of continuous failure we pause your stream, tell your engineering contact, and hold everything until it is resumed — nothing is dropped. During a secret rotation you will also receive `X-Suitiee-Callback-Signature-Previous` for 24 hours, so you can deploy the new secret on your own schedule."
    },
    "servers": [
        {
            "url": "https://suitiee.com/api/v1",
            "description": "Production"
        }
    ],
    "security": [
        {
            "connectionToken": []
        }
    ],
    "tags": [
        {
            "name": "Connection",
            "description": "Who this token belongs to, and how to give it back. Also the reconciliation list of every host connected to your application."
        },
        {
            "name": "Units",
            "description": "The units a host granted you, and the listing document for each: names, description, capacity, beds, amenities, address, photos, licence and headline price."
        },
        {
            "name": "Calendar",
            "description": "Per night: whether it can be sold, at what price, and under what stay restrictions. The endpoint every change notice points at."
        },
        {
            "name": "Bookings",
            "description": "Tell Suitiee when a guest books, moves or cancels on your platform. A booking recorded here appears on the host's calendar within the request and closes the night on every other channel they sell through."
        },
        {
            "name": "Operations",
            "description": "The stay after it is sold: whether the flat has been cleaned and is ready, what can be ordered on it and what that costs this host, and reporting a problem their guest found."
        },
        {
            "name": "Messages",
            "description": "The guest conversation. Post what a guest wrote to you; receive the host's replies as `message.sent` events, and read them back here after an outage."
        },
        {
            "name": "Events",
            "description": "What became of something you sent us, by its idempotency key."
        },
        {
            "name": "Sandbox",
            "description": "A sample of every event, and a button that posts one to your receiver."
        },
        {
            "name": "Outbound Events",
            "description": "What Suitiee POSTs to you when a calendar, a listing, a booking or a connection changes."
        }
    ],
    "paths": {
        "/channel/oauth/token": {
            "post": {
                "tags": [
                    "Connection"
                ],
                "summary": "Exchange an authorization code, or a refresh token, for an access token.",
                "operationId": "oAuthTokenControllerToken",
                "security": [
                    {
                        "appCredentials": []
                    }
                ],
                "description": "Authenticated with your application's own client id and secret over HTTP Basic, not\nwith a connection token -- there is no connection token yet.\n\n`authorization_code` takes `code`, `redirect_uri` (the same one the flow started\nwith) and `code_verifier`. `refresh_token` takes `refresh_token`.\n\nRefresh tokens are SINGLE USE. Each exchange returns a new one; store it and discard\nthe old. Presenting a spent refresh token revokes the whole connection and requires\nthe host to consent again, because that is what a stolen token looks like from here.\n\nThe response is RFC 6749 shaped rather than this API's `{data}` envelope, so your\nexisting OAuth client library parses it unchanged. `connection_id` is our addition:\nstore it against the host account on your side.",
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "token_type": {
                                                    "type": "string",
                                                    "const": "Bearer"
                                                },
                                                "access_token": {
                                                    "type": "string"
                                                },
                                                "refresh_token": {
                                                    "type": "string",
                                                    "description": "Single use. The next refresh returns another."
                                                },
                                                "expires_in": {
                                                    "type": "integer",
                                                    "description": "Seconds. 30 days."
                                                },
                                                "scope": {
                                                    "type": "string",
                                                    "description": "Space-delimited, and never wider than what the host currently grants."
                                                },
                                                "connection_id": {
                                                    "type": "string",
                                                    "format": "uuid"
                                                }
                                            }
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "invalid_request, invalid_grant or unsupported_grant_type. RFC 6749 §5.2 shape, not this API's error envelope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "INVALID_CLIENT — the client id or secret is wrong, or the application is suspended.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/oauth/revoke": {
            "post": {
                "tags": [
                    "Connection"
                ],
                "summary": "Retire a token (RFC 7009).",
                "operationId": "oAuthTokenControllerRevoke",
                "security": [
                    {
                        "appCredentials": []
                    }
                ],
                "description": "Send either an access token or a refresh token as `token`. Revoking a refresh token\nalso kills the access token it minted.\n\nAlways answers 200, whatever was presented -- including a token that never existed.\nA response that differed would be an oracle for guessing token values, and there is\nnothing you could usefully do differently either way.",
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "INVALID_CLIENT — the client id or secret is wrong.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/connections": {
            "get": {
                "tags": [
                    "Connection"
                ],
                "summary": "Every host connected to your application.",
                "operationId": "connectionControllerIndex",
                "description": "Authenticated with your client id and secret rather than with a connection token,\nbecause it is a question about the application rather than about one host. This is\nthe reconciliation endpoint: run it nightly against your own list to find hosts who\ndisconnected while your receiver was down.",
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "default": 1
                        }
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 100,
                            "default": 20
                        }
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "required": false,
                        "description": "connected, revoked or failed.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/Connection"
                                            }
                                        },
                                        "links": {
                                            "type": "object"
                                        },
                                        "meta": {
                                            "$ref": "#/components/schemas/Meta"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/webhooks/test": {
            "post": {
                "tags": [
                    "Sandbox"
                ],
                "summary": "Post a real event to your real receiver, now.",
                "operationId": "webhookSandboxControllerTest",
                "security": [
                    {
                        "appCredentials": []
                    }
                ],
                "description": "Authenticated with your application's own credentials, because it is a question about\nyour application rather than about one host -- and because you want to run it before\nany host has connected.\n\nSent in the foreground: you pressed the button and are waiting, so the response\ncarries the status code your endpoint returned. It is signed exactly as a live event\nis and recorded as a delivery marked `test`, so it can never be mistaken for real\ntraffic and can never pause your live stream if it fails.",
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "delivered": {
                                                    "type": "boolean"
                                                },
                                                "http_status": {
                                                    "type": "integer",
                                                    "nullable": true,
                                                    "description": "What your endpoint answered."
                                                },
                                                "event": {
                                                    "type": "string"
                                                },
                                                "event_id": {
                                                    "type": "string",
                                                    "format": "uuid"
                                                },
                                                "error": {
                                                    "type": "string",
                                                    "nullable": true
                                                }
                                            }
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "INVALID_CLIENT — the client id or secret is wrong.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "NO_WEBHOOK_URL — your application has no receiver configured, or UNKNOWN_EVENT.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/connection": {
            "get": {
                "tags": [
                    "Connection"
                ],
                "summary": "Who this token belongs to.",
                "operationId": "connectionControllerShow",
                "description": "The first call to make after exchanging a code, and the one to make after any\nunexpected 403: it tells you which host you are connected to, what they granted, how\nmany of their units you may see, and whether this is a sandbox connection.",
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/Connection"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "delete": {
                "tags": [
                    "Connection"
                ],
                "summary": "Give up this connection.",
                "operationId": "connectionControllerDestroy",
                "description": "For when the host removed your integration on YOUR side: rather than holding a\ncredential nobody intends you to use, hand it back. Your tokens stop working\nimmediately, the host is shown as disconnected, and any calendar link this connection\nhad superseded goes back on so their bookings keep reaching them.\n\nSafe to repeat. A connection the host already revoked answers 200 and changes\nnothing.",
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "revoked": {
                                                    "type": "boolean"
                                                }
                                            }
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/units": {
            "get": {
                "tags": [
                    "Units"
                ],
                "summary": "The units this host granted you.",
                "operationId": "unitControllerIndex",
                "description": "The first call after connecting. Each entry carries enough to decide what to do next\n-- the name, the city, the capacity, the currency, and the listing link if you have\nrecorded one -- and the full content is `GET /units/{unit}/listing`.\n\nA unit the host unticks stops appearing here on your very next call, and its id then\nanswers 404 everywhere else.",
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "default": 1
                        }
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 100,
                            "default": 20
                        }
                    },
                    {
                        "name": "updated_since",
                        "in": "query",
                        "required": false,
                        "description": "ISO-8601. Only units changed since then.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/Unit"
                                            }
                                        },
                                        "links": {
                                            "type": "object"
                                        },
                                        "meta": {
                                            "$ref": "#/components/schemas/Meta"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/units/{unit}": {
            "get": {
                "tags": [
                    "Units"
                ],
                "summary": "One unit, by its Suitiee id.",
                "operationId": "unitControllerShow",
                "description": "A unit that exists but was not granted to you answers 404, deliberately: a 403 would\nconfirm that the id names a real unit belonging to somebody.",
                "parameters": [
                    {
                        "name": "unit",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/Unit"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "UNIT_NOT_GRANTED — no unit of yours has that id.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/units/{unit}/listing": {
            "get": {
                "tags": [
                    "Units"
                ],
                "summary": "Everything a channel's own listing form asks for, in one document.",
                "operationId": "listingControllerShow",
                "description": "The names in both languages, the description, the capacity and bed layout, the\namenities as canonical codes, the address, the photos as URLs you may fetch, the\ncheck-in and check-out times, the tourism licence and the headline price. Build your\nlisting from it; nothing here needs a person to retype anything.\n\n`hash` is a fingerprint of everything above. Store it, and compare it with the one on\nthe `listing.updated` event to know whether a re-read is worth making. It deliberately\nexcludes the unit's `updated_at`, so an unrelated save does not read as a change.\n\nThe amenity codes are a fixed vocabulary, not free text: map them to your own ids\nonce, on your side. Suitiee carries no channel-specific amenity mapping, which is\nwhat lets a new channel integrate without a change here.",
                "parameters": [
                    {
                        "name": "unit",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "description": "The listing pack. Every key is always present; a value nobody has supplied is null.",
                                            "properties": {
                                                "id": {
                                                    "type": "string",
                                                    "format": "uuid"
                                                },
                                                "name": {
                                                    "type": "object"
                                                },
                                                "description": {
                                                    "type": "object"
                                                },
                                                "capacity": {
                                                    "type": "object"
                                                },
                                                "address": {
                                                    "type": "object"
                                                },
                                                "geo": {
                                                    "type": "object"
                                                },
                                                "amenities": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "string"
                                                    }
                                                },
                                                "photos": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "string"
                                                    }
                                                },
                                                "times": {
                                                    "type": "object"
                                                },
                                                "licence": {
                                                    "type": "object"
                                                },
                                                "pricing": {
                                                    "type": "object"
                                                },
                                                "hash": {
                                                    "type": "string",
                                                    "description": "sha256 of the pack, excluding updated_at and the calendar URL."
                                                }
                                            }
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "UNIT_NOT_GRANTED — no unit of yours has that id.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/units/{unit}/listing-link": {
            "put": {
                "tags": [
                    "Units"
                ],
                "summary": "Record the listing you created, so the two sides are linked.",
                "operationId": "listingControllerLink",
                "description": "Send this as soon as your listing exists. Until you do, Suitiee cannot tell you when\nthat unit's content or calendar changes, the host's own screen cannot say the flat is\non your platform, and `POST /bookings` by `external_listing_id` has nothing to\nresolve.\n\nIdempotent, and safe to send again with a corrected id: the link is replaced and the\nprevious id is kept in Suitiee's audit log, which is what explains a listing that\nstopped updating.\n\nThe moment you link, Suitiee records the listing pack's hash as the baseline for\n`listing.updated` -- so the events start from the content you have just read, not\nfrom nothing.",
                "parameters": [
                    {
                        "name": "unit",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "external_listing_id": {
                                        "type": "string",
                                        "maxLength": 128
                                    },
                                    "url": {
                                        "type": "string",
                                        "maxLength": 2048,
                                        "nullable": true
                                    },
                                    "external_rate_plan_id": {
                                        "type": "string",
                                        "maxLength": 128,
                                        "nullable": true
                                    },
                                    "status": {
                                        "type": "string",
                                        "enum": [
                                            "draft",
                                            "listed",
                                            "synced"
                                        ],
                                        "nullable": true
                                    }
                                },
                                "required": [
                                    "external_listing_id"
                                ]
                            },
                            "example": {
                                "external_listing_id": "L-88213",
                                "url": "https://example.com/units/88213"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/ListingLink"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "UNIT_NOT_GRANTED — no unit of yours has that id.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "LISTING_ID_TAKEN — another of your connections already claims that listing id.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "VALIDATION_ERROR.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "delete": {
                "tags": [
                    "Units"
                ],
                "summary": "You have taken the listing down.",
                "operationId": "listingControllerUnlink",
                "description": "Suitiee stops announcing that unit's changes to you and tells the host their flat is\nno longer listed with you. Nothing is deleted: the id is kept, so re-linking later\nneeds nothing from the host, and their unit page can still say where the flat used to\nsell.\n\nSafe to repeat: a unit that was never linked answers 200.",
                "parameters": [
                    {
                        "name": "unit",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "unlinked": {
                                                    "type": "boolean"
                                                }
                                            }
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "UNIT_NOT_GRANTED — no unit of yours has that id.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/units/{unit}/calendar": {
            "get": {
                "tags": [
                    "Calendar"
                ],
                "summary": "Availability, prices and stay restrictions, one entry per night.",
                "operationId": "calendarControllerShow",
                "description": "The endpoint to read after every `availability.changed` or `rates.changed` event, and\nthe one to seed a new listing from. Everything is derived at the moment of asking, so\nthis response is always the current truth -- which is why the events that point at it\ncarry no values of their own.\n\nA night with no price anywhere answers `rate: null` and `available: false` with\n`reason: \"unpriced\"`. Do not publish it: nobody has said what it costs, and a channel\nthat sells it will sell it at a number Suitiee never agreed to.\n\n`reason` never says WHO booked a night. Bookings made through other channels are not\nvisible to you; they appear here only as closed nights.",
                "parameters": [
                    {
                        "name": "unit",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "from",
                        "in": "query",
                        "required": false,
                        "description": "First night, Y-m-d. Defaults to today.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "to",
                        "in": "query",
                        "required": false,
                        "description": "Last night, Y-m-d. Defaults to 90 nights after `from`.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/Calendar"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "UNIT_NOT_GRANTED — no unit of yours has that id.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "RANGE_TOO_LARGE or BEYOND_HORIZON — see the parameters.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "put": {
                "tags": [
                    "Calendar"
                ],
                "summary": "Set prices, minimum stays and closed nights on a granted unit.",
                "operationId": "calendarControllerUpdate",
                "description": "The same write the host makes on their own calendar screen, through the same service —\nso a price you set here is the price every other channel they sell on is told about,\nwithin the minute for Airbnb and Booking.com and on the next drain for everyone else.\nThere is no separate \"channel rate\": one unit, one calendar, one truth.\n\n**Send a range, not a night.** `from`/`to` are inclusive, and `weekdays` narrows them,\nso \"SAR 600 every Thursday and Friday in July\" is one call. Ninety calls for a season\nis ninety writes here and ninety events at every other channel the host sells on.\n\n**An absent field is left alone; `null` clears it.** Raising a weekend price does not\nrequire resending a minimum stay you never touched — and a partner who resent it by\nhabit would silently overwrite whatever the host had set.\n\n`blocked: true` closes nights you are holding for a reason we cannot see. It is **not**\nhow a booking is recorded: a block carries no guest, no dates to modify and no\ncancellation, so use `POST /bookings` for anything a guest actually reserved.\n\nNothing here can reopen a night that is closed by somebody else's stay. Availability is\nderived from real bookings, and `blocked: false` only withdraws a block of your own.",
                "parameters": [
                    {
                        "name": "unit",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "Idempotency-Key",
                        "in": "header",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "maxLength": 80
                        },
                        "description": "Up to 80 printable ASCII characters, unique per request you intend to make. A retry with the same key returns the first result and `Idempotent-Replayed: true`."
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "from": {
                                        "type": "string"
                                    },
                                    "to": {
                                        "type": "string"
                                    },
                                    "weekdays": {
                                        "type": "array",
                                        "items": {
                                            "type": "integer"
                                        },
                                        "maxItems": 7,
                                        "nullable": true
                                    },
                                    "rate": {
                                        "type": "string",
                                        "nullable": true
                                    },
                                    "min_stay": {
                                        "type": "integer",
                                        "nullable": true
                                    },
                                    "max_stay": {
                                        "type": "integer",
                                        "nullable": true
                                    },
                                    "closed_to_arrival": {
                                        "type": "boolean",
                                        "nullable": true
                                    },
                                    "closed_to_departure": {
                                        "type": "boolean",
                                        "nullable": true
                                    },
                                    "blocked": {
                                        "type": "boolean",
                                        "nullable": true
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "maxLength": 80,
                                        "nullable": true
                                    }
                                },
                                "required": [
                                    "from",
                                    "to"
                                ]
                            },
                            "example": {
                                "from": "2026-07-01",
                                "to": "2026-07-31",
                                "weekdays": [
                                    4,
                                    5
                                ],
                                "rate": "600.00",
                                "min_stay": 2
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/Calendar"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "UNIT_NOT_GRANTED — no unit of yours has that id.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "IDEMPOTENCY_MISMATCH — that Idempotency-Key was used with a different body.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "VALIDATION_ERROR, RANGE_TOO_LARGE, BEYOND_HORIZON or UNSUPPORTED_CURRENCY.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/units/import": {
            "post": {
                "tags": [
                    "Units"
                ],
                "summary": "Propose unit details to the host.",
                "operationId": "listingControllerImport",
                "description": "For a platform that already holds a filled-in listing and wants Suitiee to have the\nsame facts -- a host who built their page on your site first, and is now connecting.\n\n**Nothing is changed by this call.** The payload becomes a draft the host reviews\nfield by field on their own screen, and applying it is their act. That is deliberate:\na host's unit record drives what a cleaner is told at the door, what every other\nchannel is shown and what a regulator is given as the licence number, so one\nmarketplace writing to it directly would propagate a data-entry mistake to every other\nchannel with nothing marking where it came from.\n\nSend the body in the same shape `GET /units/{unit}/listing` returns. Unknown keys are\nignored rather than refused, so your own richer payload is fine as it is.\n\n`target.unit_id` proposes changes to a unit you already have.\n`target: {new: true, name: \"...\"}` proposes a unit the host does not have in Suitiee\nyet; they create it from the draft if they want it.\n\nPoll `GET /events/{idempotency_key}` to see what became of the draft.",
                "parameters": [
                    {
                        "name": "Idempotency-Key",
                        "in": "header",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "maxLength": 80
                        },
                        "description": "Up to 80 printable ASCII characters, unique per request you intend to make. A retry with the same key returns the first result and `Idempotent-Replayed: true`."
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "target": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "properties": {
                                            "unit_id": {
                                                "type": "string",
                                                "maxLength": 64,
                                                "nullable": true,
                                                "description": "Required unless `target.new` is sent."
                                            },
                                            "new": {
                                                "type": "boolean",
                                                "nullable": true,
                                                "description": "Required unless `target.unit_id` is sent."
                                            },
                                            "name": {
                                                "type": "string",
                                                "maxLength": 255,
                                                "nullable": true
                                            }
                                        }
                                    },
                                    "name": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "nullable": true
                                    },
                                    "description": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "nullable": true
                                    },
                                    "capacity": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "nullable": true
                                    },
                                    "address": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "nullable": true
                                    },
                                    "geo": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "nullable": true
                                    },
                                    "times": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "nullable": true
                                    },
                                    "licence": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "nullable": true
                                    },
                                    "pricing": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "nullable": true
                                    },
                                    "amenities": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "nullable": true
                                    },
                                    "photos": {
                                        "type": "array",
                                        "items": {
                                            "type": "string",
                                            "maxLength": 2048,
                                            "nullable": true
                                        },
                                        "nullable": true
                                    },
                                    "area_sqm": {
                                        "type": "number",
                                        "nullable": true
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "maxLength": 80,
                                        "nullable": true
                                    }
                                },
                                "required": [
                                    "target"
                                ]
                            },
                            "example": {
                                "target": {
                                    "unit_id": "9f2c1a7e-0000-4c8b-9f1e-000000000001"
                                },
                                "name": {
                                    "en": "Sea View Apartment",
                                    "ar": "شقة بإطلالة بحرية"
                                },
                                "capacity": {
                                    "max_guests": 4,
                                    "bedrooms": 2
                                },
                                "amenities": [
                                    "wifi",
                                    "air_conditioning"
                                ]
                            }
                        }
                    }
                },
                "responses": {
                    "202": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "draft_id": {
                                                    "type": "string",
                                                    "format": "uuid"
                                                },
                                                "status": {
                                                    "type": "string",
                                                    "enum": [
                                                        "pending"
                                                    ]
                                                },
                                                "fields_read": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "string"
                                                    },
                                                    "description": "Listing-pack paths your payload supplied."
                                                },
                                                "fields_missing": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "string"
                                                    },
                                                    "description": "Paths it did not. Three are always here -- the two halves of the Saudi national address and the tourism licence number -- because no external source publishes them."
                                                }
                                            }
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "UNIT_NOT_GRANTED — the target unit is not one of yours.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "VALIDATION_ERROR or INVALID_IDEMPOTENCY_KEY.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/bookings": {
            "get": {
                "tags": [
                    "Bookings"
                ],
                "summary": "The bookings you have sent us.",
                "operationId": "bookingControllerIndex",
                "description": "Only your own: a stay on the same unit that arrived from another channel, or that the\nhost entered themselves, is not here and never will be. It shows up on the calendar as\na closed night, which is all you need to stop selling it.\n\n`updated_since` is what a nightly reconciliation pass reads: everything that has\nchanged on our side since your last run, including stays the host cancelled or moved\nin Suitiee.",
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "default": 1
                        }
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 100,
                            "default": 20
                        }
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "required": false,
                        "description": "anticipated, confirmed, completed, cancelled or expired.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "unit_id",
                        "in": "query",
                        "required": false,
                        "description": "Only stays in this unit.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "updated_since",
                        "in": "query",
                        "required": false,
                        "description": "ISO-8601. Only stays changed since then — the reconciliation filter.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/Booking"
                                            }
                                        },
                                        "links": {
                                            "type": "object"
                                        },
                                        "meta": {
                                            "$ref": "#/components/schemas/Meta"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "tags": [
                    "Bookings"
                ],
                "summary": "Tell Suitiee a guest has booked.",
                "operationId": "bookingControllerStore",
                "description": "The host sees the stay on their calendar within this request, and the night closes on\nevery other channel they sell through — Airbnb and Booking.com within a minute, any\nother connected platform on the next drain.\n\nSend `unit_id` (ours) or `external_listing_id` (yours, if you have recorded the link).\nSend `Idempotency-Key`: a retry with the same key returns this same response rather\nthan a second stay on somebody's calendar.\n\n**Overlaps are accepted by default.** If nights of the stay are already taken you get\n`201` with `meta.warnings` naming them, and the host is shown an oversell to resolve —\nbecause you have usually already sold the stay, and refusing it would hide the clash\nrather than prevent it. Ask us to switch your application to strict mode if you call\nbefore confirming to the guest, and you will get `409 NIGHTS_UNAVAILABLE` instead.\n\n`status: \"pending\"` records a hold. It occupies the nights exactly as a confirmed stay\ndoes — anything else would let the flat be sold twice — and is released automatically\nif you have not confirmed it within your application's hold window.",
                "parameters": [
                    {
                        "name": "Idempotency-Key",
                        "in": "header",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "maxLength": 80
                        },
                        "description": "Up to 80 printable ASCII characters, unique per request you intend to make. A retry with the same key returns the first result and `Idempotent-Replayed: true`."
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "booking_id": {
                                        "type": "string",
                                        "maxLength": 128
                                    },
                                    "unit_id": {
                                        "type": "string",
                                        "maxLength": 64,
                                        "nullable": true,
                                        "description": "Required unless `external_listing_id` is sent."
                                    },
                                    "external_listing_id": {
                                        "type": "string",
                                        "maxLength": 128,
                                        "nullable": true,
                                        "description": "Required unless `unit_id` is sent."
                                    },
                                    "status": {
                                        "type": "string",
                                        "enum": [
                                            "confirmed",
                                            "pending"
                                        ],
                                        "nullable": true
                                    },
                                    "checkin": {
                                        "type": "string",
                                        "format": "date-time"
                                    },
                                    "checkout": {
                                        "type": "string",
                                        "format": "date-time",
                                        "description": "Must be after `checkin`."
                                    },
                                    "guest": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "nullable": true,
                                        "properties": {
                                            "name": {
                                                "type": "string",
                                                "maxLength": 255,
                                                "nullable": true
                                            },
                                            "email": {
                                                "type": "string",
                                                "format": "email",
                                                "maxLength": 255,
                                                "nullable": true
                                            },
                                            "phone": {
                                                "type": "string",
                                                "maxLength": 32,
                                                "nullable": true
                                            },
                                            "language": {
                                                "type": "string",
                                                "maxLength": 16,
                                                "nullable": true
                                            }
                                        }
                                    },
                                    "amount": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "nullable": true,
                                        "properties": {
                                            "value": {
                                                "type": "string",
                                                "nullable": true,
                                                "description": "Required when `amount.currency` is sent."
                                            },
                                            "currency": {
                                                "type": "string",
                                                "nullable": true,
                                                "description": "Required when `amount.value` is sent."
                                            }
                                        }
                                    },
                                    "notes": {
                                        "type": "string",
                                        "maxLength": 2000,
                                        "nullable": true
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "maxLength": 80,
                                        "nullable": true
                                    }
                                },
                                "required": [
                                    "booking_id",
                                    "checkin",
                                    "checkout"
                                ]
                            },
                            "example": {
                                "booking_id": "GTH-88213",
                                "unit_id": "9f2c1a7e-0000-4c8b-9f1e-000000000001",
                                "status": "confirmed",
                                "checkin": "2026-10-04T15:00:00+03:00",
                                "checkout": "2026-10-07T11:00:00+03:00",
                                "guest": {
                                    "name": "Sara Ali",
                                    "email": "sara@example.com",
                                    "phone": "+966500000000",
                                    "language": "ar"
                                },
                                "amount": {
                                    "value": "1350.00",
                                    "currency": "SAR"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/Booking"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "UNIT_NOT_GRANTED — the unit is not one of yours.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "LISTING_NOT_LINKED, NIGHTS_UNAVAILABLE (strict mode only) or IDEMPOTENCY_MISMATCH.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "VALIDATION_ERROR, INVALID_IDEMPOTENCY_KEY or UNSUPPORTED_CURRENCY.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/bookings/{booking}": {
            "get": {
                "tags": [
                    "Bookings"
                ],
                "summary": "One booking, by your own id.",
                "operationId": "bookingControllerShow",
                "parameters": [
                    {
                        "name": "booking",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/Booking"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "BOOKING_NOT_FOUND — no booking of yours has that id.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "patch": {
                "tags": [
                    "Bookings"
                ],
                "summary": "Change a stay that has not happened yet.",
                "operationId": "bookingControllerUpdate",
                "description": "Dates, guest and amount, each optional and each omitted field left alone — a guest who\nextends by two nights is one date, not a whole booking resent.\n\nOnly while the stay is still `anticipated`. Once the turnover has been confirmed it\nhas been priced, charged to the host and given to a cleaning crew, so moving it is a\ncancel-and-rebook rather than an edit — the same rule the operator API follows.",
                "parameters": [
                    {
                        "name": "booking",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "Idempotency-Key",
                        "in": "header",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "maxLength": 80
                        },
                        "description": "Up to 80 printable ASCII characters, unique per request you intend to make. A retry with the same key returns the first result and `Idempotent-Replayed: true`."
                    }
                ],
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "checkin": {
                                        "type": "string",
                                        "format": "date-time",
                                        "nullable": true
                                    },
                                    "checkout": {
                                        "type": "string",
                                        "format": "date-time",
                                        "nullable": true
                                    },
                                    "guest": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "nullable": true,
                                        "properties": {
                                            "name": {
                                                "type": "string",
                                                "maxLength": 255,
                                                "nullable": true
                                            },
                                            "email": {
                                                "type": "string",
                                                "format": "email",
                                                "maxLength": 255,
                                                "nullable": true
                                            },
                                            "phone": {
                                                "type": "string",
                                                "maxLength": 32,
                                                "nullable": true
                                            },
                                            "language": {
                                                "type": "string",
                                                "maxLength": 16,
                                                "nullable": true
                                            }
                                        }
                                    },
                                    "amount": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "nullable": true,
                                        "properties": {
                                            "value": {
                                                "type": "string",
                                                "nullable": true,
                                                "description": "Required when `amount.currency` is sent."
                                            },
                                            "currency": {
                                                "type": "string",
                                                "nullable": true,
                                                "description": "Required when `amount.value` is sent."
                                            }
                                        }
                                    },
                                    "notes": {
                                        "type": "string",
                                        "maxLength": 2000,
                                        "nullable": true
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "maxLength": 80,
                                        "nullable": true
                                    }
                                }
                            },
                            "example": {
                                "checkout": "2026-10-09T11:00:00+03:00"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/Booking"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "BOOKING_NOT_FOUND — no booking of yours has that id.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "BOOKING_NOT_MODIFIABLE or IDEMPOTENCY_MISMATCH.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "VALIDATION_ERROR — nothing to change, or a checkout that would fall before its check-in.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/bookings/{booking}/cancel": {
            "post": {
                "tags": [
                    "Bookings"
                ],
                "summary": "Call a stay off.",
                "operationId": "bookingControllerCancel",
                "description": "A stay that has not happened is simply dropped and its nights go back on sale. One\nwhose turnover has already been confirmed is cancelled under the same rules an\noperator gets: the host is refunded and the crew stood down, but not inside the\n24-hour notice period and not once somebody is already in the flat — those answer 409\nsaying which rule stopped it, and the guest's own refund is a matter for your\nplatform, not ours.\n\nSafe to repeat: a stay already off answers 200 and changes nothing.",
                "parameters": [
                    {
                        "name": "booking",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "Idempotency-Key",
                        "in": "header",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "maxLength": 80
                        },
                        "description": "Up to 80 printable ASCII characters, unique per request you intend to make. A retry with the same key returns the first result and `Idempotent-Replayed: true`."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/Booking"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "BOOKING_NOT_FOUND — no booking of yours has that id.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "CANCEL_TOO_LATE or CANCEL_BLOCKED — the cleaning is inside the notice period or already under way.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/bookings/{booking}/checkout": {
            "post": {
                "tags": [
                    "Bookings"
                ],
                "summary": "Confirm the guest has left, early.",
                "operationId": "bookingControllerCheckout",
                "description": "Optional. Suitiee confirms the turnover by itself at the contracted checkout time, so\na platform that does not know when a guest actually left does not have to call this at\nall. Send it when you do know: it prices the turnover, charges the host's package or\nwallet and dispatches a cleaner from the real time rather than the expected one, which\nis a cleaner arriving hours earlier.",
                "parameters": [
                    {
                        "name": "booking",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "Idempotency-Key",
                        "in": "header",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "maxLength": 80
                        },
                        "description": "Up to 80 printable ASCII characters, unique per request you intend to make. A retry with the same key returns the first result and `Idempotent-Replayed: true`."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/Booking"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "BOOKING_NOT_FOUND.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "INSUFFICIENT_BALANCE — the host has no package visit and no wallet balance, or SERVICE_UNAVAILABLE.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/bookings/{booking}/turnover": {
            "get": {
                "tags": [
                    "Operations"
                ],
                "summary": "The turnover for one of your bookings: is the flat being cleaned, and is it ready?",
                "operationId": "operationsControllerTurnover",
                "description": "This is the endpoint behind \"can we check in early?\". `status: \"completed\"` means the\nprevious guest has gone and the flat has been cleaned and signed off. Anything else\nmeans it has not, and `window.end` is the honest answer to when it will be.\n\n`404` when the stay has no turnover yet — a booking whose checkout is next week has\nnothing scheduled, which is not an error and is not a promise that none will be.",
                "parameters": [
                    {
                        "name": "booking",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/WorkOrder"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "BOOKING_NOT_FOUND — no booking of yours has that id — or TURNOVER_NOT_FOUND — it has no turnover yet.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/services": {
            "get": {
                "tags": [
                    "Operations"
                ],
                "summary": "What can be ordered on this host's units, and what it costs them.",
                "operationId": "operationsControllerServices",
                "description": "The price is the HOST's price — resolved through their package and any discount their\nmanagement company negotiated — not a list price. Show a guest the number the host will\nactually be charged, or the host finds the difference on a statement.\n\n`unit_id` prices the catalogue for that unit's type. Without it the prices come back\nnull rather than as a guess: a studio and a four-bedroom villa are not the same clean.",
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "default": 1
                        }
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 100,
                            "default": 20
                        }
                    },
                    {
                        "name": "unit_id",
                        "in": "query",
                        "required": false,
                        "description": "Price the catalogue for this unit. Without it, `price` is null.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "trade",
                        "in": "query",
                        "required": false,
                        "description": "cleaning or maintenance.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/Service"
                                            }
                                        },
                                        "links": {
                                            "type": "object"
                                        },
                                        "meta": {
                                            "$ref": "#/components/schemas/Meta"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "UNIT_NOT_GRANTED — no unit of yours has that id.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/units/{unit}/cleanings": {
            "get": {
                "tags": [
                    "Operations"
                ],
                "summary": "The cleaning visits on a granted unit.",
                "operationId": "operationsControllerCleanings",
                "description": "Every clean on the unit, whoever ordered it — the host, their operator, or you —\nbecause a flat being cleaned is a flat that cannot be shown, and a partner who could\nonly see their own orders would be blind to most of them. What you cannot see is who is\ndoing the cleaning or what another channel's stay was.",
                "parameters": [
                    {
                        "name": "unit",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "default": 1
                        }
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 100,
                            "default": 20
                        }
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "required": false,
                        "description": "pending, assigned, in_progress, completed, cancelled or failed.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "from",
                        "in": "query",
                        "required": false,
                        "description": "ISO-8601. Visits scheduled to start after this.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/WorkOrder"
                                            }
                                        },
                                        "links": {
                                            "type": "object"
                                        },
                                        "meta": {
                                            "$ref": "#/components/schemas/Meta"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "UNIT_NOT_GRANTED — no unit of yours has that id.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "tags": [
                    "Operations"
                ],
                "summary": "Order a cleaning visit on a granted unit.",
                "operationId": "operationsControllerOrderCleaning",
                "description": "**This spends the host's money.** The visit is charged to their package or their wallet\nthe moment it is created, which is why it needs the `cleaning:write` scope of its own\nand why a host can grant you everything else and refuse this one.\n\nSend a `window_start`/`window_end` a crew can actually be dispatched into — \"between\n11:00 and 15:00\" is schedulable, an exact minute is a promise nobody can keep. Prices\ncome from `GET /services?unit_id=…`, which returns this host's real price rather than a\nlist price.\n\n`422 INSUFFICIENT_BALANCE` means exactly that: no package visit left and not enough in\nthe wallet. Nothing was created and nothing was charged — tell the host, not the guest.",
                "parameters": [
                    {
                        "name": "unit",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "Idempotency-Key",
                        "in": "header",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "maxLength": 80
                        },
                        "description": "Up to 80 printable ASCII characters, unique per request you intend to make. A retry with the same key returns the first result and `Idempotent-Replayed: true`."
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "service_id": {
                                        "type": "string",
                                        "maxLength": 64
                                    },
                                    "window_start": {
                                        "type": "string",
                                        "format": "date-time",
                                        "description": "Must be after `now`."
                                    },
                                    "window_end": {
                                        "type": "string",
                                        "format": "date-time",
                                        "description": "Must be after `window_start`."
                                    },
                                    "addons": {
                                        "type": "array",
                                        "items": {
                                            "type": "string",
                                            "maxLength": 64
                                        },
                                        "maxItems": 10,
                                        "nullable": true
                                    },
                                    "notes": {
                                        "type": "string",
                                        "maxLength": 2000,
                                        "nullable": true
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "maxLength": 80,
                                        "nullable": true
                                    }
                                },
                                "required": [
                                    "service_id",
                                    "window_start",
                                    "window_end"
                                ]
                            },
                            "example": {
                                "service_id": "2c4e1a90-0000-4c8b-9f1e-000000000001",
                                "window_start": "2026-10-07T11:00:00+03:00",
                                "window_end": "2026-10-07T15:00:00+03:00",
                                "notes": "Guest reports the kitchen needs extra attention."
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/WorkOrder"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "UNIT_NOT_GRANTED — no unit of yours has that id.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "IDEMPOTENCY_MISMATCH — that Idempotency-Key was used with a different body.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "SERVICE_UNAVAILABLE, INSUFFICIENT_BALANCE or VALIDATION_ERROR.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/cleanings/{cleaning}/cancel": {
            "post": {
                "tags": [
                    "Operations"
                ],
                "summary": "Call off a cleaning visit you ordered.",
                "operationId": "operationsControllerCancelCleaning",
                "description": "Only one of yours: a clean the host or their operator arranged is not yours to cancel,\nand answers `404` rather than `403` so a guessed id tells you nothing.\n\nThe host is refunded and the crew stood down — but not inside the 24-hour notice period\nand not once somebody is already in the flat. Those answer `409` naming the rule.",
                "parameters": [
                    {
                        "name": "cleaning",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "Idempotency-Key",
                        "in": "header",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "maxLength": 80
                        },
                        "description": "Up to 80 printable ASCII characters, unique per request you intend to make. A retry with the same key returns the first result and `Idempotent-Replayed: true`."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/WorkOrder"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "CLEANING_NOT_FOUND — no cleaning visit of yours has that id.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "CANCEL_TOO_LATE or CANCEL_BLOCKED — inside the notice period, or already under way.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/units/{unit}/maintenance": {
            "get": {
                "tags": [
                    "Operations"
                ],
                "summary": "The maintenance calls on a granted unit.",
                "operationId": "operationsControllerMaintenanceIndex",
                "description": "All of them, for the same reason as the cleans: a unit with a broken air conditioner is\na unit you should stop selling before a guest arrives at it, whoever noticed first.",
                "parameters": [
                    {
                        "name": "unit",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "default": 1
                        }
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 100,
                            "default": 20
                        }
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "required": false,
                        "description": "pending, assigned, in_progress, completed, cancelled or failed.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "from",
                        "in": "query",
                        "required": false,
                        "description": "ISO-8601. Visits scheduled to start after this.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/WorkOrder"
                                            }
                                        },
                                        "links": {
                                            "type": "object"
                                        },
                                        "meta": {
                                            "$ref": "#/components/schemas/Meta"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "UNIT_NOT_GRANTED — no unit of yours has that id.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "tags": [
                    "Operations"
                ],
                "summary": "Report a problem with a granted unit.",
                "operationId": "operationsControllerRaiseMaintenance",
                "description": "The call a guest's complaint becomes. It costs nothing and commits nobody: the visit is\nrecorded, dispatched to a trade, and quoted before any money is involved — which is why\nthis needs only `maintenance:write` and not the scope that spends the host's balance.\n\nWrite the issue in the guest's own words where you can. \"AC in the second bedroom blows\nwarm\" gets fixed on the first visit; \"broken\" does not.\n\n`priority` decides the window, so it is a real field rather than a label. Send\n`urgent` for no water, no power or a door that will not lock. A partner who sends\neverything as urgent gets a host who stops believing any of it.",
                "parameters": [
                    {
                        "name": "unit",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "Idempotency-Key",
                        "in": "header",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "maxLength": 80
                        },
                        "description": "Up to 80 printable ASCII characters, unique per request you intend to make. A retry with the same key returns the first result and `Idempotent-Replayed: true`."
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "trade": {
                                        "type": "string",
                                        "enum": [
                                            "ac",
                                            "plumbing",
                                            "electrical",
                                            "appliance",
                                            "general"
                                        ],
                                        "nullable": true
                                    },
                                    "priority": {
                                        "type": "string",
                                        "enum": [
                                            "low",
                                            "normal",
                                            "high",
                                            "urgent"
                                        ],
                                        "nullable": true
                                    },
                                    "issue": {
                                        "type": "string",
                                        "minLength": 5,
                                        "maxLength": 2000
                                    },
                                    "category": {
                                        "type": "string",
                                        "maxLength": 64,
                                        "nullable": true
                                    },
                                    "booking_id": {
                                        "type": "string",
                                        "maxLength": 128,
                                        "nullable": true
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "maxLength": 80,
                                        "nullable": true
                                    }
                                },
                                "required": [
                                    "issue"
                                ]
                            },
                            "example": {
                                "trade": "ac",
                                "priority": "urgent",
                                "issue": "Guest reports the air conditioning in the second bedroom blows warm.",
                                "booking_id": "GTH-88213"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/WorkOrder"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "UNIT_NOT_GRANTED — no unit of yours has that id.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "IDEMPOTENCY_MISMATCH — that Idempotency-Key was used with a different body.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "VALIDATION_ERROR — an unknown trade or priority, or an issue under five characters.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/messages": {
            "get": {
                "tags": [
                    "Messages"
                ],
                "summary": "The conversation so far, both directions.",
                "operationId": "messageControllerIndex",
                "description": "Your own messages come back with your ids on them and the host's replies come back\nwith ours, so this is the catch-up read after an outage rather than a copy of what you\nalready hold. Newest first.\n\n`since` is what a reconciliation pass sends: everything said after that moment. A\nreceiver that missed an afternoon reads it once and is level.",
                "parameters": [
                    {
                        "name": "page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "default": 1
                        }
                    },
                    {
                        "name": "per_page",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "minimum": 1,
                            "maximum": 100,
                            "default": 20
                        }
                    },
                    {
                        "name": "thread_id",
                        "in": "query",
                        "required": false,
                        "description": "Only this thread of yours.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "booking_id",
                        "in": "query",
                        "required": false,
                        "description": "Only the thread attached to this booking of yours.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "since",
                        "in": "query",
                        "required": false,
                        "description": "ISO-8601. Only messages sent after that moment — the catch-up filter.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "array",
                                            "items": {
                                                "$ref": "#/components/schemas/Message"
                                            }
                                        },
                                        "links": {
                                            "type": "object"
                                        },
                                        "meta": {
                                            "$ref": "#/components/schemas/Meta"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "tags": [
                    "Messages"
                ],
                "summary": "Tell Suitiee a guest has written to you.",
                "operationId": "messageControllerStore",
                "description": "The message appears in the host's inbox immediately, beside their Booking.com and\nAirbnb threads and counted in the same unread badge. Send `booking_id` when you have\none and the thread is attached to the stay, the dates and the guest record; send\n`unit_id` when you do not, and it is attached to the flat. Send neither and the\nmessage still appears — the host is waiting on it either way, and a later message that\ncarries the booking links the whole thread retrospectively.\n\nRepeating a `message_id` updates that message rather than adding a second copy, so a\nretried POST cannot make an operator answer one question twice. Send `Idempotency-Key`\nas well if your client retries on timeouts.\n\n**You cannot post as the host.** There is no `sender` field: this endpoint records what\nthe GUEST said. The host's own replies come back to you as `message.sent` events.",
                "parameters": [
                    {
                        "name": "Idempotency-Key",
                        "in": "header",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "maxLength": 80
                        },
                        "description": "Up to 80 printable ASCII characters, unique per request you intend to make. A retry with the same key returns the first result and `Idempotent-Replayed: true`."
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "thread_id": {
                                        "type": "string",
                                        "maxLength": 191
                                    },
                                    "message_id": {
                                        "type": "string",
                                        "maxLength": 191
                                    },
                                    "body": {
                                        "type": "string",
                                        "maxLength": 8000
                                    },
                                    "sent_at": {
                                        "type": "string",
                                        "format": "date-time",
                                        "nullable": true
                                    },
                                    "guest_name": {
                                        "type": "string",
                                        "maxLength": 255,
                                        "nullable": true
                                    },
                                    "booking_id": {
                                        "type": "string",
                                        "maxLength": 128,
                                        "nullable": true
                                    },
                                    "unit_id": {
                                        "type": "string",
                                        "maxLength": 64,
                                        "nullable": true
                                    },
                                    "attachments": {
                                        "type": "array",
                                        "items": {
                                            "type": "string",
                                            "maxLength": 2048
                                        },
                                        "maxItems": 10,
                                        "nullable": true
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "maxLength": 80,
                                        "nullable": true
                                    }
                                },
                                "required": [
                                    "thread_id",
                                    "message_id",
                                    "body"
                                ]
                            },
                            "example": {
                                "thread_id": "THREAD-9912",
                                "message_id": "MSG-40021",
                                "booking_id": "GTH-88213",
                                "guest_name": "Sara Ali",
                                "body": "What time can we check in?",
                                "sent_at": "2026-10-03T18:22:00+03:00"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "$ref": "#/components/schemas/Message"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "IDEMPOTENCY_MISMATCH — that Idempotency-Key was used with a different body.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "VALIDATION_ERROR or INVALID_IDEMPOTENCY_KEY.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/events/{event}": {
            "get": {
                "tags": [
                    "Events"
                ],
                "summary": "What became of something you sent us.",
                "operationId": "eventControllerShow",
                "description": "Look a request up by the `Idempotency-Key` you sent with it, and see what we did:\nwhether it was accepted, refused, replayed as a duplicate, or accepted with an\noverlap warning.\n\nThis is the endpoint for the question every channel integration eventually asks --\n\"we sent you that booking on Tuesday, where is it?\" -- and it answers without anybody\nhaving to read a server log. It also carries the response we gave, so a partner whose\nown logging failed can still see what we said.",
                "parameters": [
                    {
                        "name": "event",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "event_id": {
                                                    "type": "string",
                                                    "format": "uuid",
                                                    "description": "Suitiee's id for the request."
                                                },
                                                "idempotency_key": {
                                                    "type": "string",
                                                    "nullable": true
                                                },
                                                "kind": {
                                                    "type": "string",
                                                    "enum": [
                                                        "booking.created",
                                                        "booking.modified",
                                                        "booking.cancelled",
                                                        "booking.checked_out",
                                                        "listing.import"
                                                    ]
                                                },
                                                "outcome": {
                                                    "type": "string",
                                                    "enum": [
                                                        "accepted",
                                                        "rejected",
                                                        "duplicate",
                                                        "conflict"
                                                    ],
                                                    "description": "`conflict` means accepted, but the stay overlapped nights we already held."
                                                },
                                                "http_status": {
                                                    "type": "integer"
                                                },
                                                "error_code": {
                                                    "type": "string",
                                                    "nullable": true
                                                },
                                                "response": {
                                                    "type": "object",
                                                    "nullable": true,
                                                    "description": "The body we answered with."
                                                },
                                                "received_at": {
                                                    "type": "string",
                                                    "format": "date-time"
                                                }
                                            }
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "EVENT_NOT_FOUND — no request of yours has that key.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/channel/webhooks/samples": {
            "get": {
                "tags": [
                    "Sandbox"
                ],
                "summary": "One sample body per event we can send you.",
                "operationId": "webhookSandboxControllerSamples",
                "description": "Build your receiver against these. Each is the exact body a real event of that name\ncarries, from the same catalogue the reference and the delivery engine read -- so a\nsample that drifts from the real thing is not possible.\n\nThe envelope is shown too, with the headers that come with it: `X-Suitiee-Event-Id` is\nwhat you deduplicate on, and `X-Suitiee-Timestamp` is what lets you refuse an event\nolder than your own replay window.",
                "responses": {
                    "200": {
                        "description": "Success",
                        "headers": {
                            "X-Suitiee-Api-Version": {
                                "schema": {
                                    "type": "string",
                                    "example": "1"
                                },
                                "description": "Which version of this API answered."
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "data": {
                                            "type": "object",
                                            "properties": {
                                                "signature": {
                                                    "type": "object",
                                                    "description": "How to verify. Version 2 signs \"{timestamp}.{rawBody}\"; there is no version 1 for channel applications."
                                                },
                                                "events": {
                                                    "type": "array",
                                                    "items": {
                                                        "type": "object",
                                                        "properties": {
                                                            "event": {
                                                                "type": "string"
                                                            },
                                                            "description": {
                                                                "type": "string"
                                                            },
                                                            "body": {
                                                                "type": "object",
                                                                "description": "The complete envelope, as posted."
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "UNAUTHENTICATED — no token, or one that has expired or been revoked.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "CONNECTION_REVOKED, APP_SUSPENDED or INSUFFICIENT_SCOPE. `details.required_scope` names the missing scope.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "RATE_LIMITED — 120/min per connection. Carries `Retry-After`.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "webhooks": {
        "connection.authorized": {
            "post": {
                "summary": "connection.authorized",
                "description": "A host completed the consent screen, or re-consented with different units or scopes. The scopes and units on this event are the current ones, not a delta.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "event": {
                                        "type": "string",
                                        "const": "connection.authorized"
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "format": "uuid"
                                    },
                                    "timestamp": {
                                        "type": "string",
                                        "format": "date-time"
                                    },
                                    "api_version": {
                                        "type": "integer",
                                        "const": 1
                                    },
                                    "connection_id": {
                                        "type": "string",
                                        "format": "uuid",
                                        "nullable": true
                                    },
                                    "data": {
                                        "type": "object"
                                    }
                                },
                                "required": [
                                    "event",
                                    "event_id",
                                    "timestamp",
                                    "data"
                                ]
                            },
                            "example": {
                                "event": "connection.authorized",
                                "event_id": "3f2504e0-4f89-51d3-9a0c-0305e82c3301",
                                "timestamp": "2026-09-09T12:00:00+03:00",
                                "api_version": 1,
                                "data": {
                                    "connection_id": "4f0d0a02-0000-4c8b-9f1e-000000000001",
                                    "host": {
                                        "name": "Sara Ali"
                                    },
                                    "scopes": [
                                        "units:read",
                                        "calendar:read",
                                        "bookings:read",
                                        "bookings:write",
                                        "listings:write"
                                    ],
                                    "unit_ids": [
                                        "9f2c1a7e-0000-4c8b-9f1e-000000000001"
                                    ]
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Accepted. Any 2xx is a success; we do not read the body."
                    }
                }
            }
        },
        "connection.units_changed": {
            "post": {
                "summary": "connection.units_changed",
                "description": "The host changed which units you may see. Re-read GET /units; the ids here are a convenience, not the authority.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "event": {
                                        "type": "string",
                                        "const": "connection.units_changed"
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "format": "uuid"
                                    },
                                    "timestamp": {
                                        "type": "string",
                                        "format": "date-time"
                                    },
                                    "api_version": {
                                        "type": "integer",
                                        "const": 1
                                    },
                                    "connection_id": {
                                        "type": "string",
                                        "format": "uuid",
                                        "nullable": true
                                    },
                                    "data": {
                                        "type": "object"
                                    }
                                },
                                "required": [
                                    "event",
                                    "event_id",
                                    "timestamp",
                                    "data"
                                ]
                            },
                            "example": {
                                "event": "connection.units_changed",
                                "event_id": "3f2504e0-4f89-51d3-9a0c-0305e82c3301",
                                "timestamp": "2026-09-09T12:00:00+03:00",
                                "api_version": 1,
                                "data": {
                                    "connection_id": "4f0d0a02-0000-4c8b-9f1e-000000000001",
                                    "added": [
                                        "9f2c1a7e-0000-4c8b-9f1e-000000000001"
                                    ],
                                    "removed": []
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Accepted. Any 2xx is a success; we do not read the body."
                    }
                }
            }
        },
        "connection.revoked": {
            "post": {
                "summary": "connection.revoked",
                "description": "The host disconnected you, you called DELETE /connection, or the app was suspended. Your tokens are already dead; delete your copy.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "event": {
                                        "type": "string",
                                        "const": "connection.revoked"
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "format": "uuid"
                                    },
                                    "timestamp": {
                                        "type": "string",
                                        "format": "date-time"
                                    },
                                    "api_version": {
                                        "type": "integer",
                                        "const": 1
                                    },
                                    "connection_id": {
                                        "type": "string",
                                        "format": "uuid",
                                        "nullable": true
                                    },
                                    "data": {
                                        "type": "object"
                                    }
                                },
                                "required": [
                                    "event",
                                    "event_id",
                                    "timestamp",
                                    "data"
                                ]
                            },
                            "example": {
                                "event": "connection.revoked",
                                "event_id": "3f2504e0-4f89-51d3-9a0c-0305e82c3301",
                                "timestamp": "2026-09-09T12:00:00+03:00",
                                "api_version": 1,
                                "data": {
                                    "connection_id": "4f0d0a02-0000-4c8b-9f1e-000000000001",
                                    "reason": "host_disconnected"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Accepted. Any 2xx is a success; we do not read the body."
                    }
                }
            }
        },
        "listing.updated": {
            "post": {
                "summary": "listing.updated",
                "description": "Something a channel would show about a linked unit changed. `changed` names the top-level keys of the listing pack that differ; re-read GET /units/{id}/listing for the values.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "event": {
                                        "type": "string",
                                        "const": "listing.updated"
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "format": "uuid"
                                    },
                                    "timestamp": {
                                        "type": "string",
                                        "format": "date-time"
                                    },
                                    "api_version": {
                                        "type": "integer",
                                        "const": 1
                                    },
                                    "connection_id": {
                                        "type": "string",
                                        "format": "uuid",
                                        "nullable": true
                                    },
                                    "data": {
                                        "type": "object"
                                    }
                                },
                                "required": [
                                    "event",
                                    "event_id",
                                    "timestamp",
                                    "data"
                                ]
                            },
                            "example": {
                                "event": "listing.updated",
                                "event_id": "3f2504e0-4f89-51d3-9a0c-0305e82c3301",
                                "timestamp": "2026-09-09T12:00:00+03:00",
                                "api_version": 1,
                                "data": {
                                    "connection_id": "4f0d0a02-0000-4c8b-9f1e-000000000001",
                                    "unit_id": "9f2c1a7e-0000-4c8b-9f1e-000000000001",
                                    "external_listing_id": "L-1",
                                    "changed": [
                                        "photos",
                                        "amenities"
                                    ],
                                    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
                                    "sync_seq": 1843
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Accepted. Any 2xx is a success; we do not read the body."
                    }
                }
            }
        },
        "listing.deleted": {
            "post": {
                "summary": "listing.deleted",
                "description": "The unit was deleted in Suitiee, or the host stopped granting it to you. Sent before the row disappears, so it is the last thing you hear about that unit.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "event": {
                                        "type": "string",
                                        "const": "listing.deleted"
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "format": "uuid"
                                    },
                                    "timestamp": {
                                        "type": "string",
                                        "format": "date-time"
                                    },
                                    "api_version": {
                                        "type": "integer",
                                        "const": 1
                                    },
                                    "connection_id": {
                                        "type": "string",
                                        "format": "uuid",
                                        "nullable": true
                                    },
                                    "data": {
                                        "type": "object"
                                    }
                                },
                                "required": [
                                    "event",
                                    "event_id",
                                    "timestamp",
                                    "data"
                                ]
                            },
                            "example": {
                                "event": "listing.deleted",
                                "event_id": "3f2504e0-4f89-51d3-9a0c-0305e82c3301",
                                "timestamp": "2026-09-09T12:00:00+03:00",
                                "api_version": 1,
                                "data": {
                                    "connection_id": "4f0d0a02-0000-4c8b-9f1e-000000000001",
                                    "unit_id": "9f2c1a7e-0000-4c8b-9f1e-000000000001",
                                    "external_listing_id": "L-1",
                                    "reason": "unit_deleted"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Accepted. Any 2xx is a success; we do not read the body."
                    }
                }
            }
        },
        "availability.changed": {
            "post": {
                "summary": "availability.changed",
                "description": "Nights opened or closed. Pull the ranges named; do not infer a value from this event.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "event": {
                                        "type": "string",
                                        "const": "availability.changed"
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "format": "uuid"
                                    },
                                    "timestamp": {
                                        "type": "string",
                                        "format": "date-time"
                                    },
                                    "api_version": {
                                        "type": "integer",
                                        "const": 1
                                    },
                                    "connection_id": {
                                        "type": "string",
                                        "format": "uuid",
                                        "nullable": true
                                    },
                                    "data": {
                                        "type": "object"
                                    }
                                },
                                "required": [
                                    "event",
                                    "event_id",
                                    "timestamp",
                                    "data"
                                ]
                            },
                            "example": {
                                "event": "availability.changed",
                                "event_id": "3f2504e0-4f89-51d3-9a0c-0305e82c3301",
                                "timestamp": "2026-09-09T12:00:00+03:00",
                                "api_version": 1,
                                "data": {
                                    "connection_id": "4f0d0a02-0000-4c8b-9f1e-000000000001",
                                    "unit_id": "9f2c1a7e-0000-4c8b-9f1e-000000000001",
                                    "external_listing_id": "L-1",
                                    "ranges": [
                                        {
                                            "from": "2026-10-04",
                                            "to": "2026-10-06"
                                        }
                                    ],
                                    "sync_seq": 1844,
                                    "calendar_url": "https://suitiee.com/api/v1/channel/units/9f2c1a7e-0000-4c8b-9f1e-000000000001/calendar?from=2026-10-04&to=2026-10-06"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Accepted. Any 2xx is a success; we do not read the body."
                    }
                }
            }
        },
        "rates.changed": {
            "post": {
                "summary": "rates.changed",
                "description": "Prices or stay restrictions moved. Same shape as availability.changed, plus the currency.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "event": {
                                        "type": "string",
                                        "const": "rates.changed"
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "format": "uuid"
                                    },
                                    "timestamp": {
                                        "type": "string",
                                        "format": "date-time"
                                    },
                                    "api_version": {
                                        "type": "integer",
                                        "const": 1
                                    },
                                    "connection_id": {
                                        "type": "string",
                                        "format": "uuid",
                                        "nullable": true
                                    },
                                    "data": {
                                        "type": "object"
                                    }
                                },
                                "required": [
                                    "event",
                                    "event_id",
                                    "timestamp",
                                    "data"
                                ]
                            },
                            "example": {
                                "event": "rates.changed",
                                "event_id": "3f2504e0-4f89-51d3-9a0c-0305e82c3301",
                                "timestamp": "2026-09-09T12:00:00+03:00",
                                "api_version": 1,
                                "data": {
                                    "connection_id": "4f0d0a02-0000-4c8b-9f1e-000000000001",
                                    "unit_id": "9f2c1a7e-0000-4c8b-9f1e-000000000001",
                                    "external_listing_id": "L-1",
                                    "ranges": [
                                        {
                                            "from": "2026-11-01",
                                            "to": "2026-11-30"
                                        }
                                    ],
                                    "currency": "SAR",
                                    "sync_seq": 1845,
                                    "calendar_url": "https://suitiee.com/api/v1/channel/units/9f2c1a7e-0000-4c8b-9f1e-000000000001/calendar?from=2026-11-01&to=2026-11-30"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Accepted. Any 2xx is a success; we do not read the body."
                    }
                }
            }
        },
        "booking.modified": {
            "post": {
                "summary": "booking.modified",
                "description": "One of your bookings was moved in Suitiee -- by the host on their own calendar, or by an operator. Mirror it if your platform can; if it cannot, a person on your side has to.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "event": {
                                        "type": "string",
                                        "const": "booking.modified"
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "format": "uuid"
                                    },
                                    "timestamp": {
                                        "type": "string",
                                        "format": "date-time"
                                    },
                                    "api_version": {
                                        "type": "integer",
                                        "const": 1
                                    },
                                    "connection_id": {
                                        "type": "string",
                                        "format": "uuid",
                                        "nullable": true
                                    },
                                    "data": {
                                        "type": "object"
                                    }
                                },
                                "required": [
                                    "event",
                                    "event_id",
                                    "timestamp",
                                    "data"
                                ]
                            },
                            "example": {
                                "event": "booking.modified",
                                "event_id": "3f2504e0-4f89-51d3-9a0c-0305e82c3301",
                                "timestamp": "2026-09-09T12:00:00+03:00",
                                "api_version": 1,
                                "data": {
                                    "connection_id": "4f0d0a02-0000-4c8b-9f1e-000000000001",
                                    "booking_id": "GTH-88213",
                                    "reservation_id": "b1c2d3e4-0000-4c8b-9f1e-000000000009",
                                    "unit_id": "9f2c1a7e-0000-4c8b-9f1e-000000000001",
                                    "status": "anticipated",
                                    "checkin": "2026-10-04T15:00:00+03:00",
                                    "checkout": "2026-10-08T11:00:00+03:00",
                                    "previous": {
                                        "checkin": "2026-10-04T15:00:00+03:00",
                                        "checkout": "2026-10-07T11:00:00+03:00"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Accepted. Any 2xx is a success; we do not read the body."
                    }
                }
            }
        },
        "booking.cancelled": {
            "post": {
                "summary": "booking.cancelled",
                "description": "One of your bookings was cancelled or expired in Suitiee.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "event": {
                                        "type": "string",
                                        "const": "booking.cancelled"
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "format": "uuid"
                                    },
                                    "timestamp": {
                                        "type": "string",
                                        "format": "date-time"
                                    },
                                    "api_version": {
                                        "type": "integer",
                                        "const": 1
                                    },
                                    "connection_id": {
                                        "type": "string",
                                        "format": "uuid",
                                        "nullable": true
                                    },
                                    "data": {
                                        "type": "object"
                                    }
                                },
                                "required": [
                                    "event",
                                    "event_id",
                                    "timestamp",
                                    "data"
                                ]
                            },
                            "example": {
                                "event": "booking.cancelled",
                                "event_id": "3f2504e0-4f89-51d3-9a0c-0305e82c3301",
                                "timestamp": "2026-09-09T12:00:00+03:00",
                                "api_version": 1,
                                "data": {
                                    "connection_id": "4f0d0a02-0000-4c8b-9f1e-000000000001",
                                    "booking_id": "GTH-88213",
                                    "reservation_id": "b1c2d3e4-0000-4c8b-9f1e-000000000009",
                                    "unit_id": "9f2c1a7e-0000-4c8b-9f1e-000000000001",
                                    "reason": "cancelled"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Accepted. Any 2xx is a success; we do not read the body."
                    }
                }
            }
        },
        "booking.checked_out": {
            "post": {
                "summary": "booking.checked_out",
                "description": "The turnover for one of your bookings was confirmed: the guest has gone and the unit is being turned around.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "event": {
                                        "type": "string",
                                        "const": "booking.checked_out"
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "format": "uuid"
                                    },
                                    "timestamp": {
                                        "type": "string",
                                        "format": "date-time"
                                    },
                                    "api_version": {
                                        "type": "integer",
                                        "const": 1
                                    },
                                    "connection_id": {
                                        "type": "string",
                                        "format": "uuid",
                                        "nullable": true
                                    },
                                    "data": {
                                        "type": "object"
                                    }
                                },
                                "required": [
                                    "event",
                                    "event_id",
                                    "timestamp",
                                    "data"
                                ]
                            },
                            "example": {
                                "event": "booking.checked_out",
                                "event_id": "3f2504e0-4f89-51d3-9a0c-0305e82c3301",
                                "timestamp": "2026-09-09T12:00:00+03:00",
                                "api_version": 1,
                                "data": {
                                    "connection_id": "4f0d0a02-0000-4c8b-9f1e-000000000001",
                                    "booking_id": "GTH-88213",
                                    "reservation_id": "b1c2d3e4-0000-4c8b-9f1e-000000000009",
                                    "unit_id": "9f2c1a7e-0000-4c8b-9f1e-000000000001",
                                    "actual_checkout_at": "2026-10-07T11:00:00+03:00"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Accepted. Any 2xx is a success; we do not read the body."
                    }
                }
            }
        },
        "message.sent": {
            "post": {
                "summary": "message.sent",
                "description": "The host, or their operator, answered a guest of yours. Deliver it on your platform. THE ONE EVENT HERE THAT CARRIES A VALUE RATHER THAN A POINTER: a message is append-only and identified by its own id, so applying it twice or late ends in the same place — which is the test the pointer rule exists to satisfy. Deduplicate on `message.id`. Requires the messages:read scope.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "event": {
                                        "type": "string",
                                        "const": "message.sent"
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "format": "uuid"
                                    },
                                    "timestamp": {
                                        "type": "string",
                                        "format": "date-time"
                                    },
                                    "api_version": {
                                        "type": "integer",
                                        "const": 1
                                    },
                                    "connection_id": {
                                        "type": "string",
                                        "format": "uuid",
                                        "nullable": true
                                    },
                                    "data": {
                                        "type": "object"
                                    }
                                },
                                "required": [
                                    "event",
                                    "event_id",
                                    "timestamp",
                                    "data"
                                ]
                            },
                            "example": {
                                "event": "message.sent",
                                "event_id": "3f2504e0-4f89-51d3-9a0c-0305e82c3301",
                                "timestamp": "2026-09-09T12:00:00+03:00",
                                "api_version": 1,
                                "data": {
                                    "connection_id": "4f0d0a02-0000-4c8b-9f1e-000000000001",
                                    "thread_id": "THREAD-9912",
                                    "unit_id": "9f2c1a7e-0000-4c8b-9f1e-000000000001",
                                    "booking_id": "GTH-88213",
                                    "message": {
                                        "id": "c7a1e5b4-0000-4c8b-9f1e-000000000042",
                                        "body": "Check-in is from 15:00. The door code is in your arrival email.",
                                        "sent_at": "2026-10-03T18:40:00+03:00"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Accepted. Any 2xx is a success; we do not read the body."
                    }
                }
            }
        },
        "message.thread_closed": {
            "post": {
                "summary": "message.thread_closed",
                "description": "Nobody at Suitiee is waiting on this thread any more. It does not stop the guest writing again, and a new message from them reopens it — what you do with it on your own platform is your decision.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "event": {
                                        "type": "string",
                                        "const": "message.thread_closed"
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "format": "uuid"
                                    },
                                    "timestamp": {
                                        "type": "string",
                                        "format": "date-time"
                                    },
                                    "api_version": {
                                        "type": "integer",
                                        "const": 1
                                    },
                                    "connection_id": {
                                        "type": "string",
                                        "format": "uuid",
                                        "nullable": true
                                    },
                                    "data": {
                                        "type": "object"
                                    }
                                },
                                "required": [
                                    "event",
                                    "event_id",
                                    "timestamp",
                                    "data"
                                ]
                            },
                            "example": {
                                "event": "message.thread_closed",
                                "event_id": "3f2504e0-4f89-51d3-9a0c-0305e82c3301",
                                "timestamp": "2026-09-09T12:00:00+03:00",
                                "api_version": 1,
                                "data": {
                                    "connection_id": "4f0d0a02-0000-4c8b-9f1e-000000000001",
                                    "thread_id": "THREAD-9912"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Accepted. Any 2xx is a success; we do not read the body."
                    }
                }
            }
        },
        "turnover.updated": {
            "post": {
                "summary": "turnover.updated",
                "description": "A cleaning visit on a granted unit changed state. `status: \"completed\"` on a turnover means the flat is clean and empty — the answer to \"can my guest check in early\". Sent for every cleaning visit on the unit, whichever channel's stay it follows: `is_turnover` says whether it is the between-stays clean, and `booking_id` is filled only when the stay is one of yours. Requires the operations:read scope.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "event": {
                                        "type": "string",
                                        "const": "turnover.updated"
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "format": "uuid"
                                    },
                                    "timestamp": {
                                        "type": "string",
                                        "format": "date-time"
                                    },
                                    "api_version": {
                                        "type": "integer",
                                        "const": 1
                                    },
                                    "connection_id": {
                                        "type": "string",
                                        "format": "uuid",
                                        "nullable": true
                                    },
                                    "data": {
                                        "type": "object"
                                    }
                                },
                                "required": [
                                    "event",
                                    "event_id",
                                    "timestamp",
                                    "data"
                                ]
                            },
                            "example": {
                                "event": "turnover.updated",
                                "event_id": "3f2504e0-4f89-51d3-9a0c-0305e82c3301",
                                "timestamp": "2026-09-09T12:00:00+03:00",
                                "api_version": 1,
                                "data": {
                                    "connection_id": "4f0d0a02-0000-4c8b-9f1e-000000000001",
                                    "unit_id": "9f2c1a7e-0000-4c8b-9f1e-000000000001",
                                    "visit_id": "a1b2c3d4-0000-4c8b-9f1e-000000000007",
                                    "booking_id": "GTH-88213",
                                    "is_turnover": true,
                                    "status": "completed",
                                    "window": {
                                        "start": "2026-10-07T11:00:00+03:00",
                                        "end": "2026-10-07T15:00:00+03:00"
                                    },
                                    "completed_at": "2026-10-07T13:42:00+03:00"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Accepted. Any 2xx is a success; we do not read the body."
                    }
                }
            }
        },
        "maintenance.updated": {
            "post": {
                "summary": "maintenance.updated",
                "description": "A maintenance visit on a granted unit changed state — including one you raised yourself. A unit with an open urgent visit is one to stop selling before a guest arrives at it. Requires the operations:read scope.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "event": {
                                        "type": "string",
                                        "const": "maintenance.updated"
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "format": "uuid"
                                    },
                                    "timestamp": {
                                        "type": "string",
                                        "format": "date-time"
                                    },
                                    "api_version": {
                                        "type": "integer",
                                        "const": 1
                                    },
                                    "connection_id": {
                                        "type": "string",
                                        "format": "uuid",
                                        "nullable": true
                                    },
                                    "data": {
                                        "type": "object"
                                    }
                                },
                                "required": [
                                    "event",
                                    "event_id",
                                    "timestamp",
                                    "data"
                                ]
                            },
                            "example": {
                                "event": "maintenance.updated",
                                "event_id": "3f2504e0-4f89-51d3-9a0c-0305e82c3301",
                                "timestamp": "2026-09-09T12:00:00+03:00",
                                "api_version": 1,
                                "data": {
                                    "connection_id": "4f0d0a02-0000-4c8b-9f1e-000000000001",
                                    "unit_id": "9f2c1a7e-0000-4c8b-9f1e-000000000001",
                                    "visit_id": "a1b2c3d4-0000-4c8b-9f1e-000000000008",
                                    "trade": "ac",
                                    "priority": "urgent",
                                    "status": "completed",
                                    "completed_at": "2026-10-05T16:20:00+03:00"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Accepted. Any 2xx is a success; we do not read the body."
                    }
                }
            }
        },
        "test.ping": {
            "post": {
                "summary": "test.ping",
                "description": "Sent by POST /webhooks/test, and by the Send test event button in Suitiee. Never sent by anything real.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "event": {
                                        "type": "string",
                                        "const": "test.ping"
                                    },
                                    "event_id": {
                                        "type": "string",
                                        "format": "uuid"
                                    },
                                    "timestamp": {
                                        "type": "string",
                                        "format": "date-time"
                                    },
                                    "api_version": {
                                        "type": "integer",
                                        "const": 1
                                    },
                                    "connection_id": {
                                        "type": "string",
                                        "format": "uuid",
                                        "nullable": true
                                    },
                                    "data": {
                                        "type": "object"
                                    }
                                },
                                "required": [
                                    "event",
                                    "event_id",
                                    "timestamp",
                                    "data"
                                ]
                            },
                            "example": {
                                "event": "test.ping",
                                "event_id": "3f2504e0-4f89-51d3-9a0c-0305e82c3301",
                                "timestamp": "2026-09-09T12:00:00+03:00",
                                "api_version": 1,
                                "data": {
                                    "message": "This is a test event from Suitiee.",
                                    "sent_at": "2026-09-09T12:00:00+03:00"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Accepted. Any 2xx is a success; we do not read the body."
                    }
                }
            }
        }
    },
    "components": {
        "securitySchemes": {
            "connectionToken": {
                "type": "http",
                "scheme": "bearer",
                "description": "A connection's access token, from `POST /channel/oauth/token`. Resolves to one host and the units they granted you."
            },
            "appCredentials": {
                "type": "http",
                "scheme": "basic",
                "description": "Your application's `oauth_client_id` and secret. Used only by the token endpoint, the revocation endpoint, `GET /connections` and `POST /webhooks/test`."
            }
        },
        "schemas": {
            "Meta": {
                "type": "object",
                "properties": {
                    "current_page": {
                        "type": "integer"
                    },
                    "last_page": {
                        "type": "integer"
                    },
                    "per_page": {
                        "type": "integer"
                    },
                    "total": {
                        "type": "integer"
                    }
                }
            },
            "Error": {
                "type": "object",
                "properties": {
                    "error": {
                        "type": "object",
                        "properties": {
                            "code": {
                                "type": "string",
                                "example": "UNIT_NOT_GRANTED"
                            },
                            "message": {
                                "type": "string"
                            },
                            "details": {
                                "type": "object"
                            }
                        },
                        "required": [
                            "code",
                            "message"
                        ]
                    }
                }
            },
            "Booking": {
                "type": "object",
                "properties": {
                    "booking_id": {
                        "type": "string",
                        "description": "Your own id for the stay. The path segment of every /bookings/{booking_id} call."
                    },
                    "reservation_id": {
                        "type": "string",
                        "format": "uuid",
                        "nullable": true,
                        "description": "Suitiee's id for the same stay. Useful in a support conversation; you never need to send it."
                    },
                    "unit_id": {
                        "type": "string",
                        "format": "uuid",
                        "nullable": true
                    },
                    "external_listing_id": {
                        "type": "string",
                        "nullable": true
                    },
                    "status": {
                        "type": "string",
                        "nullable": true,
                        "enum": [
                            "anticipated",
                            "confirmed",
                            "completed",
                            "cancelled",
                            "expired"
                        ],
                        "description": "Suitiee's state. A booking may be changed only while `anticipated`; after the turnover is confirmed it has been priced and dispatched."
                    },
                    "external_status": {
                        "type": "string",
                        "nullable": true,
                        "description": "The status you last told us: confirmed, pending or cancelled."
                    },
                    "checkin": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "checkout": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "actual_checkout_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "nights": {
                        "type": "integer",
                        "nullable": true
                    },
                    "guest": {
                        "type": "object",
                        "description": "As you supplied it. Returned only to the connection that created the booking.",
                        "properties": {
                            "name": {
                                "type": "string",
                                "nullable": true
                            },
                            "email": {
                                "type": "string",
                                "nullable": true
                            },
                            "phone": {
                                "type": "string",
                                "nullable": true
                            },
                            "language": {
                                "type": "string",
                                "nullable": true
                            }
                        }
                    },
                    "amount": {
                        "type": "object",
                        "nullable": true,
                        "properties": {
                            "value": {
                                "type": "string",
                                "description": "A decimal string, never a number."
                            },
                            "currency": {
                                "type": "string"
                            }
                        }
                    },
                    "is_test": {
                        "type": "boolean"
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    }
                }
            },
            "Calendar": {
                "type": "object",
                "properties": {
                    "unit_id": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "rate_plan": {
                        "type": "object",
                        "description": "Suitiee has one rate plan per unit today; its id is always \"standard\". Reserved so that a future multi-plan calendar is an added query parameter rather than a breaking change.",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "name": {
                                "type": "string"
                            }
                        }
                    },
                    "currency": {
                        "type": "string"
                    },
                    "calendar_version": {
                        "type": "integer",
                        "description": "The connection sync_seq this response reflects. Ignore any later event whose sync_seq is below the one you last applied."
                    },
                    "nights": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "date": {
                                    "type": "string",
                                    "format": "date"
                                },
                                "available": {
                                    "type": "boolean"
                                },
                                "rate": {
                                    "type": "string",
                                    "nullable": true,
                                    "description": "A decimal string in the unit currency, never a number. Null means nobody has priced this night, and `available` is then false."
                                },
                                "min_stay": {
                                    "type": "integer"
                                },
                                "max_stay": {
                                    "type": "integer",
                                    "nullable": true
                                },
                                "closed_to_arrival": {
                                    "type": "boolean"
                                },
                                "closed_to_departure": {
                                    "type": "boolean"
                                },
                                "reason": {
                                    "type": "string",
                                    "nullable": true,
                                    "enum": [
                                        "booked",
                                        "blocked",
                                        "external_calendar",
                                        "unpriced",
                                        "past"
                                    ],
                                    "description": "Why the night is closed. Null when it is open. `booked` never says by whom: another channel's bookings are not visible to you."
                                }
                            }
                        }
                    }
                }
            },
            "Connection": {
                "type": "object",
                "properties": {
                    "connection_id": {
                        "type": "string",
                        "format": "uuid",
                        "description": "Store this against the host account on your side. It is stable across re-consent."
                    },
                    "channel": {
                        "type": "string"
                    },
                    "status": {
                        "type": "string",
                        "enum": [
                            "connected",
                            "revoked",
                            "failed"
                        ]
                    },
                    "host": {
                        "type": "object",
                        "properties": {
                            "name": {
                                "type": "string",
                                "nullable": true
                            }
                        },
                        "description": "The host account, by display name only."
                    },
                    "scopes": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        }
                    },
                    "unit_count": {
                        "type": "integer"
                    },
                    "sync_seq": {
                        "type": "integer",
                        "description": "The sequence number outbound events for this connection are currently at. Discard any event whose sync_seq is below one you have already acted on."
                    },
                    "is_test": {
                        "type": "boolean",
                        "description": "True for a sandbox application. Nothing is billed and no cleaner is dispatched."
                    },
                    "connected_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    }
                }
            },
            "ListingLink": {
                "type": "object",
                "properties": {
                    "unit_id": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "external_listing_id": {
                        "type": "string"
                    },
                    "external_rate_plan_id": {
                        "type": "string",
                        "nullable": true
                    },
                    "url": {
                        "type": "string",
                        "nullable": true
                    },
                    "status": {
                        "type": "string",
                        "enum": [
                            "draft",
                            "listed",
                            "synced",
                            "error"
                        ]
                    },
                    "content_hash": {
                        "type": "string",
                        "nullable": true,
                        "description": "The listing-pack hash as of this link. Compare it with the hash on a listing.updated event to decide whether to re-read the pack."
                    },
                    "linked_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    }
                }
            },
            "Message": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid",
                        "description": "Suitiee's id for the message."
                    },
                    "external_id": {
                        "type": "string",
                        "nullable": true,
                        "description": "Your own id, for a message you sent us. Null for a reply from the host."
                    },
                    "thread_id": {
                        "type": "string",
                        "nullable": true,
                        "description": "Your own thread id."
                    },
                    "booking_id": {
                        "type": "string",
                        "nullable": true
                    },
                    "unit_id": {
                        "type": "string",
                        "format": "uuid",
                        "nullable": true
                    },
                    "sender": {
                        "type": "string",
                        "enum": [
                            "guest",
                            "property"
                        ],
                        "description": "`guest` is a message you sent us; `property` is the host or their operator answering."
                    },
                    "body": {
                        "type": "string",
                        "nullable": true
                    },
                    "attachments": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        },
                        "description": "Links, as supplied. We never re-host them."
                    },
                    "status": {
                        "type": "string",
                        "enum": [
                            "pending",
                            "delivered",
                            "failed"
                        ],
                        "description": "For a reply: `pending` while the message.sent event has not reached you, `delivered` once it has."
                    },
                    "sent_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    }
                }
            },
            "Service": {
                "type": "object",
                "properties": {
                    "service_id": {
                        "type": "string",
                        "format": "uuid",
                        "description": "What POST /units/{unit}/cleanings takes as `service_id`."
                    },
                    "code": {
                        "type": "string",
                        "example": "standard_clean"
                    },
                    "trade": {
                        "type": "string",
                        "example": "cleaning"
                    },
                    "name": {
                        "type": "object",
                        "properties": {
                            "en": {
                                "type": "string"
                            },
                            "ar": {
                                "type": "string"
                            }
                        }
                    },
                    "description": {
                        "type": "object",
                        "properties": {
                            "en": {
                                "type": "string",
                                "nullable": true
                            },
                            "ar": {
                                "type": "string",
                                "nullable": true
                            }
                        }
                    },
                    "duration_minutes": {
                        "type": "integer",
                        "nullable": true
                    },
                    "addons": {
                        "type": "array",
                        "items": {
                            "type": "object"
                        }
                    },
                    "price": {
                        "type": "object",
                        "nullable": true,
                        "description": "What THIS host pays, through their package and any operator discount — not a list price. Null when we could not resolve the unit type, and never 0.00 for \"unknown\".",
                        "properties": {
                            "unit_type": {
                                "type": "string"
                            },
                            "total": {
                                "type": "string",
                                "description": "A decimal string, VAT included."
                            },
                            "vat_amount": {
                                "type": "string"
                            },
                            "vat_rate": {
                                "type": "number"
                            },
                            "currency": {
                                "type": "string"
                            },
                            "quoted": {
                                "type": "boolean",
                                "description": "False for a service that is assessed before it is priced, such as maintenance."
                            }
                        }
                    }
                }
            },
            "Unit": {
                "type": "object",
                "properties": {
                    "unit_id": {
                        "type": "string",
                        "format": "uuid",
                        "description": "The only identifier for a unit anywhere in this API. Stable for the life of the unit."
                    },
                    "name": {
                        "type": "object",
                        "properties": {
                            "en": {
                                "type": "string",
                                "nullable": true
                            },
                            "ar": {
                                "type": "string",
                                "nullable": true
                            }
                        }
                    },
                    "type": {
                        "type": "string",
                        "nullable": true
                    },
                    "city": {
                        "type": "string",
                        "nullable": true
                    },
                    "bedrooms": {
                        "type": "integer",
                        "nullable": true
                    },
                    "max_guests": {
                        "type": "integer",
                        "nullable": true
                    },
                    "currency": {
                        "type": "string",
                        "description": "ISO 4217. Every price for this unit is in it."
                    },
                    "status": {
                        "type": "string"
                    },
                    "listing": {
                        "type": "object",
                        "nullable": true,
                        "description": "The mapping you recorded with PUT /units/{unit}/listing-link. Null until you do.",
                        "properties": {
                            "external_listing_id": {
                                "type": "string",
                                "nullable": true
                            },
                            "external_rate_plan_id": {
                                "type": "string",
                                "nullable": true
                            },
                            "url": {
                                "type": "string",
                                "nullable": true
                            },
                            "status": {
                                "type": "string",
                                "enum": [
                                    "draft",
                                    "listed",
                                    "synced",
                                    "error"
                                ]
                            },
                            "linked_at": {
                                "type": "string",
                                "format": "date-time",
                                "nullable": true
                            }
                        }
                    },
                    "content_hash": {
                        "type": "string",
                        "nullable": true,
                        "description": "The listing-pack hash as of the last listing.updated we sent you. Null before the first one. The authoritative hash is on the pack itself."
                    },
                    "updated_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    }
                }
            },
            "WorkOrder": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "type": {
                        "type": "string",
                        "enum": [
                            "cleaning",
                            "maintenance"
                        ]
                    },
                    "unit_id": {
                        "type": "string",
                        "format": "uuid",
                        "nullable": true
                    },
                    "status": {
                        "type": "string",
                        "enum": [
                            "pending",
                            "assigned",
                            "in_progress",
                            "completed",
                            "cancelled",
                            "failed"
                        ],
                        "description": "`completed` on the turnover of a stay means the flat is clean and empty — the answer to \"can my guest check in early\"."
                    },
                    "trade": {
                        "type": "string",
                        "nullable": true
                    },
                    "priority": {
                        "type": "string",
                        "nullable": true
                    },
                    "window": {
                        "type": "object",
                        "properties": {
                            "start": {
                                "type": "string",
                                "format": "date-time",
                                "nullable": true
                            },
                            "end": {
                                "type": "string",
                                "format": "date-time",
                                "nullable": true
                            }
                        }
                    },
                    "completed_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "cancelled_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    },
                    "reported_issue": {
                        "type": "string",
                        "nullable": true
                    },
                    "resolution_notes": {
                        "type": "string",
                        "nullable": true
                    },
                    "booking_id": {
                        "type": "string",
                        "nullable": true,
                        "description": "Your own booking id, when this visit belongs to a stay you sold. Null otherwise — including for another channel's turnover, whose existence closes the flat but whose booking is never yours to see."
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    }
                }
            }
        }
    }
}
