{"openapi":"3.1.0","info":{"title":"Crystal Charters — Agent API","version":"1.0.0","description":"Public, read-mostly endpoints for AI agents and integrations: catalogue, pricing, availability, and a PII-safe draft-booking handoff. Agents deliver a guest to the booking flow; the human always confirms the slot and pays. All values are re-validated server-side on booking submit; nothing here completes a payment.","contact":{"email":"bookings@crystalcharters.com"}},"servers":[{"url":"https://crystalcharters.com","description":"Current environment"}],"paths":{"/llms/catalog":{"get":{"operationId":"getCatalog","summary":"Fleet, activities, pickups, and priced tours for a country.","description":"The structured-data mirror of /llms.txt — ingest once instead of crawling pages. PII-free.","parameters":[{"name":"country","in":"query","required":false,"schema":{"type":"string","default":"cayman-islands"}}],"responses":{"200":{"description":"Catalogue snapshot.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Catalog"}}}},"500":{"$ref":"#/components/responses/Error"}}}},"/llms/quote":{"get":{"operationId":"getTourQuote","summary":"Starting-from price for a popular tour.","description":"Returns the exact 'from' price shown on the tour page and its TouristTrip/Offer JSON-LD.\n\nPATH-FORM ALTERNATIVE for fetch tools that strip query strings: the same parameters can be sent as key=value path segments in any order, e.g. /llms/quote/tour=classic-stingray/country=cayman-islands — identical validation and response.","parameters":[{"name":"tour","in":"query","required":true,"schema":{"type":"string","example":"stingray-combo"}},{"name":"country","in":"query","required":false,"schema":{"type":"string","default":"cayman-islands"}}],"responses":{"200":{"description":"Quote.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Quote"}}}},"400":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"422":{"$ref":"#/components/responses/Error"}}}},"/api/availability/boats":{"get":{"operationId":"getAvailableBoats","summary":"Boats available in a date window.","parameters":[{"name":"startDate","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"endDate","in":"query","required":true,"schema":{"type":"string","format":"date"}},{"name":"countryId","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Per-date boat availability."},"400":{"$ref":"#/components/responses/Error"},"502":{"$ref":"#/components/responses/Error"}}}},"/api/availability/time-slots":{"post":{"operationId":"getAvailableTimeSlots","summary":"Bookable start times for a boat/date/trip type.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["countryId","startDate","endDate","boats"],"properties":{"countryId":{"type":"string"},"startDate":{"type":"string","format":"date"},"endDate":{"type":"string","format":"date"},"tripType":{"type":"string","enum":["STANDARD","BIO_BAY","DSF","BIO_BAY_DSF"]},"duration":{"type":"number"},"boats":{"type":"array","items":{"type":"object","properties":{"boatId":{"type":"string"}}}}}}}}},"responses":{"200":{"description":"Available slots by date."},"502":{"$ref":"#/components/responses/Error"}}}},"/llms/trip-preview":{"get":{"operationId":"getTripPreview","summary":"Fully-shaped trip preview in one call — boat, meeting point, exact total price.","description":"The recommended one-shot endpoint for constructing a full trip preview. Returns the recommended boat (with reason), recommended meeting point (with reason — Yacht Club for west / central, Kaibo for east / north), exact total price with breakdown (charter + activity add-ons + pickup fee), a preferred start-time hint, and the canonical / booking URLs. Composed from cached catalog + pure pricing math + the recommendation helper. No live availability call from this endpoint; agents needing exact time slots POST the embedded `timeSlotsBody` to `/api/availability/time-slots`. PII-free.\n\nPATH-FORM ALTERNATIVE for fetch tools that strip query strings: the same parameters can be sent as key=value path segments in any order, e.g. /llms/trip-preview/date=2026-11-10/adults=2/children=0/activity=stingray-city,snorkeling/country=cayman-islands — identical validation and response.","parameters":[{"name":"date","in":"query","required":true,"schema":{"type":"string","format":"date","example":"2026-11-10"}},{"name":"adults","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":32,"default":2}},{"name":"children","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":32,"default":0}},{"name":"tour","in":"query","required":false,"description":"Popular tour slug. Mutually exclusive with `activity`.","schema":{"type":"string","example":"biolum-bay"}},{"name":"activity","in":"query","required":false,"description":"Comma-separated activity slugs for a custom trip. Mutually exclusive with `tour`.","schema":{"type":"string","example":"stingray-city,snorkeling"}},{"name":"boat","in":"query","required":false,"description":"Specific boat preference (urlTitle). Honoured when the boat seats the party and supports every selected activity.","schema":{"type":"string"}},{"name":"pickup","in":"query","required":false,"description":"Specific pickup preference (urlTitle or known name alias). Overrides `guestArea`.","schema":{"type":"string"}},{"name":"guestArea","in":"query","required":false,"description":"Where the guest is staying. Drives pickup recommendation: west / central → Cayman Islands Yacht Club; east / north → Kaibo.","schema":{"type":"string","enum":["west","central","east","north"],"default":"central"}},{"name":"fishingHours","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":12}},{"name":"visitor","in":"query","required":false,"schema":{"type":"string","enum":["cruise","flight","resident"]}},{"name":"country","in":"query","required":false,"schema":{"type":"string","default":"cayman-islands"}}],"responses":{"200":{"description":"Trip preview.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripPreview"}}}},"400":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"422":{"$ref":"#/components/responses/Error"},"500":{"$ref":"#/components/responses/Error"}}}},"/api/agent/draft":{"post":{"operationId":"createAgentDraft","summary":"PII-safe draft-booking handoff.","description":"POST trip intent + guest details, receive a short-lived one-time token. Hand the guest resumeUrl (/book/resume/{token}); they land in the booking flow pre-filled and only confirm the slot and pay. The resume URL accepts an optional `?slot=HH:MM` query param which is forwarded through to pre-select that time slot. No PII in any URL. Trip fields use the same slug vocabulary as the trip-builder deep links.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentDraftRequest"}}}},"responses":{"201":{"description":"Draft created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentDraftResponse"}}}},"400":{"$ref":"#/components/responses/Error"},"502":{"$ref":"#/components/responses/Error"}}}},"/llms/fleet":{"get":{"operationId":"getFleetProfile","summary":"Per-boat fleet profile.","description":"Type, capacity, pricing tiers, supported activities, pickups with per-boat fees, plus operator-authored ride stability, best-for-groups, weather notes, agent summary.","parameters":[{"name":"country","in":"query","required":false,"schema":{"type":"string","default":"cayman-islands"}},{"name":"format","in":"query","required":false,"description":"Response projection: 'json' or 'md'. Default is markdown served as text/plain (browse-tool friendly); Accept: application/json also selects JSON.","schema":{"type":"string","enum":["json","md","markdown"]}}],"responses":{"200":{"description":"Per-boat fleet profile.","content":{"text/plain":{"schema":{"type":"string","description":"Markdown document (default projection)."}},"application/json":{"schema":{"type":"object"}}}}}}},"/llms/activities":{"get":{"operationId":"getActivityProfiles","summary":"Per-activity profile doc.","description":"Description, duration, pricing, boats offering it, common pairings, best-time-to-visit, kids-friendly note, water conditions, agent summary.","parameters":[{"name":"country","in":"query","required":false,"schema":{"type":"string","default":"cayman-islands"}},{"name":"format","in":"query","required":false,"description":"Response projection: 'json' or 'md'. Default is markdown served as text/plain (browse-tool friendly); Accept: application/json also selects JSON.","schema":{"type":"string","enum":["json","md","markdown"]}}],"responses":{"200":{"description":"Per-activity profile doc.","content":{"text/plain":{"schema":{"type":"string","description":"Markdown document (default projection)."}},"application/json":{"schema":{"type":"object"}}}}}}},"/llms/pickups":{"get":{"operationId":"getPickupProfiles","summary":"Per-pickup profile doc.","description":"Fee, highlights, best-for-accommodation-areas, address, coordinates, instructions.","parameters":[{"name":"country","in":"query","required":false,"schema":{"type":"string","default":"cayman-islands"}},{"name":"format","in":"query","required":false,"description":"Response projection: 'json' or 'md'. Default is markdown served as text/plain (browse-tool friendly); Accept: application/json also selects JSON.","schema":{"type":"string","enum":["json","md","markdown"]}}],"responses":{"200":{"description":"Per-pickup profile doc.","content":{"text/plain":{"schema":{"type":"string","description":"Markdown document (default projection)."}},"application/json":{"schema":{"type":"object"}}}}}}},"/llms/build-a-trip":{"get":{"operationId":"getBuildATripGuide","summary":"Instructional doc for building a custom trip.","description":"How to call /llms/trip-preview, the duration-calculation rule, pricing rules, cruise-passenger advisory, suggested add-ons, season notes, per-activity durations.","parameters":[{"name":"country","in":"query","required":false,"schema":{"type":"string","default":"cayman-islands"}},{"name":"format","in":"query","required":false,"description":"Response projection: 'json' or 'md'. Default is markdown served as text/plain (browse-tool friendly); Accept: application/json also selects JSON.","schema":{"type":"string","enum":["json","md","markdown"]}}],"responses":{"200":{"description":"Instructional doc for building a custom trip.","content":{"text/plain":{"schema":{"type":"string","description":"Markdown document (default projection)."}},"application/json":{"schema":{"type":"object"}}}}}}},"/llms/echo":{"get":{"operationId":"echoTransport","summary":"Transport diagnostic — echoes what the server received.","description":"Always 200 text/plain. Reports the request path, every query param received, Accept header, and User-Agent. If params you sent are missing, your fetch pipeline strips query strings — use the path-segment form.","responses":{"200":{"description":"Echo report.","content":{"text/plain":{"schema":{"type":"string","description":"Markdown document (default projection)."}},"application/json":{"schema":{"type":"object"}}}}}}}},"components":{"responses":{"Error":{"description":"Error.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{"type":"string"}}}}}}},"schemas":{"Quote":{"type":"object","properties":{"tour":{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":"string"},"activities":{"type":"array","items":{"type":"string"}},"durationHours":{"type":"number"},"guests":{"type":"object","properties":{"min":{"type":"number"},"max":{"type":"number"}}}}},"quote":{"type":"object","properties":{"startingPriceUSD":{"type":"number"},"currency":{"type":"string","const":"USD"},"hours":{"type":"number"}}},"canonicalUrl":{"type":"string","format":"uri"},"bookingUrl":{"type":"string","format":"uri"}}},"Catalog":{"type":"object","properties":{"country":{"type":"string"},"currency":{"type":"string"},"pricingModel":{"type":"string"},"booking":{"type":"object"},"fleet":{"type":"array","items":{"$ref":"#/components/schemas/FleetItem"}},"activities":{"type":"array","items":{"type":"object"}},"pickups":{"type":"array","items":{"type":"object"}},"tours":{"type":"array","items":{"type":"object"}}}},"FleetItem":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"capacity":{"type":"number"},"minHours":{"type":["number","null"]},"maxHours":{"type":"number"},"pricePerHourUSD":{"type":"number"},"activities":{"type":"array","items":{"type":"string"}},"detailUrl":{"type":"string","format":"uri"},"bookingUrl":{"type":"string","format":"uri"}}},"AgentDraftRequest":{"type":"object","required":["trip","guest"],"properties":{"country":{"type":"string","default":"cayman-islands"},"source":{"type":"string","description":"Attribution tag, e.g. ai-claude.","maxLength":60},"trip":{"type":"object","properties":{"startDate":{"type":"string","format":"date"},"endDate":{"type":"string","format":"date"},"adults":{"type":"integer","minimum":1,"maximum":32},"children":{"type":"integer","minimum":0,"maximum":32},"activitySlugs":{"type":"array","items":{"type":"string"},"maxItems":8},"boat":{"type":"string"},"pickup":{"type":"string"},"fishingHours":{"type":"integer","minimum":1,"maximum":12},"visitor":{"type":"string","enum":["cruise","flight","resident"]}}},"guest":{"type":"object","properties":{"firstName":{"type":"string","maxLength":100},"lastName":{"type":"string","maxLength":100},"email":{"type":"string","format":"email","maxLength":254},"phone":{"type":"string","maxLength":32},"notes":{"type":"string","maxLength":1000}}}}},"AgentDraftResponse":{"type":"object","properties":{"token":{"type":"string"},"resumeUrl":{"type":"string","format":"uri"},"expiresAt":{"type":"string","format":"date-time"}}},"TripPreview":{"type":"object","properties":{"tripType":{"type":"string","enum":["popular-tour","custom"]},"tourSlug":{"type":["string","null"]},"tourTitle":{"type":["string","null"]},"country":{"type":"string"},"date":{"type":"string","format":"date"},"party":{"type":"object","properties":{"adults":{"type":"integer"},"children":{"type":"integer"},"total":{"type":"integer"}}},"activities":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"},"priceUSD":{"type":["number","null"]},"priceIncluded":{"type":"boolean"},"durationMinutes":{"type":"number"}}}},"boatOptions":{"type":"array","description":"Up to 3 ranked boat options. boatOptions[0] is the primary recommendation (best-value); 1 and 2 are diverse alternatives (best-fit + most-stable). Each option carries its own pricing and bookingUrl so the agent can present any choice end-to-end.","maxItems":3,"items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"urlTitle":{"type":["string","null"]},"capacity":{"type":"integer"},"imageUrl":{"type":["string","null"]},"supportedActivitySlugs":{"type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"},"description":"Derived: one of [small, medium, large] for size; one each of [fishing, bio-bay, jet-ski, tubing, sunset] for capabilities the boat supports; plus optional [family-friendly] (capacity ≥ 8 + snorkeling + stingray-city) and [luxury] (bundles both tubing and jet-ski for free via activityAddons)."},"selectionSlot":{"type":"string","enum":["best-value","best-fit","most-stable"]},"selectionReason":{"type":"string","description":"One-sentence narrative the agent can quote to the guest for THIS option."},"pricing":{"type":"object","properties":{"currency":{"type":"string","enum":["USD"]},"charter":{"type":"object","properties":{"hours":{"type":"number"},"totalUSD":{"type":"number"}}},"activityAddOnsUSD":{"type":"number"},"pickupFeeUSD":{"type":"number"},"totalUSD":{"type":"number","description":"Exact total for THIS boat option, USD."},"explanation":{"type":"string"}}},"bookingUrl":{"type":"string","format":"uri","description":"Wizard deep link with THIS boat pre-selected."}}}},"meetingPoint":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"address":{"type":["string","null"]},"feeUSD":{"type":["number","null"]},"feeIncluded":{"type":"boolean"},"recommendationReason":{"type":"string","description":"Why this dock was chosen (geographic match or explicit override). Shared across all boatOptions."}}},"availability":{"type":"object","properties":{"isLikelyAvailable":{"type":["boolean","null"]},"reason":{"type":"string"},"timeSlotsEndpoint":{"type":"string","format":"uri","description":"POST this endpoint with `timeSlotsBody` below to get exact available slots. The body is pinned to boatOptions[0]; swap `boatId` to query slots for a different option."},"timeSlotsBody":{"type":"object","description":"Pre-constructed body for the time-slots POST. Includes boatId (slot-1), countryId, date, activities."},"preferredStartTime":{"type":["string","null"],"description":"Heuristic start-time suggestion (e.g. 17:30 for Bio Bay)."},"preferredStartReason":{"type":["string","null"]}}},"canonicalUrl":{"type":"string","format":"uri"},"agentDraftEndpoint":{"type":"string","format":"uri","description":"POST here with the guest's PII to receive a one-time resume token (preferred over putting PII in any bookingUrl)."},"warnings":{"type":"array","items":{"type":"string"},"description":"Soft warnings — unresolved activity slugs, preferred boat fallbacks, etc. Surfaced for agent telemetry, never user-facing."}}}}}}