{"openapi":"3.1.0","info":{"title":"Public accommodation booking requests","version":"1.0.0"},"servers":[{"url":"/"}],"x-booking-policy":{"instant_confirmation":false,"workflow":"request -> host confirmation -> payment by agreed bank transfer","guest_oauth":"not_available","ucp_lodging":"implemented_draft","ucp_lodging_endpoint":"/api/ucp/booking-sessions","ucp_lodging_version":"2026-10-28","ucp_lodging_completion":"complete_in_progress; non-binding request requiring host confirmation","instant_booking_enabled":false},"paths":{"/api/accommodation/availability":{"get":{"x-itt-canonical-runtime-path":true,"operationId":"getPublicStayAvailability","summary":"Public busy and available date ranges","description":"Read tenant-bound availability. Dates in ranges are inclusive. Availability is not a reservation.","security":[],"parameters":[{"name":"roomTypeId","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Availability or room catalogue"},"404":{"description":"Room type not found"},"429":{"description":"Rate limited"},"503":{"description":"Tenant or availability unavailable"}}}},"/api/accommodation/price-quote":{"post":{"x-itt-canonical-runtime-path":true,"operationId":"quotePublicStay","summary":"Calculate a non-binding stay quote","description":"Requires the website CSRF session and a fresh single-use hashcash challenge for scope fewo:price-quote. Never infer a confirmed booking from a quote.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicStayQuote"}}}},"responses":{"200":{"description":"Calculated quote using canonical pricing"},"400":{"description":"Malformed or legacy field validation error"},"403":{"description":"CSRF rejected"},"404":{"description":"Unknown room"},"422":{"description":"Stay unavailable or form proof rejected"},"429":{"description":"Rate limited"},"503":{"description":"Pricing or proof service unavailable"}}}},"/api/accommodation/booking":{"post":{"x-itt-canonical-runtime-path":true,"operationId":"requestPublicStayBooking","summary":"Submit a pending booking request","description":"Requires explicit guest permission, website session, CSRF and the complete FormWrapper proof envelope. A valid guest session binds the booking email to that account in the exact storage tenant; anonymous booking remains available. Creates a pending request; the host confirms before payment. Never retry an uncertain submission automatically.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicBookingRequest"}}}},"responses":{"200":{"description":"Pending booking request received, not instant confirmation"},"201":{"description":"Pending booking request created, not instant confirmation"},"400":{"description":"Malformed request or legacy field/proof validation error"},"403":{"description":"CSRF rejected or guest session belongs to another tenant (customer_tenant_mismatch)"},"409":{"description":"Capacity changed"},"422":{"description":"Business rule, site mode or guest account email mismatch (customer_email_mismatch)"},"429":{"description":"Rate limited"},"503":{"description":"Booking service unavailable"}}}},"/api/accommodation/agent-openapi.json":{"get":{"x-itt-canonical-runtime-path":true,"operationId":"getPublicStayAgentContract","summary":"Public OpenAPI 3.1 booking contract","security":[],"responses":{"200":{"description":"Closed public booking specification"},"429":{"description":"Rate limited"}}}},"/api/accommodation/mcp":{"post":{"x-itt-canonical-runtime-path":true,"operationId":"callPublicStayMcp","summary":"Stateless MCP Streamable HTTP adapter for public booking forms","description":"Tools availability, price, booking_request. Identical REST middleware and proofs apply; no guest OAuth delegation is implemented. JSON-RPC errors use error.code; tool errors retain REST HTTP status in result._meta.httpStatus. No SSE sessions.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["jsonrpc","method"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"id":{"type":["string","integer"]},"method":{"type":"string"},"params":{"type":"object"}}}}}},"responses":{"200":{"description":"JSON-RPC response, inspect tool isError and _meta.httpStatus"},"202":{"description":"Notification accepted without tool execution"},"400":{"description":"Malformed JSON-RPC"},"429":{"description":"Rate limited"}}}},"/api/accommodation/calendars/{unit}.ics":{"get":{"x-itt-canonical-runtime-path":true,"operationId":"getPublicStayBusyCalendar","summary":"Anonymous busy periods for exactly two public units","security":[],"parameters":[{"name":"unit","in":"path","required":true,"schema":{"type":"string","enum":["2-persons","4-persons"]}}],"responses":{"200":{"description":"Busy periods only, all-day exclusive DTEND, no guest data","content":{"text/calendar":{"schema":{"type":"string"}}}},"404":{"description":"Calendar not published"},"429":{"description":"Rate limited"},"503":{"description":"Availability unavailable"}}}}},"components":{"schemas":{"PublicStayQuote":{"type":"object","required":["roomTypeId","startDate","endDate","guests","hashcash","hashcash_challenge"],"properties":{"roomTypeId":{"type":"string"},"startDate":{"type":"string","format":"date"},"endDate":{"type":"string","format":"date"},"guests":{"type":"integer","minimum":1},"hashcash":{"type":"string","description":"Single-use stamp"},"hashcash_challenge":{"type":"string","description":"Server-signed challenge"},"couponCode":{"type":"string"},"packageId":{"type":"string"}}},"PublicBookingRequest":{"allOf":[{"$ref":"#/components/schemas/PublicBookingFields"},{"type":"object","properties":{"bookingMode":{"type":"string","enum":["request"]}}}]},"PublicBookingFields":{"type":"object","required":["roomType","persons","checkIn","checkOut","firstName","lastName","email","guests","acceptNoSmoking","acceptTerms","form_token","client_session_id","hashcash_solutions"],"description":"Submit only with guest authorization. Security fields must come from the normal session-bound website challenge flow, never placeholders. Optional extras follow the existing booking form.","properties":{"roomType":{"type":"string"},"persons":{"type":"integer","minimum":1},"checkIn":{"type":"string","format":"date"},"checkOut":{"type":"string","format":"date"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","format":"email"},"guests":{"type":"integer","minimum":1},"acceptNoSmoking":{"type":"boolean","const":true},"acceptTerms":{"type":"boolean","const":true},"form_token":{"type":"string"},"client_session_id":{"type":"string"},"captcha_input":{"type":"string"},"hashcash_solutions":{"type":"array","items":{"type":"object"}},"hp":{"type":"string"},"hp_dynamic":{"type":"string"}}}}}}