{
    "openapi": "3.1.0",
    "info": {
        "title": "Mariner's Loft Salt Spring Island Agent API",
        "summary": "Read-only property, policy and content data for Mariner's Loft Salt Spring Island.",
        "description": "Machine-readable access to everything an assistant needs in order to answer questions about staying at Mariner's Loft Salt Spring Island: the property and its address, the two suite layouts, the deposit and cancellation terms, the pet policy, and the text of every published page.\n\n**No key required.** Every scope is read-only and every read scope is granted to anonymous callers, so there is no signup, no sales conversation, and nothing to wait for. A key raises the rate limit and identifies the caller.\n\n**Sandbox.** Append `?sandbox=1` to any read endpoint to get the same response shape labelled `\"_sandbox\": true`. No credential needed, no signup, nothing to wait for.\n\n**Errors** are RFC 9457 problem documents (`application/problem+json`) with a dereferenceable `type`, a stable `title`, and the WordPress error preserved under `wp`.\n\n**Rate limits** are published on every response as `RateLimit` and `RateLimit-Policy`, with the older `RateLimit-Limit`/`-Remaining`/`-Reset` triple alongside, and `Retry-After` on a 429.\n\n**Versioning.** URL path. The major version is the `v1` segment of /wp-json/marinersloft/v1/. A breaking change ships as /v2/ at a new path; /v1/ keeps working. New endpoints, new optional parameters and new response fields ship inside the current version without notice. Clients must ignore unknown fields. Deprecations are signalled with `Deprecation` (RFC 9745) and `Sunset` (RFC 8594) headers, with at least 180 days' notice.\n\nThis API does not create reservations. Booking is handled by the Sirvoy widget on the reservations page.",
        "version": "1.0.0",
        "termsOfService": "https://marinersloftsaltspring.ca/refund-policy/",
        "contact": {
            "name": "Mariner's Loft Salt Spring Island",
            "url": "https://marinersloftsaltspring.ca/for-agents/",
            "email": "marinersloft1@gmail.com"
        },
        "license": {
            "name": "Free for agent and assistant use, attribution appreciated",
            "identifier": "LicenseRef-MarinersLoft-Agent-Use"
        }
    },
    "servers": [
        {
            "url": "https://marinersloftsaltspring.ca/wp-json/marinersloft/v1",
            "description": "Production"
        }
    ],
    "externalDocs": {
        "description": "Agent and API documentation",
        "url": "https://marinersloftsaltspring.ca/for-agents/"
    },
    "security": [
        {
            "BearerApiKey": [
                "property:read",
                "policies:read",
                "content:read"
            ]
        },
        {
            "ApiKeyHeader": [
                "property:read",
                "policies:read",
                "content:read"
            ]
        },
        []
    ],
    "tags": [
        {
            "name": "discovery",
            "description": "Service description and scope catalogue."
        },
        {
            "name": "property",
            "description": "The property and its unit layouts."
        },
        {
            "name": "policies",
            "description": "Booking, cancellation and guest policies."
        },
        {
            "name": "content",
            "description": "Published page content."
        },
        {
            "name": "auth",
            "description": "Self-serve scoped API keys."
        }
    ],
    "paths": {
        "/": {
            "get": {
                "tags": [
                    "discovery"
                ],
                "summary": "Service description",
                "description": "Endpoints, scopes, authentication options, rate limits, the error catalogue, the lifecycle policy, and the scopes the caller currently holds.",
                "operationId": "getServiceDescription",
                "security": [
                    []
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Sandbox"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Service description.",
                        "headers": {
                            "RateLimit": {
                                "description": "Current counter, draft-ietf-httpapi-ratelimit-headers form: \"policy\";r=remaining;t=seconds-until-reset.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";r=297;t=280"
                                    ]
                                }
                            },
                            "RateLimit-Policy": {
                                "description": "The policy in force: \"name\";q=quota;w=window-seconds.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";q=300;w=300"
                                    ]
                                }
                            },
                            "RateLimit-Limit": {
                                "description": "Quota for the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Remaining": {
                                "description": "Requests left in the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Reset": {
                                "description": "Seconds until the window resets.",
                                "schema": {
                                    "type": "integer"
                                }
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Discovery"
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        },
        "/scopes": {
            "get": {
                "tags": [
                    "discovery"
                ],
                "summary": "List scopes",
                "description": "Every scope this API understands and whether it is granted anonymously.",
                "operationId": "listScopes",
                "security": [
                    []
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Sandbox"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "List scopes.",
                        "headers": {
                            "RateLimit": {
                                "description": "Current counter, draft-ietf-httpapi-ratelimit-headers form: \"policy\";r=remaining;t=seconds-until-reset.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";r=297;t=280"
                                    ]
                                }
                            },
                            "RateLimit-Policy": {
                                "description": "The policy in force: \"name\";q=quota;w=window-seconds.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";q=300;w=300"
                                    ]
                                }
                            },
                            "RateLimit-Limit": {
                                "description": "Quota for the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Remaining": {
                                "description": "Requests left in the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Reset": {
                                "description": "Seconds until the window resets.",
                                "schema": {
                                    "type": "integer"
                                }
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ScopeCatalogue"
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        },
        "/property": {
            "get": {
                "tags": [
                    "property"
                ],
                "summary": "Property details",
                "description": "Name, description, address, geo coordinates, contact details, unit count, currency and check-in times.",
                "operationId": "getProperty",
                "security": [
                    {
                        "BearerApiKey": [
                            "property:read"
                        ]
                    },
                    {
                        "ApiKeyHeader": [
                            "property:read"
                        ]
                    },
                    []
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Sandbox"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Property details.",
                        "headers": {
                            "RateLimit": {
                                "description": "Current counter, draft-ietf-httpapi-ratelimit-headers form: \"policy\";r=remaining;t=seconds-until-reset.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";r=297;t=280"
                                    ]
                                }
                            },
                            "RateLimit-Policy": {
                                "description": "The policy in force: \"name\";q=quota;w=window-seconds.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";q=300;w=300"
                                    ]
                                }
                            },
                            "RateLimit-Limit": {
                                "description": "Quota for the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Remaining": {
                                "description": "Requests left in the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Reset": {
                                "description": "Seconds until the window resets.",
                                "schema": {
                                    "type": "integer"
                                }
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Property"
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/InsufficientScope"
                    }
                }
            }
        },
        "/accommodations": {
            "get": {
                "tags": [
                    "property"
                ],
                "summary": "List unit layouts",
                "description": "Both layouts with bedroom counts, floor areas, amenities and any per-stay fees.",
                "operationId": "listAccommodations",
                "security": [
                    {
                        "BearerApiKey": [
                            "property:read"
                        ]
                    },
                    {
                        "ApiKeyHeader": [
                            "property:read"
                        ]
                    },
                    []
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Sandbox"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "List unit layouts.",
                        "headers": {
                            "RateLimit": {
                                "description": "Current counter, draft-ietf-httpapi-ratelimit-headers form: \"policy\";r=remaining;t=seconds-until-reset.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";r=297;t=280"
                                    ]
                                }
                            },
                            "RateLimit-Policy": {
                                "description": "The policy in force: \"name\";q=quota;w=window-seconds.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";q=300;w=300"
                                    ]
                                }
                            },
                            "RateLimit-Limit": {
                                "description": "Quota for the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Remaining": {
                                "description": "Requests left in the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Reset": {
                                "description": "Seconds until the window resets.",
                                "schema": {
                                    "type": "integer"
                                }
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/AccommodationList"
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/InsufficientScope"
                    }
                }
            }
        },
        "/accommodations/{id}": {
            "get": {
                "tags": [
                    "property"
                ],
                "summary": "Get one unit layout",
                "description": "A single layout by its identifier.",
                "operationId": "getAccommodation",
                "security": [
                    {
                        "BearerApiKey": [
                            "property:read"
                        ]
                    },
                    {
                        "ApiKeyHeader": [
                            "property:read"
                        ]
                    },
                    []
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "Layout identifier.",
                        "schema": {
                            "type": "string",
                            "enum": [
                                "2-bedroom-suite",
                                "3-bedroom-penthouse"
                            ]
                        }
                    },
                    {
                        "$ref": "#/components/parameters/Sandbox"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The layout.",
                        "headers": {
                            "RateLimit": {
                                "description": "Current counter, draft-ietf-httpapi-ratelimit-headers form: \"policy\";r=remaining;t=seconds-until-reset.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";r=297;t=280"
                                    ]
                                }
                            },
                            "RateLimit-Policy": {
                                "description": "The policy in force: \"name\";q=quota;w=window-seconds.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";q=300;w=300"
                                    ]
                                }
                            },
                            "RateLimit-Limit": {
                                "description": "Quota for the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Remaining": {
                                "description": "Requests left in the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Reset": {
                                "description": "Seconds until the window resets.",
                                "schema": {
                                    "type": "integer"
                                }
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Accommodation"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/InsufficientScope"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        },
        "/policies": {
            "get": {
                "tags": [
                    "policies"
                ],
                "summary": "Booking and guest policies",
                "description": "Deposit, cancellation deadlines and refund terms, pet policy and fees, smoking, parking and connectivity.",
                "operationId": "getPolicies",
                "security": [
                    {
                        "BearerApiKey": [
                            "policies:read"
                        ]
                    },
                    {
                        "ApiKeyHeader": [
                            "policies:read"
                        ]
                    },
                    []
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Sandbox"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Booking and guest policies.",
                        "headers": {
                            "RateLimit": {
                                "description": "Current counter, draft-ietf-httpapi-ratelimit-headers form: \"policy\";r=remaining;t=seconds-until-reset.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";r=297;t=280"
                                    ]
                                }
                            },
                            "RateLimit-Policy": {
                                "description": "The policy in force: \"name\";q=quota;w=window-seconds.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";q=300;w=300"
                                    ]
                                }
                            },
                            "RateLimit-Limit": {
                                "description": "Quota for the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Remaining": {
                                "description": "Requests left in the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Reset": {
                                "description": "Seconds until the window resets.",
                                "schema": {
                                    "type": "integer"
                                }
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Policies"
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/InsufficientScope"
                    }
                }
            }
        },
        "/pages": {
            "get": {
                "tags": [
                    "content"
                ],
                "summary": "List published pages",
                "description": "Every published page and post with its canonical URL and its Markdown URL.",
                "operationId": "listPages",
                "security": [
                    {
                        "BearerApiKey": [
                            "content:read"
                        ]
                    },
                    {
                        "ApiKeyHeader": [
                            "content:read"
                        ]
                    },
                    []
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Sandbox"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "List published pages.",
                        "headers": {
                            "RateLimit": {
                                "description": "Current counter, draft-ietf-httpapi-ratelimit-headers form: \"policy\";r=remaining;t=seconds-until-reset.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";r=297;t=280"
                                    ]
                                }
                            },
                            "RateLimit-Policy": {
                                "description": "The policy in force: \"name\";q=quota;w=window-seconds.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";q=300;w=300"
                                    ]
                                }
                            },
                            "RateLimit-Limit": {
                                "description": "Quota for the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Remaining": {
                                "description": "Requests left in the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Reset": {
                                "description": "Seconds until the window resets.",
                                "schema": {
                                    "type": "integer"
                                }
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/PageList"
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/InsufficientScope"
                    }
                }
            }
        },
        "/keys": {
            "post": {
                "tags": [
                    "auth"
                ],
                "summary": "Issue a scoped read-only API key",
                "description": "Self-serve and unauthenticated: an agent cannot fill in a contact form. Keys are read-only, expire after 90 days, and are stored only as a SHA-256 hash, so the key is shown once and cannot be recovered. Issuance is limited to 5 keys per hour per address.\n\nA key is optional — every read scope works anonymously. Request one for the higher rate limit.",
                "operationId": "issueApiKey",
                "security": [
                    []
                ],
                "requestBody": {
                    "required": false,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/KeyRequest"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Key issued. The secret is shown once and cannot be retrieved again.",
                        "headers": {
                            "RateLimit": {
                                "description": "Current counter, draft-ietf-httpapi-ratelimit-headers form: \"policy\";r=remaining;t=seconds-until-reset.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";r=297;t=280"
                                    ]
                                }
                            },
                            "RateLimit-Policy": {
                                "description": "The policy in force: \"name\";q=quota;w=window-seconds.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";q=300;w=300"
                                    ]
                                }
                            },
                            "RateLimit-Limit": {
                                "description": "Quota for the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Remaining": {
                                "description": "Requests left in the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Reset": {
                                "description": "Seconds until the window resets.",
                                "schema": {
                                    "type": "integer"
                                }
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/KeyIssued"
                                }
                            }
                        }
                    },
                    "400": {
                        "$ref": "#/components/responses/BadRequest"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        },
        "/keys/me": {
            "get": {
                "tags": [
                    "auth"
                ],
                "summary": "Introspect the presented key",
                "description": "Reports whether the key is active, what scopes it carries, whether it is in sandbox mode, and its rate limit.",
                "operationId": "introspectKey",
                "security": [
                    []
                ],
                "parameters": [
                    {
                        "$ref": "#/components/parameters/Sandbox"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Introspect the presented key.",
                        "headers": {
                            "RateLimit": {
                                "description": "Current counter, draft-ietf-httpapi-ratelimit-headers form: \"policy\";r=remaining;t=seconds-until-reset.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";r=297;t=280"
                                    ]
                                }
                            },
                            "RateLimit-Policy": {
                                "description": "The policy in force: \"name\";q=quota;w=window-seconds.",
                                "schema": {
                                    "type": "string",
                                    "examples": [
                                        "\"agent-api-anonymous\";q=300;w=300"
                                    ]
                                }
                            },
                            "RateLimit-Limit": {
                                "description": "Quota for the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Remaining": {
                                "description": "Requests left in the window.",
                                "schema": {
                                    "type": "integer"
                                }
                            },
                            "RateLimit-Reset": {
                                "description": "Seconds until the window resets.",
                                "schema": {
                                    "type": "integer"
                                }
                            }
                        },
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/KeyIntrospection"
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    }
                }
            }
        }
    },
    "components": {
        "securitySchemes": {
            "BearerApiKey": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "opaque",
                "description": "Scoped read-only key in the Authorization header: `Authorization: Bearer mlk_…`. Obtain one from POST /keys. Scopes are enforced per endpoint; a call outside the key's scopes returns 403 with a WWW-Authenticate challenge naming the missing scope.\n\nAvailable scopes:\n- `property:read` — Read property details, unit layouts, amenities and location.\n- `policies:read` — Read booking, cancellation, deposit and pet policies.\n- `content:read` — Read published page and post content as Markdown."
            },
            "ApiKeyHeader": {
                "type": "apiKey",
                "in": "header",
                "name": "X-API-Key",
                "description": "The same scoped key, for clients that cannot set Authorization.\n\nAvailable scopes:\n- `property:read` — Read property details, unit layouts, amenities and location.\n- `policies:read` — Read booking, cancellation, deposit and pet policies.\n- `content:read` — Read published page and post content as Markdown."
            }
        },
        "parameters": {
            "Sandbox": {
                "name": "sandbox",
                "in": "query",
                "required": false,
                "description": "Return sandbox output: the same shape, labelled `\"_sandbox\": true`. Needs no credential. A query parameter rather than a header, because the page cache in front of this API keys on the URL.",
                "schema": {
                    "type": "boolean",
                    "default": false
                }
            }
        },
        "responses": {
            "Unauthorized": {
                "description": "The presented key is unknown or expired.",
                "content": {
                    "application/problem+json": {
                        "schema": {
                            "$ref": "#/components/schemas/Problem"
                        }
                    }
                },
                "headers": {
                    "WWW-Authenticate": {
                        "description": "Bearer challenge naming the error and, on a scope failure, the missing scope.",
                        "schema": {
                            "type": "string",
                            "examples": [
                                "Bearer realm=\"https://marinersloftsaltspring.ca/\", error=\"insufficient_scope\", scope=\"policies:read\""
                            ]
                        }
                    }
                }
            },
            "InsufficientScope": {
                "description": "The key is valid but lacks the required scope. The body names the required scope and the granted ones.",
                "content": {
                    "application/problem+json": {
                        "schema": {
                            "$ref": "#/components/schemas/Problem"
                        }
                    }
                },
                "headers": {
                    "WWW-Authenticate": {
                        "description": "Bearer challenge naming the error and, on a scope failure, the missing scope.",
                        "schema": {
                            "type": "string",
                            "examples": [
                                "Bearer realm=\"https://marinersloftsaltspring.ca/\", error=\"insufficient_scope\", scope=\"policies:read\""
                            ]
                        }
                    }
                }
            },
            "NotFound": {
                "description": "No such resource.",
                "content": {
                    "application/problem+json": {
                        "schema": {
                            "$ref": "#/components/schemas/Problem"
                        }
                    }
                }
            },
            "BadRequest": {
                "description": "The request could not be understood.",
                "content": {
                    "application/problem+json": {
                        "schema": {
                            "$ref": "#/components/schemas/Problem"
                        }
                    }
                }
            },
            "Forbidden": {
                "description": "Refused.",
                "content": {
                    "application/problem+json": {
                        "schema": {
                            "$ref": "#/components/schemas/Problem"
                        }
                    }
                }
            },
            "RateLimited": {
                "description": "Rate limit exceeded. Retry after the interval in Retry-After.",
                "content": {
                    "application/problem+json": {
                        "schema": {
                            "$ref": "#/components/schemas/Problem"
                        }
                    }
                },
                "headers": {
                    "Retry-After": {
                        "description": "Seconds to wait before retrying.",
                        "schema": {
                            "type": "integer"
                        }
                    }
                }
            }
        },
        "schemas": {
            "Problem": {
                "type": "object",
                "title": "Problem",
                "description": "RFC 9457 problem details. The WordPress error is preserved under `wp` so existing clients keep working.",
                "required": [
                    "type",
                    "title",
                    "status"
                ],
                "properties": {
                    "type": {
                        "type": "string",
                        "format": "uri",
                        "description": "Stable identifier for this problem kind. Resolves to an explanation in the docs. Branch on this, not on the text."
                    },
                    "title": {
                        "type": "string",
                        "description": "Short, stable summary. Does not change between occurrences."
                    },
                    "status": {
                        "type": "integer",
                        "description": "HTTP status code."
                    },
                    "detail": {
                        "type": "string",
                        "description": "What went wrong this time. Safe to show a user."
                    },
                    "instance": {
                        "type": "string",
                        "format": "uri",
                        "description": "The endpoint that produced the error."
                    },
                    "code": {
                        "type": "string",
                        "description": "Machine-readable error code."
                    },
                    "required_scope": {
                        "type": "string",
                        "description": "On a 403: the scope the endpoint needed."
                    },
                    "granted_scopes": {
                        "type": "array",
                        "items": {
                            "type": "string",
                            "description": "A scope this API understands.",
                            "enum": [
                                "property:read",
                                "policies:read",
                                "content:read"
                            ]
                        },
                        "description": "On a 403: what the caller actually holds."
                    },
                    "retry_after": {
                        "type": "integer",
                        "description": "On a 429: seconds to wait."
                    },
                    "docs": {
                        "type": "string",
                        "format": "uri"
                    },
                    "wp": {
                        "type": "object",
                        "description": "The original WordPress error shape.",
                        "properties": {
                            "code": {
                                "type": "string"
                            },
                            "message": {
                                "type": "string"
                            },
                            "data": {
                                "type": "object"
                            }
                        }
                    }
                }
            },
            "PostalAddress": {
                "type": "object",
                "title": "PostalAddress",
                "required": [
                    "street",
                    "locality",
                    "region",
                    "country"
                ],
                "properties": {
                    "street": {
                        "type": "string"
                    },
                    "locality": {
                        "type": "string"
                    },
                    "region": {
                        "type": "string",
                        "description": "Province or state code."
                    },
                    "postal_code": {
                        "type": "string"
                    },
                    "country": {
                        "type": "string",
                        "description": "ISO 3166-1 alpha-2."
                    }
                }
            },
            "GeoCoordinates": {
                "type": "object",
                "title": "GeoCoordinates",
                "required": [
                    "latitude",
                    "longitude"
                ],
                "properties": {
                    "latitude": {
                        "type": "number",
                        "format": "double"
                    },
                    "longitude": {
                        "type": "number",
                        "format": "double"
                    }
                }
            },
            "Property": {
                "type": "object",
                "title": "Property",
                "description": "The property itself.",
                "required": [
                    "name",
                    "url",
                    "address",
                    "total_units"
                ],
                "properties": {
                    "name": {
                        "type": "string"
                    },
                    "description": {
                        "type": "string"
                    },
                    "url": {
                        "type": "string",
                        "format": "uri"
                    },
                    "telephone": {
                        "type": "string",
                        "description": "E.164-ish, with separators."
                    },
                    "email": {
                        "type": "string",
                        "format": "email"
                    },
                    "address": {
                        "$ref": "#/components/schemas/PostalAddress"
                    },
                    "geo": {
                        "$ref": "#/components/schemas/GeoCoordinates"
                    },
                    "total_units": {
                        "type": "integer",
                        "description": "Rentable units across all layouts."
                    },
                    "check_in": {
                        "type": "string",
                        "description": "Local time, 24-hour, HH:MM."
                    },
                    "check_out": {
                        "type": "string",
                        "description": "Local time, 24-hour, HH:MM."
                    },
                    "currency": {
                        "type": "string",
                        "description": "ISO 4217."
                    },
                    "booking_url": {
                        "type": "string",
                        "format": "uri",
                        "description": "Where a traveller completes a reservation. This API cannot book."
                    },
                    "same_as": {
                        "type": "array",
                        "items": {
                            "type": "string",
                            "format": "uri"
                        }
                    },
                    "_sandbox": {
                        "type": "boolean",
                        "description": "Present and true only in sandbox mode."
                    },
                    "_notice": {
                        "type": "string",
                        "description": "Present only in sandbox mode."
                    }
                }
            },
            "Fee": {
                "type": "object",
                "title": "Fee",
                "required": [
                    "name",
                    "amount",
                    "currency"
                ],
                "properties": {
                    "name": {
                        "type": "string"
                    },
                    "amount": {
                        "type": "number"
                    },
                    "currency": {
                        "type": "string"
                    },
                    "per": {
                        "type": "string",
                        "enum": [
                            "stay",
                            "night"
                        ]
                    }
                }
            },
            "Accommodation": {
                "type": "object",
                "title": "Accommodation",
                "description": "One unit layout.",
                "required": [
                    "id",
                    "name",
                    "bedrooms",
                    "units"
                ],
                "properties": {
                    "id": {
                        "type": "string",
                        "enum": [
                            "2-bedroom-suite",
                            "3-bedroom-penthouse"
                        ]
                    },
                    "name": {
                        "type": "string"
                    },
                    "url": {
                        "type": "string",
                        "format": "uri"
                    },
                    "bedrooms": {
                        "type": "integer"
                    },
                    "units": {
                        "type": "integer",
                        "description": "How many of this layout exist."
                    },
                    "description": {
                        "type": "string"
                    },
                    "floor_area": {
                        "type": "object",
                        "properties": {
                            "value": {
                                "type": "number"
                            },
                            "unit": {
                                "type": "string",
                                "enum": [
                                    "SQFT"
                                ]
                            }
                        }
                    },
                    "amenities": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        }
                    },
                    "fees": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Fee"
                        }
                    }
                }
            },
            "AccommodationList": {
                "type": "object",
                "title": "AccommodationList",
                "required": [
                    "accommodations"
                ],
                "properties": {
                    "accommodations": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Accommodation"
                        }
                    },
                    "_sandbox": {
                        "type": "boolean"
                    },
                    "_notice": {
                        "type": "string"
                    }
                }
            },
            "CancellationDeadline": {
                "type": "object",
                "title": "CancellationDeadline",
                "required": [
                    "scope",
                    "days_before_arrival"
                ],
                "properties": {
                    "scope": {
                        "type": "string",
                        "description": "How much of the property is booked.",
                        "enum": [
                            "one suite",
                            "two or more suites",
                            "full property"
                        ]
                    },
                    "days_before_arrival": {
                        "type": "integer"
                    }
                }
            },
            "Policies": {
                "type": "object",
                "title": "Policies",
                "description": "Booking and guest policies. Quote the cancellation terms exactly — they have financial consequences for a guest who gets them wrong.",
                "required": [
                    "cancellation",
                    "pets",
                    "check_in",
                    "check_out"
                ],
                "properties": {
                    "check_in": {
                        "type": "object",
                        "properties": {
                            "from": {
                                "type": "string"
                            },
                            "until": {
                                "type": [
                                    "string",
                                    "null"
                                ]
                            },
                            "self_check_in": {
                                "type": "boolean"
                            }
                        }
                    },
                    "check_out": {
                        "type": "object",
                        "properties": {
                            "until": {
                                "type": "string"
                            }
                        }
                    },
                    "deposit": {
                        "type": "object",
                        "properties": {
                            "percent": {
                                "type": "integer"
                            },
                            "charged": {
                                "type": "string"
                            },
                            "description": {
                                "type": "string"
                            }
                        }
                    },
                    "cancellation": {
                        "type": "object",
                        "required": [
                            "deadlines",
                            "before_deadline",
                            "after_deadline"
                        ],
                        "properties": {
                            "deadlines": {
                                "type": "array",
                                "items": {
                                    "$ref": "#/components/schemas/CancellationDeadline"
                                }
                            },
                            "before_deadline": {
                                "type": "string"
                            },
                            "after_deadline": {
                                "type": "string"
                            },
                            "card_fee_percent": {
                                "type": "number"
                            },
                            "goodwill": {
                                "type": "string",
                                "description": "Discretionary. Never present this as a guarantee."
                            },
                            "policy_url": {
                                "type": "string",
                                "format": "uri"
                            }
                        }
                    },
                    "pets": {
                        "type": "object",
                        "properties": {
                            "allowed": {
                                "type": "boolean"
                            },
                            "species": {
                                "type": "string"
                            },
                            "fee_per_pet_night": {
                                "type": "number"
                            },
                            "currency": {
                                "type": "string"
                            },
                            "max_pets_per_suite": {
                                "type": "integer"
                            },
                            "size_limit": {
                                "type": [
                                    "string",
                                    "null"
                                ],
                                "description": "null means no size limit."
                            },
                            "registration": {
                                "type": "string"
                            },
                            "provided": {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                }
                            }
                        }
                    },
                    "smoking": {
                        "type": "object",
                        "properties": {
                            "allowed": {
                                "type": "boolean"
                            }
                        }
                    },
                    "parking": {
                        "type": "object",
                        "properties": {
                            "on_site": {
                                "type": "boolean"
                            },
                            "included": {
                                "type": "boolean"
                            }
                        }
                    },
                    "connectivity": {
                        "type": "object",
                        "properties": {
                            "wifi": {
                                "type": "string"
                            },
                            "television": {
                                "type": "string"
                            }
                        }
                    },
                    "authoritative_source": {
                        "type": "string",
                        "format": "uri"
                    },
                    "note": {
                        "type": "string"
                    },
                    "_sandbox": {
                        "type": "boolean"
                    },
                    "_notice": {
                        "type": "string"
                    }
                }
            },
            "PageSummary": {
                "type": "object",
                "title": "PageSummary",
                "required": [
                    "id",
                    "title",
                    "url"
                ],
                "properties": {
                    "id": {
                        "type": "integer"
                    },
                    "type": {
                        "type": "string",
                        "enum": [
                            "page",
                            "post"
                        ]
                    },
                    "title": {
                        "type": "string"
                    },
                    "url": {
                        "type": "string",
                        "format": "uri"
                    },
                    "markdown_url": {
                        "type": "string",
                        "format": "uri",
                        "description": "The .md twin. The canonical URL also answers to Accept: text/markdown."
                    },
                    "modified": {
                        "type": "string",
                        "format": "date-time"
                    }
                }
            },
            "PageList": {
                "type": "object",
                "title": "PageList",
                "required": [
                    "count",
                    "pages"
                ],
                "properties": {
                    "count": {
                        "type": "integer"
                    },
                    "note": {
                        "type": "string"
                    },
                    "pages": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/PageSummary"
                        }
                    }
                }
            },
            "ScopeDescriptor": {
                "type": "object",
                "title": "ScopeDescriptor",
                "required": [
                    "scope",
                    "description"
                ],
                "properties": {
                    "scope": {
                        "type": "string",
                        "description": "A scope this API understands.",
                        "enum": [
                            "property:read",
                            "policies:read",
                            "content:read"
                        ]
                    },
                    "description": {
                        "type": "string"
                    },
                    "granted_anonymous": {
                        "type": "boolean"
                    },
                    "read_only": {
                        "type": "boolean"
                    }
                }
            },
            "ScopeCatalogue": {
                "type": "object",
                "title": "ScopeCatalogue",
                "required": [
                    "scopes"
                ],
                "properties": {
                    "scopes": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/ScopeDescriptor"
                        }
                    },
                    "anonymous_scopes": {
                        "type": "array",
                        "items": {
                            "type": "string",
                            "description": "A scope this API understands.",
                            "enum": [
                                "property:read",
                                "policies:read",
                                "content:read"
                            ]
                        }
                    },
                    "documentation": {
                        "type": "string",
                        "format": "uri"
                    }
                }
            },
            "EndpointDescriptor": {
                "type": "object",
                "title": "EndpointDescriptor",
                "properties": {
                    "method": {
                        "type": "string",
                        "enum": [
                            "GET",
                            "POST"
                        ]
                    },
                    "path": {
                        "type": "string",
                        "format": "uri"
                    },
                    "scope": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "summary": {
                        "type": "string"
                    }
                }
            },
            "Discovery": {
                "type": "object",
                "title": "Discovery",
                "description": "Self-description of the service. Fetch this first.",
                "required": [
                    "service",
                    "version",
                    "endpoints"
                ],
                "properties": {
                    "service": {
                        "type": "string"
                    },
                    "version": {
                        "type": "string"
                    },
                    "description": {
                        "type": "string"
                    },
                    "documentation": {
                        "type": "string",
                        "format": "uri"
                    },
                    "openapi": {
                        "type": "string",
                        "format": "uri"
                    },
                    "llms_txt": {
                        "type": "string",
                        "format": "uri"
                    },
                    "terms": {
                        "type": "string",
                        "format": "uri"
                    },
                    "auth": {
                        "type": "object",
                        "properties": {
                            "required": {
                                "type": "boolean"
                            },
                            "anonymous_scopes": {
                                "type": "array",
                                "items": {
                                    "type": "string",
                                    "description": "A scope this API understands.",
                                    "enum": [
                                        "property:read",
                                        "policies:read",
                                        "content:read"
                                    ]
                                }
                            },
                            "schemes": {
                                "type": "array",
                                "items": {
                                    "type": "string"
                                }
                            },
                            "self_serve_keys": {
                                "type": "boolean"
                            },
                            "issue_endpoint": {
                                "type": "string",
                                "format": "uri"
                            },
                            "sandbox_available": {
                                "type": "boolean"
                            }
                        }
                    },
                    "sandbox": {
                        "type": "object",
                        "properties": {
                            "available": {
                                "type": "boolean"
                            },
                            "no_key": {
                                "type": "boolean"
                            },
                            "how": {
                                "type": "string"
                            },
                            "try_it": {
                                "type": "string",
                                "format": "uri"
                            },
                            "persistent": {
                                "type": "string"
                            },
                            "marker": {
                                "type": "string"
                            }
                        }
                    },
                    "rate_limits": {
                        "type": "object"
                    },
                    "errors": {
                        "type": "object"
                    },
                    "lifecycle": {
                        "$ref": "#/components/schemas/Lifecycle"
                    },
                    "you": {
                        "type": "object",
                        "properties": {
                            "authenticated": {
                                "type": "boolean"
                            },
                            "sandbox": {
                                "type": "boolean"
                            },
                            "scopes": {
                                "type": "array",
                                "items": {
                                    "type": "string",
                                    "description": "A scope this API understands.",
                                    "enum": [
                                        "property:read",
                                        "policies:read",
                                        "content:read"
                                    ]
                                }
                            }
                        }
                    },
                    "endpoints": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/EndpointDescriptor"
                        }
                    },
                    "booking": {
                        "type": "object",
                        "properties": {
                            "note": {
                                "type": "string"
                            },
                            "url": {
                                "type": "string",
                                "format": "uri"
                            }
                        }
                    },
                    "routes": {
                        "type": "object",
                        "description": "WordPress's own route map, preserved for generic REST clients."
                    }
                }
            },
            "Lifecycle": {
                "type": "object",
                "title": "Lifecycle",
                "description": "How this API is versioned and how deprecations are announced.",
                "properties": {
                    "current_version": {
                        "type": "string"
                    },
                    "versioning": {
                        "type": "string"
                    },
                    "breaking_change": {
                        "type": "string"
                    },
                    "additive_change": {
                        "type": "string"
                    },
                    "deprecation_signals": {
                        "type": "object"
                    },
                    "minimum_notice_days": {
                        "type": "integer"
                    },
                    "policy_url": {
                        "type": "string",
                        "format": "uri"
                    },
                    "currently_deprecated": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        }
                    }
                }
            },
            "KeyRequest": {
                "type": "object",
                "title": "KeyRequest",
                "properties": {
                    "label": {
                        "type": "string",
                        "description": "What the key is for. Returned on introspection."
                    },
                    "scopes": {
                        "type": "array",
                        "items": {
                            "type": "string",
                            "description": "A scope this API understands.",
                            "enum": [
                                "property:read",
                                "policies:read",
                                "content:read"
                            ]
                        },
                        "description": "Defaults to every read scope. Ask for only what you need."
                    },
                    "sandbox": {
                        "type": "boolean",
                        "default": false,
                        "description": "Issue a key that is permanently in sandbox mode."
                    }
                }
            },
            "KeyIssued": {
                "type": "object",
                "title": "KeyIssued",
                "required": [
                    "api_key",
                    "scopes",
                    "expires_at"
                ],
                "properties": {
                    "api_key": {
                        "type": "string",
                        "description": "The secret. Shown once; only a SHA-256 hash is stored."
                    },
                    "scopes": {
                        "type": "array",
                        "items": {
                            "type": "string",
                            "description": "A scope this API understands.",
                            "enum": [
                                "property:read",
                                "policies:read",
                                "content:read"
                            ]
                        }
                    },
                    "rejected_scopes": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        },
                        "description": "Requested scopes that do not exist."
                    },
                    "sandbox": {
                        "type": "boolean"
                    },
                    "expires_at": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "rate_limit": {
                        "$ref": "#/components/schemas/RateLimitInfo"
                    },
                    "usage": {
                        "type": "string"
                    },
                    "stored": {
                        "type": "string"
                    },
                    "documentation": {
                        "type": "string",
                        "format": "uri"
                    }
                }
            },
            "RateLimitInfo": {
                "type": "object",
                "title": "RateLimitInfo",
                "properties": {
                    "requests": {
                        "type": "integer"
                    },
                    "per_seconds": {
                        "type": "integer"
                    },
                    "policy": {
                        "type": "string"
                    }
                }
            },
            "KeyIntrospection": {
                "type": "object",
                "title": "KeyIntrospection",
                "required": [
                    "active"
                ],
                "properties": {
                    "active": {
                        "type": "boolean"
                    },
                    "anonymous": {
                        "type": "boolean"
                    },
                    "reason": {
                        "type": "string",
                        "description": "Why an inactive key was rejected."
                    },
                    "label": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "key": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Truncated for display; never the full secret."
                    },
                    "scopes": {
                        "type": "array",
                        "items": {
                            "type": "string",
                            "description": "A scope this API understands.",
                            "enum": [
                                "property:read",
                                "policies:read",
                                "content:read"
                            ]
                        }
                    },
                    "sandbox": {
                        "type": "boolean"
                    },
                    "rate_limit": {
                        "$ref": "#/components/schemas/RateLimitInfo"
                    },
                    "hint": {
                        "type": "string"
                    }
                }
            }
        }
    },
    "x-api-lifecycle": {
        "current_version": "1.0.0",
        "versioning": "URL path. The major version is the `v1` segment of /wp-json/marinersloft/v1/. A breaking change ships as /v2/ at a new path; /v1/ keeps working.",
        "breaking_change": "Removing an endpoint or field, renaming a field, narrowing a type, or adding a required parameter.",
        "additive_change": "New endpoints, new optional parameters and new response fields ship inside the current version without notice. Clients must ignore unknown fields.",
        "deprecation_signals": {
            "Deprecation": "RFC 9745. Present from the moment an endpoint is deprecated.",
            "Sunset": "RFC 8594. The date the endpoint stops responding.",
            "Link": "rel=\"successor-version\" points at the replacement; rel=\"deprecation\" at this policy."
        },
        "minimum_notice_days": 180,
        "policy_url": "https://marinersloftsaltspring.ca/for-agents/#versioning",
        "currently_deprecated": []
    },
    "x-agent-usage": {
        "anonymous_scopes": [
            "property:read",
            "policies:read",
            "content:read"
        ],
        "rate_limits": {
            "anonymous": {
                "requests": 300,
                "per_seconds": 300
            },
            "with_key": {
                "requests": 3000,
                "per_seconds": 300
            },
            "other_rest": {
                "requests": 600,
                "per_seconds": 300
            }
        },
        "sandbox": {
            "available": true,
            "no_key": true,
            "how": "Add ?sandbox=1 to any read endpoint.",
            "try_it": "https://marinersloftsaltspring.ca/wp-json/marinersloft/v1/property?sandbox=1"
        },
        "markdown": {
            "note": "Every page URL also serves Markdown via Accept: text/markdown, or by appending .md to the path.",
            "llms_txt": "https://marinersloftsaltspring.ca/llms.txt"
        },
        "cli": {
            "package": "@marinersloft/cli",
            "usage": "npx @marinersloft/cli policies",
            "source": "https://marinersloftsaltspring.ca/for-agents/#cli"
        }
    }
}