{
  "openapi": "3.1.0",
  "info": {
    "title": "PLAT.HAUS",
    "version": "1.0.0",
    "summary": "Read-only public API for booking recording studio sessions, mixing and mastering, beat licensing and production consultations with PLAT in Austin, Texas.",
    "description": "PLAT.HAUS is the creative house of PLAT — a 2x platinum record producer and audio engineer based in Austin, Texas.\n\nThis API is **read-only and unauthenticated**. There is no API key, no account, no rate-limit header and no write operation: every endpoint answers `GET` and nothing else. It exists so an agent can answer \"what does PLAT do, what does it cost, which room, and is that night open?\" without scraping the app.\n\nBooking itself is not an API call. Every response that could lead to a purchase carries a `bookingUrl` — hand that link to the person; payment, scheduling and terms all happen there.\n\nPrices published here are **entry points**, never quotes. The full rate card is deliberately private, and so are exact studio addresses — those go out on the calendar invite once a session is confirmed.\n\n## Versioning\n\nThe current version is **v1**. Endpoints are versioned in the URL path: `https://plat.haus/api/v1/services`. The unversioned form `/api/services` is a permanent alias for the current major and answers identically — pin `/api/v1/` if you want the shape documented here to keep answering after a v2 exists. Every response carries an `API-Version` header naming the contract that served it.\n\n**The compatibility promise.** Inside a major version this API only ever adds: new endpoints, new optional query parameters, new fields on existing objects. Nothing is removed, renamed or retyped, and no field changes meaning. Anything that would break a caller ships as `/api/v2/` instead. Treat unknown response fields as additive and ignore them.\n\n**Deprecation.** When a version is retired it is announced on the responses themselves, at least 6 months ahead, using `Deprecation` (RFC 9745) and `Sunset` (RFC 8594) headers plus a `Link: <...>; rel=\"deprecation\"` pointing at the notes. Neither header is emitted today — nothing is deprecated. An agent that watches for them gets the full notice period automatically.\n\n## Rate limits\n\n120 requests per 60 seconds per client IP — a ceiling to pace against. Every response carries `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` (delta-seconds, never a timestamp) and `RateLimit-Policy`; a 429 adds `Retry-After`.\n\n**Read `Remaining` for what it is.** It counts the edge instance that served you, and instances are short-lived and not shared, so it normally reads close to the limit however fast you call. It is not a live global budget. Pace against the published number, and treat a 429 with `Retry-After` as the authoritative signal to back off.\n\nResponses are CDN-cached for minutes at a time, so polling the same endpoint in a loop returns the same bytes and buys nothing.",
    "contact": {
      "name": "PLAT.HAUS",
      "email": "hello@plat.haus",
      "url": "https://plat.haus"
    },
    "license": {
      "name": "Terms of Service",
      "url": "https://plat.haus/terms"
    }
  },
  "servers": [
    {
      "url": "https://plat.haus",
      "description": "Production"
    }
  ],
  "security": [],
  "externalDocs": {
    "description": "llms.txt — what PLAT.HAUS does and when to reach for it",
    "url": "https://plat.haus/llms.txt"
  },
  "tags": [
    {
      "name": "discovery",
      "description": "What this API is and what it offers."
    },
    {
      "name": "catalog",
      "description": "Services, rooms and beat packs."
    },
    {
      "name": "scheduling",
      "description": "When a studio can be booked."
    }
  ],
  "paths": {
    "/api/v1": {
      "get": {
        "operationId": "getApiIndex",
        "summary": "API index",
        "description": "Lists every endpoint on this API with a one-line description, states that no authentication is required, and links the OpenAPI document. Start here when you have no other context.",
        "tags": [
          "discovery"
        ],
        "responses": {
          "200": {
            "description": "The endpoint index.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiIndex"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    },
    "/api/v1/services": {
      "get": {
        "operationId": "listServices",
        "summary": "List services and entry prices",
        "description": "Everything PLAT.HAUS sells: studio sessions, mixing and mastering, master-only, beat licensing and production consultations. Each service carries its entry price in USD, the unit that price is charged in, whether it is delivered in person in Austin or remotely, and the URL a person uses to book it.\n\n`priceFrom` is the cheapest real starting point for that service, not a quote — final cost depends on room, session length, song count, license tier and add-ons. Master-only deliberately shares a booking URL with mixing: it is a scope option inside the mix order, not a separate product.",
        "tags": [
          "catalog"
        ],
        "responses": {
          "200": {
            "description": "The service catalog.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceList"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": false,
            "description": "Return only this service. Omit for the whole catalog. The envelope is the same either way — always read `services[]`.",
            "schema": {
              "type": "string",
              "enum": [
                "studio-session",
                "mix-master",
                "master-only",
                "beat-license",
                "consultation"
              ],
              "examples": [
                "mix-master"
              ]
            }
          }
        ]
      }
    },
    "/api/v1/studios": {
      "get": {
        "operationId": "listStudios",
        "summary": "List bookable rooms",
        "description": "The Austin rooms PLAT records in, in display order. Each entry gives the room's slug (use it with `getAvailability`), its name, its neighborhood, a description, the rooms inside it, notable gear, photos, and its booking terms — hourly with a minimum, or block-only.\n\nExact street addresses are not published and are not inferable from this response. They are sent on the calendar invite once a session is confirmed.",
        "tags": [
          "catalog"
        ],
        "responses": {
          "200": {
            "description": "The studio list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StudioList"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        },
        "parameters": [
          {
            "name": "slug",
            "in": "query",
            "required": false,
            "description": "Return only this room, by the slug `getAvailability` uses. Omit for every room. Unknown slug is a 404, never an empty list.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "examples": [
                "infinite_atx"
              ]
            }
          }
        ]
      }
    },
    "/api/v1/packs": {
      "get": {
        "operationId": "listBeatPacks",
        "summary": "List public beat packs",
        "description": "Published beat packs, most recently updated first. Each pack carries its slug, name, volume number, description, cover image and the public page where a person can preview its first beats and license them.\n\nOnly listed store packs appear. Unreleased drops and private client packs are not part of this catalog and cannot be reached from it.",
        "tags": [
          "catalog"
        ],
        "responses": {
          "200": {
            "description": "The public pack catalog.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PackList"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Cap the number of packs returned, newest first. Omit for the whole catalog — it is dozens of packs, not thousands, so there is no cursor to carry.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "examples": [
                10
              ]
            }
          }
        ]
      }
    },
    "/api/v1/availability": {
      "get": {
        "operationId": "getAvailability",
        "summary": "Get bookable start times for one studio on one date",
        "description": "Returns every session start time PLAT can sell at one studio on one night, from 10 AM through midnight, each marked available or not. Times are ISO 8601 instants with the studio's real UTC offset, so the midnight slot correctly belongs to the following calendar day.\n\nThis is advisory and deliberately conservative: an hour is reported unavailable if anything on the studio's calendar overlaps it, including events this API cannot identify. It answers \"is this hour safe to sell?\", not \"is PLAT booked?\". Minimum booking length varies by room — check `listStudios` before assuming a single free hour is bookable. Availability is confirmed at booking, not here.",
        "tags": [
          "scheduling"
        ],
        "parameters": [
          {
            "name": "studio",
            "in": "query",
            "required": true,
            "description": "Studio slug, from `listStudios`. Rooms with no online calendar are call-to-book and return 404.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "examples": [
                "infinite_atx"
              ]
            }
          },
          {
            "name": "date",
            "in": "query",
            "required": true,
            "description": "Calendar date of the night in question, in the studio's local zone (America/Chicago).",
            "schema": {
              "type": "string",
              "format": "date",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "examples": [
                "2026-09-20"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The night's start-time grid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Availability"
                }
              }
            },
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "API-Version": {
                "$ref": "#/components/headers/API-Version"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          },
          "default": {
            "$ref": "#/components/responses/UnexpectedError"
          }
        }
      }
    }
  },
  "components": {
    "responses": {
      "BadRequest": {
        "description": "A parameter is missing or malformed. The `hint` names the fix.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "headers": {
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          },
          "API-Version": {
            "$ref": "#/components/headers/API-Version"
          }
        }
      },
      "NotFound": {
        "description": "No such endpoint, or no such studio.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "headers": {
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          },
          "API-Version": {
            "$ref": "#/components/headers/API-Version"
          }
        }
      },
      "MethodNotAllowed": {
        "description": "This API is read-only; only GET and HEAD are supported.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "headers": {
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          },
          "API-Version": {
            "$ref": "#/components/headers/API-Version"
          }
        }
      },
      "Unavailable": {
        "description": "A transient upstream failure. Retry shortly — this is never an empty result.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "headers": {
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          },
          "API-Version": {
            "$ref": "#/components/headers/API-Version"
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded. `Retry-After` says how long to wait.",
        "headers": {
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          },
          "API-Version": {
            "$ref": "#/components/headers/API-Version"
          },
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InternalError": {
        "description": "An unexpected server-side failure. Safe to retry.",
        "headers": {
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          },
          "API-Version": {
            "$ref": "#/components/headers/API-Version"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UnexpectedError": {
        "description": "Any other failure. Always the same typed error object.",
        "headers": {
          "RateLimit-Limit": {
            "$ref": "#/components/headers/RateLimit-Limit"
          },
          "RateLimit-Remaining": {
            "$ref": "#/components/headers/RateLimit-Remaining"
          },
          "RateLimit-Reset": {
            "$ref": "#/components/headers/RateLimit-Reset"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimit-Policy"
          },
          "API-Version": {
            "$ref": "#/components/headers/API-Version"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "title": "Error",
        "type": "object",
        "description": "Every error on this API has this shape.",
        "required": [
          "error",
          "message",
          "hint",
          "status"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Stable machine-readable code.",
            "enum": [
              "internal_error",
              "invalid_date",
              "invalid_parameter",
              "method_not_allowed",
              "missing_parameter",
              "rate_limited",
              "unknown_endpoint",
              "unknown_service",
              "unknown_studio",
              "upstream_unavailable"
            ]
          },
          "message": {
            "type": "string",
            "description": "What went wrong, in one sentence."
          },
          "hint": {
            "type": "string",
            "description": "How to fix it — a corrected example, or where to look."
          },
          "status": {
            "type": "integer",
            "description": "HTTP status, repeated in the body.",
            "examples": [
              400
            ]
          }
        }
      },
      "ApiIndex": {
        "title": "ApiIndex",
        "type": "object",
        "required": [
          "service",
          "description",
          "authentication",
          "rateLimit",
          "deprecationPolicy",
          "versioning",
          "apiVersion",
          "openapi",
          "endpoints",
          "contact",
          "website"
        ],
        "properties": {
          "service": {
            "type": "string",
            "description": "Name of the API.",
            "examples": [
              "PLAT.HAUS"
            ]
          },
          "description": {
            "type": "string",
            "description": "What PLAT.HAUS does."
          },
          "authentication": {
            "type": "string",
            "description": "Auth requirements. Always none.",
            "examples": [
              "none — public, read-only, no API key"
            ]
          },
          "apiVersion": {
            "type": "string",
            "description": "Current major version.",
            "examples": [
              "1"
            ]
          },
          "versioning": {
            "type": "string",
            "description": "How versions are addressed and what is guaranteed inside one."
          },
          "deprecationPolicy": {
            "type": "string",
            "description": "How and how far ahead a retirement is announced."
          },
          "rateLimit": {
            "type": "string",
            "description": "The limit in force and which headers report it."
          },
          "openapi": {
            "type": "string",
            "format": "uri",
            "description": "URL of this document."
          },
          "endpoints": {
            "type": "array",
            "description": "Every endpoint on this API.",
            "items": {
              "type": "object",
              "required": [
                "path",
                "description"
              ],
              "properties": {
                "path": {
                  "type": "string",
                  "description": "Path, relative to the server URL."
                },
                "description": {
                  "type": "string",
                  "description": "What it returns."
                }
              }
            }
          },
          "contact": {
            "type": "string",
            "format": "email",
            "description": "Where a human can ask."
          },
          "website": {
            "type": "string",
            "format": "uri",
            "description": "The public site."
          }
        }
      },
      "Service": {
        "title": "Service",
        "type": "object",
        "required": [
          "id",
          "name",
          "description",
          "priceFrom",
          "priceCurrency",
          "priceUnit",
          "delivery",
          "bookingUrl"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier.",
            "enum": [
              "studio-session",
              "mix-master",
              "master-only",
              "beat-license",
              "consultation"
            ]
          },
          "name": {
            "type": "string",
            "description": "Human-facing name."
          },
          "description": {
            "type": "string",
            "description": "What is actually delivered."
          },
          "priceFrom": {
            "type": "number",
            "description": "Entry price. The cheapest real starting point, never a quote.",
            "examples": [
              100
            ]
          },
          "priceCurrency": {
            "type": "string",
            "description": "ISO 4217 currency.",
            "enum": [
              "USD"
            ]
          },
          "priceUnit": {
            "type": "string",
            "description": "What the price is charged per.",
            "enum": [
              "per hour",
              "per song",
              "per beat",
              "per session"
            ]
          },
          "delivery": {
            "type": "string",
            "description": "Where the work happens.",
            "enum": [
              "in person (Austin, TX)",
              "remote"
            ]
          },
          "bookingUrl": {
            "type": "string",
            "format": "uri",
            "description": "Where a person books this. Hand this link to the user."
          }
        }
      },
      "ServiceList": {
        "title": "ServiceList",
        "type": "object",
        "required": [
          "services",
          "note"
        ],
        "properties": {
          "services": {
            "type": "array",
            "description": "Every service PLAT.HAUS sells.",
            "items": {
              "$ref": "#/components/schemas/Service"
            }
          },
          "note": {
            "type": "string",
            "description": "Why these numbers are floors and not quotes."
          }
        }
      },
      "Studio": {
        "title": "Studio",
        "type": "object",
        "required": [
          "slug",
          "name",
          "where",
          "description",
          "rooms",
          "booking",
          "listMeta",
          "gear",
          "photos"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "Identifier. Pass this to getAvailability.",
            "examples": [
              "infinite_atx"
            ]
          },
          "name": {
            "type": "string",
            "description": "Studio name."
          },
          "where": {
            "type": "string",
            "description": "Neighborhood only — never a street address.",
            "examples": [
              "NE Austin"
            ]
          },
          "description": {
            "type": "string",
            "description": "What the room is like."
          },
          "rooms": {
            "type": "array",
            "description": "Named rooms inside this studio, in display order.",
            "items": {
              "type": "string"
            }
          },
          "booking": {
            "type": "string",
            "description": "Booking terms in one sentence.",
            "examples": [
              "2 hour minimum · booked hourly"
            ]
          },
          "listMeta": {
            "type": "string",
            "description": "Short form of the booking terms.",
            "examples": [
              "2 hr min"
            ]
          },
          "gear": {
            "type": "array",
            "description": "Notable equipment, grouped by category.",
            "items": {
              "type": "object",
              "required": [
                "category",
                "items"
              ],
              "properties": {
                "category": {
                  "type": "string",
                  "description": "Gear category."
                },
                "items": {
                  "type": "array",
                  "description": "Pieces in that category.",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "photos": {
            "type": "array",
            "description": "Public image URLs.",
            "items": {
              "type": "string",
              "format": "uri"
            }
          }
        }
      },
      "StudioList": {
        "title": "StudioList",
        "type": "object",
        "required": [
          "studios",
          "note",
          "bookingUrl"
        ],
        "properties": {
          "studios": {
            "type": "array",
            "description": "Active rooms, in display order.",
            "items": {
              "$ref": "#/components/schemas/Studio"
            }
          },
          "note": {
            "type": "string",
            "description": "States that addresses are private and must not be guessed."
          },
          "bookingUrl": {
            "type": "string",
            "format": "uri",
            "description": "Where a person books a session."
          }
        }
      },
      "Pack": {
        "title": "Pack",
        "type": "object",
        "required": [
          "slug",
          "name",
          "volume",
          "description",
          "cover",
          "url",
          "updated"
        ],
        "properties": {
          "slug": {
            "type": "string",
            "description": "Identifier and last path segment of the public page.",
            "examples": [
              "platpack-005"
            ]
          },
          "name": {
            "type": "string",
            "description": "Pack name."
          },
          "volume": {
            "type": [
              "string",
              "null"
            ],
            "description": "Volume number, when the pack is part of a series."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "What the pack is."
          },
          "cover": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Cover artwork URL."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Public pack page. Hand this link to the user."
          },
          "updated": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Last time the pack changed."
          }
        }
      },
      "PackList": {
        "title": "PackList",
        "type": "object",
        "required": [
          "packs",
          "note",
          "browseUrl"
        ],
        "properties": {
          "packs": {
            "type": "array",
            "description": "Public packs, most recently updated first.",
            "items": {
              "$ref": "#/components/schemas/Pack"
            }
          },
          "note": {
            "type": "string",
            "description": "How previewing and licensing work."
          },
          "browseUrl": {
            "type": "string",
            "format": "uri",
            "description": "The beat store."
          }
        }
      },
      "Slot": {
        "title": "Slot",
        "type": "object",
        "required": [
          "start",
          "hour",
          "available"
        ],
        "properties": {
          "start": {
            "type": "string",
            "format": "date-time",
            "description": "Session start, ISO 8601 with the studio's real UTC offset."
          },
          "hour": {
            "type": "integer",
            "minimum": 10,
            "maximum": 24,
            "description": "Hour of the night, absolute. 24 is midnight at the END of the queried night.",
            "examples": [
              21
            ]
          },
          "available": {
            "type": "boolean",
            "description": "Whether a session can start here."
          }
        }
      },
      "Availability": {
        "title": "Availability",
        "type": "object",
        "required": [
          "studio",
          "date",
          "timeZone",
          "slots",
          "available",
          "note",
          "bookingUrl"
        ],
        "properties": {
          "studio": {
            "type": "string",
            "description": "The studio slug that was queried."
          },
          "date": {
            "type": "string",
            "format": "date",
            "description": "The date that was queried."
          },
          "timeZone": {
            "type": "string",
            "description": "IANA zone the date and hours are expressed in.",
            "enum": [
              "America/Chicago"
            ]
          },
          "slots": {
            "type": "array",
            "description": "Every start time on the night, available or not.",
            "items": {
              "$ref": "#/components/schemas/Slot"
            }
          },
          "available": {
            "type": "array",
            "description": "Just the bookable start times, as ISO 8601 instants.",
            "items": {
              "type": "string",
              "format": "date-time"
            }
          },
          "note": {
            "type": "string",
            "description": "How to read this grid, and what it does not mean."
          },
          "bookingUrl": {
            "type": "string",
            "format": "uri",
            "description": "Where a person books the session."
          }
        }
      }
    },
    "headers": {
      "RateLimit-Limit": {
        "description": "Requests allowed per window. The published ceiling.",
        "schema": {
          "type": "integer",
          "examples": [
            120
          ]
        }
      },
      "RateLimit-Remaining": {
        "description": "Requests left in the serving instance's window. Instances are short-lived, so this usually reads near the limit — see Rate limits.",
        "schema": {
          "type": "integer",
          "examples": [
            118
          ]
        }
      },
      "RateLimit-Reset": {
        "description": "Seconds until the window resets. A duration, never a timestamp.",
        "schema": {
          "type": "integer",
          "examples": [
            42
          ]
        }
      },
      "RateLimit-Policy": {
        "description": "The policy in force, as limit;w=window-seconds.",
        "schema": {
          "type": "string",
          "examples": [
            "120;w=60"
          ]
        }
      },
      "API-Version": {
        "description": "Major version of the contract that served this response.",
        "schema": {
          "type": "string",
          "examples": [
            "1"
          ]
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying. Sent on 429.",
        "schema": {
          "type": "integer",
          "examples": [
            42
          ]
        }
      }
    }
  }
}
