{
  "openapi": "3.1.0",
  "info": {
    "title": "Crystal Beach Wedding read-only availability API",
    "version": "1.0.0",
    "description": "Public date and package projections. Source verification is at most one hour old for confirmed states. Unknown or stale dates require confirmation. These endpoints do not reserve dates or accept payment."
  },
  "servers": [{ "url": "/" }],
  "paths": {
    "/api/v1/availability": {
      "get": {
        "summary": "Read wedding and tour availability",
        "description": "The inclusive date range may contain at most 1097 days. Omitted start defaults to today's venue-local date, and omitted end defaults to 1096 days after start. Query parameter refresh does not force a source read.",
        "parameters": [
          { "$ref": "#/components/parameters/start" },
          { "$ref": "#/components/parameters/end" },
          { "$ref": "#/components/parameters/package" }
        ],
        "responses": {
          "200": {
            "description": "Calendar with at least one confirmed date",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Availability" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "503": {
            "description": "Source unavailable or all requested dates unknown",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Availability" }
              }
            }
          }
        }
      }
    },
    "/api/v1/quote": {
      "get": {
        "summary": "Read a package quote for a date and guest count",
        "description": "Read-only quote. Omitted start and end default to three days before and after date. A status of needs-confirmation, unavailable, or a null price is not a booking offer. The response is not cached at the edge.",
        "parameters": [
          { "$ref": "#/components/parameters/start" },
          { "$ref": "#/components/parameters/end" },
          { "$ref": "#/components/parameters/package" },
          {
            "name": "date",
            "in": "query",
            "required": true,
            "schema": { "type": "string", "format": "date" }
          },
          {
            "name": "guests",
            "in": "query",
            "required": true,
            "schema": { "type": "integer", "minimum": 2 }
          },
          {
            "name": "addons",
            "in": "query",
            "schema": { "type": "string" },
            "description": "Comma-separated IDs from the published catalog."
          },
          {
            "name": "nightsBefore",
            "in": "query",
            "schema": { "type": "integer", "default": 0 },
            "description": "Extra nights are currently disabled; only zero is accepted."
          },
          {
            "name": "nightsAfter",
            "in": "query",
            "schema": { "type": "integer", "default": 0 },
            "description": "Extra nights are currently disabled; only zero is accepted."
          }
        ],
        "responses": {
          "200": {
            "description": "Quote, including a status indicating whether it is currently confirmable",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Quote" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "start": {
        "name": "start",
        "in": "query",
        "schema": { "type": "string", "format": "date" }
      },
      "end": {
        "name": "end",
        "in": "query",
        "schema": { "type": "string", "format": "date" }
      },
      "package": {
        "name": "package",
        "in": "query",
        "schema": {
          "type": "string",
          "enum": ["ceremony", "ceremony-reception", "ceremony-reception-stay"],
          "default": "ceremony-reception-stay"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid dates, package or quote selection",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": ["error"],
              "properties": { "error": { "type": "string" } }
            }
          }
        }
      }
    },
    "schemas": {
      "Availability": {
        "type": "object",
        "required": ["version", "data", "meta"],
        "properties": {
          "version": { "type": "string", "const": "2.0" },
          "data": {
            "type": "object",
            "required": ["startDate", "endDate", "days"],
            "properties": {
              "startDate": { "type": "string", "format": "date" },
              "endDate": { "type": "string", "format": "date" },
              "days": {
                "type": "array",
                "items": { "$ref": "#/components/schemas/Day" }
              }
            }
          },
          "meta": {
            "type": "object",
            "required": ["fresh", "lastVerifiedAt", "timezone"],
            "properties": {
              "fresh": { "type": "boolean" },
              "lastVerifiedAt": {
                "type": ["string", "null"],
                "format": "date-time"
              },
              "timezone": { "type": "string", "const": "America/Chicago" }
            }
          }
        }
      },
      "Day": {
        "type": "object",
        "required": [
          "date",
          "occupancy",
          "wedding",
          "tour",
          "arrivals",
          "departures"
        ],
        "properties": {
          "date": { "type": "string", "format": "date" },
          "occupancy": {
            "type": "string",
            "enum": [
              "unknown",
              "occupied",
              "turnover",
              "arrival",
              "departure",
              "free"
            ]
          },
          "wedding": {
            "type": "string",
            "enum": [
              "unavailable",
              "needs-confirmation",
              "all-packages",
              "without-stay"
            ]
          },
          "tour": {
            "type": "string",
            "enum": [
              "unavailable",
              "needs-confirmation",
              "guaranteed",
              "tentative"
            ]
          },
          "arrivals": { "type": "array", "items": { "type": "string" } },
          "departures": { "type": "array", "items": { "type": "string" } },
          "discountEligible": { "type": "boolean" }
        }
      },
      "Quote": {
        "type": "object",
        "required": [
          "version",
          "catalogVersion",
          "currency",
          "date",
          "guests",
          "status",
          "packages",
          "selectedPriceUsd",
          "totalPriceUsd",
          "asOf",
          "lastVerifiedAt"
        ],
        "properties": {
          "version": { "type": "string", "const": "1.0" },
          "catalogVersion": { "type": "string" },
          "currency": { "type": "string", "const": "USD" },
          "date": { "type": "string", "format": "date" },
          "guests": { "type": "integer" },
          "status": {
            "type": "string",
            "enum": ["quoted", "needs-confirmation", "unavailable"]
          },
          "packages": { "type": "array", "items": { "type": "object" } },
          "selectedPriceUsd": { "type": ["number", "null"] },
          "totalPriceUsd": { "type": ["number", "null"] },
          "deposit": { "type": "object" },
          "extraNights": { "type": "object" },
          "asOf": { "type": "string", "format": "date-time" },
          "lastVerifiedAt": {
            "type": ["string", "null"],
            "format": "date-time"
          }
        }
      }
    }
  }
}
