openapi: 3.1.0

info:
  title: Pensacola Winterfest Ticket Availability API
  version: "1.0"
  description: |
	Read-only API exposing live ticket availability, pricing, and purchase URLs
	for Pensacola Winterfest trolley tours and photo sessions.

	**No authentication required.** All endpoints are public and CORS-open.

	**Cache TTL:** responses are fresh for 5 minutes. Do not cache longer than
	`Cache-Control: max-age=300` — seat counts change as reservations come in.

	**Time zone:** all `startDate`/`endDate` values are in America/Chicago
	(CST −06:00 / CDT −05:00). The UTC offset is embedded in each ISO 8601
	timestamp.

	**Purchase flow:** each `Offer` object contains a `url` field with a
	pre-filled purchase link. Direct users there to complete checkout.
  contact:
	name: Winterfest of Pensacola Inc.
	url: https://pensacolawinterfest.org/support
	email: info@pensacolawinterfest.org
  license:
	name: Proprietary — read-only access permitted for AI agent use

servers:
  - url: https://www.pensacolawinterfest.org
	description: Production

paths:

  /tickets/feed:
	get:
	  operationId: getMasterFeed
	  summary: All upcoming events with live availability
	  description: |
		Returns every future, for-sale schedule slot across all tours and photo
		sessions. Default response is a Schema.org `ItemList` of `Event` objects
		(JSON-LD). Use `?format=json` for a flat JSON array without the
		Schema.org wrapper.

		**Use this endpoint** to get the full schedule in one request.
		Refreshes every 5 minutes.
	  parameters:
		- name: format
		  in: query
		  required: false
		  description: |
			`jsonld` (default) — Schema.org ItemList wrapper, `Content-Type: application/ld+json`.
			`json` — flat array, `Content-Type: application/json`.
		  schema:
			type: string
			enum: [jsonld, json]
			default: jsonld
	  responses:
		"200":
		  description: Success
		  headers:
			Cache-Control:
			  schema:
				type: string
				example: public, max-age=300
			Access-Control-Allow-Origin:
			  schema:
				type: string
				example: "*"
		  content:
			application/ld+json:
			  schema:
				$ref: "#/components/schemas/ItemListJsonLd"
			application/json:
			  schema:
				$ref: "#/components/schemas/FlatFeed"

  /tickets/event/{schedule_id}:
	get:
	  operationId: getEvent
	  summary: Single schedule slot by ID
	  description: |
		Returns one `Event` object for the given schedule row. Useful for
		deep-linking, verification, or refreshing a single slot without
		fetching the full feed.

		**Content negotiation:** send `Accept: application/ld+json` or
		`Accept: application/json`, or append `?format=jsonld|json`.
		Without one of these signals, a human browser receives a redirect
		to the ticket purchase page instead.

		Responses are statically cached on disk after the first hit; the
		cache is invalidated automatically when inventory changes.
	  parameters:
		- name: schedule_id
		  in: path
		  required: true
		  description: Numeric schedule row ID, obtained from the `identifier` field of a feed Event.
		  schema:
			type: integer
			example: 48291
		- name: format
		  in: query
		  required: false
		  description: |
			Override Accept header. `jsonld` (default) or `json`.
		  schema:
			type: string
			enum: [jsonld, json]
			default: jsonld
	  responses:
		"200":
		  description: Event found
		  headers:
			Cache-Control:
			  schema:
				type: string
				example: public, max-age=300
			Access-Control-Allow-Origin:
			  schema:
				type: string
				example: "*"
		  content:
			application/ld+json:
			  schema:
				$ref: "#/components/schemas/EventJsonLd"
			application/json:
			  schema:
				$ref: "#/components/schemas/Event"
		"200-redirect":
		  description: |
			Human browser without JSON Accept header — redirects to purchase
			page. Not a true 200; documented here for clarity.
		"404":
		  description: schedule_id not found
		  content:
			application/json:
			  schema:
				$ref: "#/components/schemas/ErrorResponse"

  /tickets/snippet/{tour}/{date}:
	get:
	  operationId: getTourDateSnippet
	  summary: Per-tour-per-date Schema.org snippet (HTML embed)
	  description: |
		Returns a `<script type="application/ld+json">` block for a specific
		tour on a specific date, covering all time slots that day. Used
		internally to inject structured data into tour page HTML.

		Agents may use this endpoint to get date-level event data for a known
		tour/date pair without fetching the full feed.
	  parameters:
		- name: tour
		  in: path
		  required: true
		  description: Tour short name (e.g. `peanuts`, `grinch`, `visitsanta`). Matches `tourdata.tour` in the feed.
		  schema:
			type: string
			example: peanuts
		- name: date
		  in: path
		  required: true
		  description: Date in `YYYY-MM-DD` format.
		  schema:
			type: string
			format: date
			example: "2025-12-06"
	  responses:
		"200":
		  description: JSON-LD script tag
		  content:
			application/ld+json:
			  schema:
				$ref: "#/components/schemas/EventJsonLd"

