{"openapi":"3.1.0","info":{"title":"Odybook Agent Discovery API","description":"Public read-only endpoints for discovering explicitly opted-in tour providers, retrieving publication-safe experiences, and checking current availability. Booking writes use the MCP endpoint with private Bearer tokens.","version":"1.0.0","contact":{"name":"Odybook Support","email":"support@odybook.com"}},"servers":[{"url":"https://odybook.com","description":"Production"}],"paths":{"/api/ai/providers":{"get":{"summary":"List eligible providers","description":"Use this first when an agent needs to discover tour providers that explicitly opted in and have synchronized, marketplace-eligible public inventory.","operationId":"list_agent_providers","parameters":[{"name":"q","in":"query","required":false,"description":"Optional provider, city, country, or text filter.","schema":{"type":"string","minLength":1,"maxLength":100},"example":"paris"},{"name":"limit","in":"query","required":false,"description":"Maximum number of providers to return.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"example":20}],"responses":{"200":{"description":"Eligible public providers","content":{"application/json":{"schema":{"type":"object","required":["success","providers","meta"],"properties":{"success":{"type":"boolean","const":true},"providers":{"type":"array","items":{"$ref":"#/components/schemas/AgentProvider"}},"meta":{"$ref":"#/components/schemas/ResponseMeta"}}}}}},"400":{"description":"Structured agent error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentErrorResponse"},"example":{"success":false,"error":{"code":"INVALID_QUERY","message":"Invalid query parameters.","hint":"Use /openapi.json or /skill.md to inspect required parameters.","documentation_url":"https://odybook.com/skill.md#error-handling","details":{"issues":[{"path":"date","message":"date must use YYYY-MM-DD"}]}}}}}},"429":{"description":"Public read rate limit exceeded","headers":{"RateLimit-Policy":{"description":"Applied public read policy, or an explicit not-enforced marker when Redis is unavailable.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Public read budget for this IP when Redis rate limiting is available.","schema":{"type":"integer","example":100}},"X-RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","example":99}},"X-RateLimit-Reset":{"description":"Unix timestamp when the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying the request.","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentErrorResponse"},"example":{"success":false,"error":{"code":"RATE_LIMITED","message":"Agent read request limit exceeded.","hint":"Retry after 60 seconds and reuse cached discovery responses where possible.","documentation_url":"https://odybook.com/skill.md#error-handling"}}}}},"500":{"description":"Structured agent error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentErrorResponse"},"example":{"success":false,"error":{"code":"INVALID_QUERY","message":"Invalid query parameters.","hint":"Use /openapi.json or /skill.md to inspect required parameters.","documentation_url":"https://odybook.com/skill.md#error-handling","details":{"issues":[{"path":"date","message":"date must use YYYY-MM-DD"}]}}}}}}}}},"/api/ai/experiences":{"get":{"summary":"List eligible experiences for one provider","description":"Use after provider discovery to retrieve publication-safe experiences, booking constraints, price model, and stable public URLs.","operationId":"list_agent_experiences","parameters":[{"name":"org","in":"query","required":true,"description":"Lowercase provider slug from GET /api/ai/providers.","schema":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"example":"tours-with-sonali"},{"name":"date","in":"query","required":false,"description":"Optional ISO calendar date. When present, the response includes current slots for each experience.","schema":{"type":"string","format":"date"},"example":"2026-10-18"},{"name":"limit","in":"query","required":false,"description":"Maximum number of results.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"example":20}],"responses":{"200":{"description":"Eligible public experiences","content":{"application/json":{"schema":{"type":"object","required":["success","experiences","meta"],"properties":{"success":{"type":"boolean","const":true},"experiences":{"type":"array","items":{"$ref":"#/components/schemas/AgentExperience"}},"meta":{"$ref":"#/components/schemas/ResponseMeta"}}}}}},"400":{"description":"Structured agent error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentErrorResponse"},"example":{"success":false,"error":{"code":"INVALID_QUERY","message":"Invalid query parameters.","hint":"Use /openapi.json or /skill.md to inspect required parameters.","documentation_url":"https://odybook.com/skill.md#error-handling","details":{"issues":[{"path":"date","message":"date must use YYYY-MM-DD"}]}}}}}},"404":{"description":"Structured agent error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentErrorResponse"},"example":{"success":false,"error":{"code":"INVALID_QUERY","message":"Invalid query parameters.","hint":"Use /openapi.json or /skill.md to inspect required parameters.","documentation_url":"https://odybook.com/skill.md#error-handling","details":{"issues":[{"path":"date","message":"date must use YYYY-MM-DD"}]}}}}}},"429":{"description":"Public read rate limit exceeded","headers":{"RateLimit-Policy":{"description":"Applied public read policy, or an explicit not-enforced marker when Redis is unavailable.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Public read budget for this IP when Redis rate limiting is available.","schema":{"type":"integer","example":100}},"X-RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","example":99}},"X-RateLimit-Reset":{"description":"Unix timestamp when the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying the request.","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentErrorResponse"},"example":{"success":false,"error":{"code":"RATE_LIMITED","message":"Agent read request limit exceeded.","hint":"Retry after 60 seconds and reuse cached discovery responses where possible.","documentation_url":"https://odybook.com/skill.md#error-handling"}}}}},"500":{"description":"Structured agent error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentErrorResponse"},"example":{"success":false,"error":{"code":"INVALID_QUERY","message":"Invalid query parameters.","hint":"Use /openapi.json or /skill.md to inspect required parameters.","documentation_url":"https://odybook.com/skill.md#error-handling","details":{"issues":[{"path":"date","message":"date must use YYYY-MM-DD"}]}}}}}}}}},"/api/ai/availability":{"get":{"summary":"Check current experience availability","description":"Use immediately before suggesting a bookable time. A tour is not bookable unless this endpoint returns available capacity for the requested date.","operationId":"check_agent_availability","parameters":[{"name":"org","in":"query","required":true,"description":"Lowercase provider slug from GET /api/ai/providers.","schema":{"type":"string","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"example":"tours-with-sonali"},{"name":"experience_id","in":"query","required":true,"description":"Experience id or public slug from the experiences response.","schema":{"type":"string","minLength":1,"maxLength":200},"example":"morning-food-walk"},{"name":"date","in":"query","required":true,"description":"Real ISO calendar date in YYYY-MM-DD format.","schema":{"type":"string","format":"date"},"example":"2026-10-18"}],"responses":{"200":{"description":"Current availability snapshot","headers":{"RateLimit-Policy":{"description":"Applied public read policy, or an explicit not-enforced marker when Redis is unavailable.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Public read budget for this IP when Redis rate limiting is available.","schema":{"type":"integer","example":100}},"X-RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","example":99}},"X-RateLimit-Reset":{"description":"Unix timestamp when the current window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailabilityResponse"}}}},"400":{"description":"Structured agent error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentErrorResponse"},"example":{"success":false,"error":{"code":"INVALID_QUERY","message":"Invalid query parameters.","hint":"Use /openapi.json or /skill.md to inspect required parameters.","documentation_url":"https://odybook.com/skill.md#error-handling","details":{"issues":[{"path":"date","message":"date must use YYYY-MM-DD"}]}}}}}},"404":{"description":"Structured agent error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentErrorResponse"},"example":{"success":false,"error":{"code":"INVALID_QUERY","message":"Invalid query parameters.","hint":"Use /openapi.json or /skill.md to inspect required parameters.","documentation_url":"https://odybook.com/skill.md#error-handling","details":{"issues":[{"path":"date","message":"date must use YYYY-MM-DD"}]}}}}}},"429":{"description":"Public read rate limit exceeded","headers":{"RateLimit-Policy":{"description":"Applied public read policy, or an explicit not-enforced marker when Redis is unavailable.","schema":{"type":"string"}},"X-RateLimit-Limit":{"description":"Public read budget for this IP when Redis rate limiting is available.","schema":{"type":"integer","example":100}},"X-RateLimit-Remaining":{"description":"Remaining requests in the current window.","schema":{"type":"integer","example":99}},"X-RateLimit-Reset":{"description":"Unix timestamp when the current window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying the request.","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentErrorResponse"},"example":{"success":false,"error":{"code":"RATE_LIMITED","message":"Agent read request limit exceeded.","hint":"Retry after 60 seconds and reuse cached discovery responses where possible.","documentation_url":"https://odybook.com/skill.md#error-handling"}}}}},"500":{"description":"Structured agent error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentErrorResponse"},"example":{"success":false,"error":{"code":"INVALID_QUERY","message":"Invalid query parameters.","hint":"Use /openapi.json or /skill.md to inspect required parameters.","documentation_url":"https://odybook.com/skill.md#error-handling","details":{"issues":[{"path":"date","message":"date must use YYYY-MM-DD"}]}}}}}}}}}},"components":{"schemas":{"AgentProvider":{"type":"object","required":["name","slug","experience_count","experiences_url"],"properties":{"name":{"type":"string"},"slug":{"type":"string"},"experience_count":{"type":"integer"},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"}},"languages":{"type":"array","items":{"type":"string"}},"currencies":{"type":"array","items":{"type":"string"}},"timezone":{"type":["string","null"]},"experiences_url":{"type":"string","format":"uri"}}},"AgentExperience":{"type":"object","required":["id","slug","title","provider","duration_minutes","price","url","booking_url","availability_url"],"properties":{"id":{"type":"string"},"slug":{"type":"string"},"title":{"type":"string"},"description":{"type":["string","null"]},"type":{"type":"string"},"provider":{"$ref":"#/components/schemas/ProviderRef"},"duration_minutes":{"type":"integer"},"price":{"$ref":"#/components/schemas/Price"},"location":{"$ref":"#/components/schemas/Location"},"timezone":{"type":["string","null"]},"languages":{"type":"array","items":{"type":"string"}},"accessibility":{"$ref":"#/components/schemas/Accessibility"},"highlights":{"type":"array","items":{"type":"string"}},"included":{"type":"array","items":{"type":"string"}},"excluded":{"type":"array","items":{"type":"string"}},"requirements":{"type":"array","items":{"type":"string"}},"cancellation":{"$ref":"#/components/schemas/Cancellation"},"booking_constraints":{"$ref":"#/components/schemas/BookingConstraints"},"min_group_size":{"type":"integer"},"max_group_size":{"type":"integer"},"image":{"type":["string","null"],"format":"uri"},"url":{"type":"string","format":"uri"},"booking_url":{"type":"string","format":"uri"},"availability_url":{"type":"string","format":"uri"},"available_slots":{"type":"array","items":{"type":"string"}},"available":{"type":"boolean"},"last_updated":{"type":["string","null"],"format":"date-time"}}},"AvailabilityResponse":{"type":"object","required":["success","experience","date","availability","booking_url"],"properties":{"success":{"type":"boolean","const":true},"experience":{"$ref":"#/components/schemas/AgentExperience"},"date":{"type":"string","format":"date"},"availability":{"type":"object","required":["available","remaining_spots","time_slots"],"properties":{"available":{"type":"boolean"},"remaining_spots":{"type":"integer","minimum":0},"time_slots":{"type":"array","items":{"type":"object","required":["time","available","remaining_spots","booking_url"],"properties":{"time":{"type":"string"},"available":{"type":"boolean"},"remaining_spots":{"type":"integer","minimum":0},"booking_url":{"type":"string","format":"uri"}}}}}},"booking_url":{"type":"string","format":"uri"}}},"AgentErrorResponse":{"type":"object","required":["success","error"],"properties":{"success":{"type":"boolean","const":false},"error":{"type":"object","required":["code","message","hint","documentation_url"],"properties":{"code":{"type":"string","enum":["INVALID_QUERY","PROVIDER_NOT_FOUND","EXPERIENCE_NOT_FOUND","RATE_LIMITED","INTERNAL_ERROR"]},"message":{"type":"string"},"hint":{"type":"string"},"documentation_url":{"type":"string","format":"uri"},"available_values":{"type":"array","items":{"type":"string"}},"details":{"type":"object"}}}}},"Location":{"type":"object","required":["city","country"],"properties":{"address":{"type":"string"},"city":{"type":"string"},"country":{"type":"string"},"lat":{"type":"number","minimum":-90,"maximum":90},"lng":{"type":"number","minimum":-180,"maximum":180}}},"Accessibility":{"type":["object","null"],"properties":{"fitness_level":{"type":["string","null"],"enum":["easy","moderate","challenging","strenuous",null]},"wheelchair_accessible":{"type":["boolean","null"]},"wheelchair_notes":{"type":["string","null"]},"stroller_friendly":{"type":["boolean","null"]},"hearing_accommodations":{"type":["boolean","null"]},"hearing_notes":{"type":["string","null"]},"visual_descriptions":{"type":["boolean","null"]},"max_walking_distance_km":{"type":["number","null"],"minimum":0},"terrain_types":{"type":["array","null"],"items":{"type":"string","enum":["flat","stairs","cobblestone","unpaved","hills"]}},"additional_notes":{"type":["string","null"]}}},"Cancellation":{"type":"object","required":["policy","non_refundable_terms"],"properties":{"policy":{"type":["string","null"]},"non_refundable_terms":{"type":["string","null"]}}},"BookingConstraints":{"type":"object","required":["min_group_size","max_group_size","booking_cutoff_hours","departure_times","confirmation_method","payment_mode","deposit_amount","deposit_percentage","balance_due_days_before"],"properties":{"min_group_size":{"type":"integer","minimum":1},"max_group_size":{"type":"integer","minimum":1},"booking_cutoff_hours":{"type":"integer","minimum":0},"departure_times":{"type":"array","items":{"type":"string"}},"confirmation_method":{"type":"string","enum":["auto","manual"]},"payment_mode":{"type":"string","enum":["pay_now","deposit_now_balance_later"]},"deposit_amount":{"type":"number","minimum":0},"deposit_percentage":{"type":"number","minimum":0,"maximum":100},"balance_due_days_before":{"type":"integer","minimum":0}}},"Price":{"type":"object","required":["amount","starting_amount","currency","type"],"properties":{"amount":{"type":"number"},"starting_amount":{"type":"number"},"currency":{"type":"string"},"type":{"type":"string"},"categories":{"type":["array","null"]}}},"ProviderRef":{"type":"object","required":["name","slug"],"properties":{"name":{"type":"string"},"slug":{"type":"string"}}},"ResponseMeta":{"type":"object","required":["count","timestamp"],"properties":{"count":{"type":"integer"},"organization":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}}}}}}