components:
  schemas:

	Offer:
	  type: object
	  description: Schema.org Offer — one ticket type or group-size bucket.
	  properties:
		"@type":
		  type: string
		  enum: [Offer]
		name:
		  type: string
		  description: |
			`Adult` or `Child` for tours.
			`Small group (1–4 people)`, `Family group (5–10 people)`,
			or `Large group (11+ people)` for photo sessions.
		  example: Adult
		description:
		  type: string
		  description: Present on photo session offers only; describes what is included.
		  example: "Up to 4 people — one pose + candids"
		price:
		  type: number
		  format: float
		  description: Price in USD. Varies by night level and purchase timing (earlybird/regular/final).
		  example: 28.00
		priceCurrency:
		  type: string
		  enum: [USD]
		url:
		  type: string
		  format: uri
		  description: |
			Pre-filled purchase link. For tours, encodes adult/child/group counts
			and the specific date/time slot.
		  example: "https://www.pensacolawinterfest.org/tickets/tour/peanuts/1/0/0/2025-12-06/19:00:00"
		availability:
		  type: string
		  enum:
			- "https://schema.org/InStock"
			- "https://schema.org/SoldOut"
		validFrom:
		  type: string
		  format: date
		  description: Date tickets went on sale (typically July 1 of the event year).
		  example: "2025-07-01"
	  required: [price, priceCurrency, url, availability]

	PostalAddress:
	  type: object
	  properties:
		"@type":
		  type: string
		  enum: [PostalAddress]
		streetAddress:
		  type: string
		  example: 223 South Palafox Place
		addressLocality:
		  type: string
		  example: Pensacola
		postalCode:
		  type: string
		  example: "32502"
		addressRegion:
		  type: string
		  example: FL
		addressCountry:
		  type: string
		  example: US

	Place:
	  type: object
	  properties:
		"@type":
		  type: string
		  enum: [Place]
		name:
		  type: string
		  example: Plaza Wonderland
		address:
		  $ref: "#/components/schemas/PostalAddress"

	Organization:
	  type: object
	  properties:
		"@type":
		  type: string
		  enum: [Organization]
		name:
		  type: string
		  example: Winterfest of Pensacola Inc.
		url:
		  type: string
		  format: uri
		email:
		  type: string
		  format: email
		telephone:
		  type: string

	Event:
	  type: object
	  description: One schedule slot — a specific tour on a specific date at a specific time.
	  properties:
		"@type":
		  type: string
		  enum: [Event]
		"@id":
		  type: string
		  format: uri
		  description: Canonical URL for this schedule slot.
		  example: "https://www.pensacolawinterfest.org/tickets/event/48291"
		identifier:
		  type: string
		  description: Numeric schedule row ID as a string. Use this to call `/tickets/event/{id}`.
		  example: "48291"
		name:
		  type: string
		  description: Human-readable event name including date and time.
		  example: "A Charlie Brown Christmas Tour — December 6th 7:00pm"
		startDate:
		  type: string
		  format: date-time
		  description: ISO 8601 with America/Chicago UTC offset.
		  example: "2025-12-06T19:00:00-06:00"
		endDate:
		  type: string
		  format: date-time
		  description: startDate + 30 minutes (approximate tour/session duration).
		  example: "2025-12-06T19:30:00-06:00"
		duration:
		  type: string
		  description: ISO 8601 duration.
		  example: PT30M
		eventStatus:
		  type: string
		  enum: ["https://schema.org/EventScheduled"]
		eventAttendanceMode:
		  type: string
		  enum: ["https://schema.org/OfflineEventAttendanceMode"]
		location:
		  $ref: "#/components/schemas/Place"
		organizer:
		  $ref: "#/components/schemas/Organization"
		image:
		  type: array
		  items:
			type: string
			format: uri
		description:
		  type: string
		  description: Tour or session slugline.
		remainingAttendeeCapacity:
		  type: integer
		  description: Seats (or group slots) currently available. 0 means sold out.
		  example: 14
		offers:
		  type: array
		  description: |
			Tours have Adult + Child offers.
			Photo sessions have three group-size bucket offers.
		  items:
			$ref: "#/components/schemas/Offer"
	  required: [identifier, name, startDate, endDate, remainingAttendeeCapacity, offers]

	EventJsonLd:
	  description: Event object with Schema.org @context — returned by the single-event endpoint.
	  allOf:
		- $ref: "#/components/schemas/Event"
		- type: object
		  properties:
			"@context":
			  type: string
			  enum: ["https://schema.org"]

	ListItem:
	  type: object
	  properties:
		"@type":
		  type: string
		  enum: [ListItem]
		position:
		  type: integer
		  description: 1-based position in the list.
		item:
		  $ref: "#/components/schemas/Event"

	ItemListJsonLd:
	  type: object
	  description: Schema.org ItemList wrapping all upcoming Event objects. Default feed format.
	  properties:
		"@context":
		  type: string
		  enum: ["https://schema.org"]
		"@type":
		  type: string
		  enum: [ItemList]
		name:
		  type: string
		  example: "Pensacola Winterfest — Ticket Availability"
		description:
		  type: string
		url:
		  type: string
		  format: uri
		  example: "https://www.pensacolawinterfest.org/tickets"
		numberOfItems:
		  type: integer
		  description: Total event slots in this response.
		  example: 87
		itemListElement:
		  type: array
		  items:
			$ref: "#/components/schemas/ListItem"
	  required: [numberOfItems, itemListElement]

	FlatFeed:
	  type: object
	  description: Flat JSON feed — returned when `?format=json`.
	  properties:
		generated:
		  type: string
		  format: date-time
		  description: ISO 8601 timestamp when this response was generated.
		  example: "2025-11-15T08:30:00-06:00"
		event_count:
		  type: integer
		  example: 87
		events:
		  type: array
		  items:
			$ref: "#/components/schemas/Event"
	  required: [generated, event_count, events]

	ErrorResponse:
	  type: object
	  properties:
		error:
		  type: string
		  example: schedule not found
		id:
		  type: integer
		  example: 99999