{"openapi":"3.0.3","info":{"title":"Goyondo API","description":"API for trip planning, itineraries, and bookings. Use with Bearer token (Clerk session or API key gyd_*).","version":"1.0.0"},"servers":[{"url":"https://goyondo.run"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Clerk session token or Goyondo API key (prefix gyd_)"}},"schemas":{"Error":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"object","properties":{"code":{"type":"string","example":"validation_failed"},"message":{"type":"string"},"details":{"type":"array","items":{}}}}}},"CapabilityDefinition":{"type":"object","properties":{"name":{"type":"string","enum":["describe_capabilities","get_schema","list_trips","get_trip","create_trip","update_trip","regenerate_itinerary","add_activity","update_activity","list_activities","delete_activity","list_bookings","get_booking","create_booking","check_availability","get_user_preferences","list_itinerary_segments","get_trip_document","get_trip_expense_summary","ingest_confirmation","submit_group_preferences","submit_group_inspiration","get_group_board","react_to_place","update_place_candidate","update_user_preferences","create_group_trip","list_group_trips","delete_group_trip","join_group_trip","get_group_trip","run_alignment","get_alignment","list_proposals","approve_proposal","reject_proposal","get_edit_log","set_user_preference","get_preference_profile","update_day_notes"]},"description":{"type":"string"},"auth_required":{"type":"boolean","example":true},"implementation_status":{"type":"string","enum":["implemented"],"description":"Implementation state of the published capability."},"exposed_via":{"type":"array","items":{"type":"string","enum":["task","mcp"]}},"input_schema":{"type":"object","description":"JSON Schema describing capability arguments."}}},"AgentCapabilitiesResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true},"capabilities":{"type":"array","items":{"$ref":"#/components/schemas/CapabilityDefinition"}}}},"AgentTaskRequest":{"type":"object","required":["capability"],"properties":{"capability":{"type":"string","enum":["describe_capabilities","get_schema","list_trips","get_trip","create_trip","update_trip","regenerate_itinerary","add_activity","update_activity","list_activities","delete_activity","list_bookings","get_booking","create_booking","check_availability","get_user_preferences","list_itinerary_segments","get_trip_document","get_trip_expense_summary","ingest_confirmation","submit_group_preferences","submit_group_inspiration","get_group_board","react_to_place","update_place_candidate","update_user_preferences","create_group_trip","list_group_trips","delete_group_trip","join_group_trip","get_group_trip","run_alignment","get_alignment","list_proposals","approve_proposal","reject_proposal","get_edit_log","set_user_preference","get_preference_profile","update_day_notes"],"description":"Capability name from GET /api/agent/capabilities"},"arguments":{"type":"object","description":"Arguments for the selected capability. See capability input_schema for exact fields."}}},"AgentTaskResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true},"result":{"type":"object","description":"Capability-specific response payload."}}},"DeviceAuthRequest":{"type":"object","required":["agent_name","requested_scopes"],"properties":{"agent_name":{"type":"string","example":"My Travel Agent"},"requested_scopes":{"type":"array","items":{"type":"string","enum":["trips:read","trips:write","bookings:read","bookings:write","preferences:read","group_trips:read","group_trips:write"]},"example":["trips:read","trips:write"]}}},"DeviceAuthResponse":{"type":"object","properties":{"user_code":{"type":"string","example":"GYD-A3F9"},"verification_url":{"type":"string","format":"uri","example":"https://goyondo.run/connect"},"verification_uri_complete":{"type":"string","format":"uri","example":"https://goyondo.run/connect?code=GYD-A3F9"},"device_code":{"type":"string","example":"<opaque 64-char hex>"},"expires_in":{"type":"integer","example":600},"interval":{"type":"integer","example":5}}},"DeviceTokenRequest":{"type":"object","required":["device_code"],"properties":{"device_code":{"type":"string","example":"<opaque 64-char hex>"}}},"DeviceTokenResponse":{"type":"object","properties":{"access_token":{"type":"string","example":"gyd_..."},"token_type":{"type":"string","example":"bearer"},"scope":{"type":"string","example":"trips:read trips:write"}}},"DeviceTokenError":{"type":"object","properties":{"error":{"type":"string","example":"authorization_pending"},"error_description":{"type":"string","example":"Waiting for user approval"}}},"TripCreate":{"type":"object","required":["destination","start_date","end_date"],"properties":{"destination":{"type":"string"},"start_date":{"type":"string","format":"date"},"end_date":{"type":"string","format":"date"},"timezone":{"type":"string","description":"Traveler IANA time zone (e.g. America/Los_Angeles). Used to decide whether start_date is in the past. Not stored."},"travelers":{"type":"integer","default":1},"interests":{"type":"array","items":{"type":"string"}},"travel_style":{"type":"string","enum":["budget","comfort","luxury","adventure"]},"accommodation_type":{"type":"string","enum":["hotel","hostel","airbnb","resort","camping","bb"]},"skip_ai_generation":{"type":"boolean","default":false},"groupTripCode":{"type":"string","description":"Collaborative group trip code. When present, the server loads board consensus and readiness context for itinerary generation."},"regeneration_mode":{"type":"string","enum":["replace","new_version"],"description":"Required when groupTripCode already has a linked itinerary. replace updates that itinerary; new_version creates and links a fresh trip."}}},"BookingCreate":{"type":"object","required":["destination","dates","guests","preferences"],"properties":{"destination":{},"dates":{},"guests":{},"preferences":{},"notes":{"type":"string"},"specialRequests":{}}}}},"paths":{"/api/agent-manifest":{"get":{"summary":"Agent manifest","description":"Machine-readable description of capabilities and how to use the API","security":[],"responses":{"200":{"description":"Agent manifest JSON"}}}},"/api/agent/capabilities":{"get":{"summary":"List executable agent capabilities","description":"Canonical registry of task/MCP capabilities, their implementation status, and JSON argument schemas.","security":[],"responses":{"200":{"description":"Capability registry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentCapabilitiesResponse"},"example":{"success":true,"capabilities":[{"name":"describe_capabilities","description":"Describe available Goyondo agent capabilities, including input schemas, enum values, and supported interfaces. Use this for introspection before calling other capabilities.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"capability":{"type":"string","description":"Optional capability name to describe. Omit to list every capability."},"name":{"type":"string","description":"Alias for capability."}},"required":[]}},{"name":"get_schema","description":"Return the JSON input schema for one Goyondo capability, or all schemas if no capability is provided.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"capability":{"type":"string","description":"Optional capability name to fetch schema for."},"name":{"type":"string","description":"Alias for capability."}},"required":[]}},{"name":"list_trips","description":"List trips for the authenticated user. Returns trip id, destination, dates, status. Supports limit; response includes has_more to indicate whether more trips exist.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"limit":{"type":"integer","description":"Maximum number of trips to return. If has_more is true in the response, there are more trips beyond this page."}},"required":[]}},{"name":"get_trip","description":"Get a single trip by id including full itinerary (days and activities).","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_id":{"type":"string","description":"UUID of the trip"}},"required":["trip_id"]}},{"name":"create_trip","description":"Create a new trip and optionally generate an AI itinerary. Returns the created trip and itinerary.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"destination":{"type":"string"},"start_date":{"type":"string","description":"ISO date YYYY-MM-DD"},"end_date":{"type":"string","description":"ISO date YYYY-MM-DD"},"timezone":{"type":"string","description":"Traveler IANA time zone, e.g. America/Los_Angeles. Used to validate that start_date is not in the past."},"travelers":{"type":"integer","default":1},"interests":{"type":"array","items":{"type":"string"}},"travel_style":{"type":"string","enum":["budget","comfort","luxury","adventure"]},"accommodation_type":{"type":"string","enum":["hotel","hostel","airbnb","resort","camping","bb"]},"skip_ai_generation":{"type":"boolean","default":false},"groupTripCode":{"type":"string","description":"Collaborative group trip code. Server loads readiness and board context."},"regeneration_mode":{"type":"string","enum":["replace","new_version"],"description":"Required when the group already has a linked itinerary."}},"required":["destination","start_date","end_date"]}},{"name":"update_trip","description":"Update trip metadata such as dates, title, traveler count, budget, or status. When start_date or end_date changes, the response includes itinerary_dates showing the first and last day dates after the shift — verify these match your intended dates before proceeding.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_id":{"type":"string","description":"UUID of the trip"},"title":{"type":"string","description":"Trip title, max 255 chars"},"destination":{"type":"string","description":"Destination name"},"start_date":{"type":"string","description":"Start date in ISO format YYYY-MM-DD"},"end_date":{"type":"string","description":"End date in ISO format YYYY-MM-DD. May be sent without start_date. If both provided, end_date must be on or after start_date."},"travelers":{"type":"integer","description":"Number of travelers (1–20)"},"budget_per_person":{"type":"number","description":"Budget per person"},"total_budget":{"type":"number","description":"Total budget"},"currency":{"type":"string","description":"3-letter currency code, e.g. USD"},"travel_style":{"type":"string","enum":["budget","comfort","luxury","adventure"]},"accommodation_type":{"type":"string","enum":["hotel","hostel","airbnb","resort","camping","bb"]},"status":{"type":"string","enum":["draft","confirmed","active","completed","cancelled"]},"notes":{"type":"string","description":"Free-text notes, max 2000 chars"}},"required":["trip_id"]}},{"name":"regenerate_itinerary","description":"Regenerate the AI itinerary for an existing trip.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_id":{"type":"string","description":"UUID of the trip"}},"required":["trip_id"]}},{"name":"add_activity","description":"Add an activity to a trip. To assign to a specific day use itinerary_id (UUID from get_trip itinerary days — most reliable), date (YYYY-MM-DD), or day_number (1-based). If none are provided the activity lands on Day 1.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_id":{"type":"string","description":"UUID of the trip (from list_trips or get_trip)"},"title":{"type":"string","description":"Activity name, max 255 chars"},"description":{"type":"string","description":"Optional details, max 1000 chars"},"activity_type":{"type":"string","enum":["general","sightseeing","dining","transport","accommodation","entertainment","shopping","outdoor","cultural","business"],"description":"Canonical enum. Common aliases are accepted and normalized, e.g. event -> entertainment, food/dinner -> dining, meeting/conference -> business. Default: general"},"start_time":{"type":"string","description":"Time in HH:MM 24-hour format. HH:MM:SS and ISO-8601 date-time strings are accepted and normalized to HH:MM."},"end_time":{"type":"string","description":"Time in HH:MM 24-hour format. HH:MM:SS and ISO-8601 date-time strings are accepted and normalized to HH:MM."},"duration_minutes":{"type":"integer","description":"Duration in minutes"},"location":{"type":"string","description":"Venue or area name, max 255 chars"},"address":{"type":"string","description":"Full address, max 500 chars"},"estimated_cost":{"type":"number","description":"Cost per person in trip currency"},"priority":{"type":"integer","description":"1 (lowest) to 10 (highest), default 5"},"notes":{"type":"string","description":"Internal notes, max 1000 chars"},"itinerary_id":{"type":"string","description":"UUID of the itinerary day (from get_trip itinerary[].id). Most reliable way to target a specific day."},"day_number":{"type":"integer","description":"Day of the trip (1 = first day). Use itinerary_id or date instead when available."},"date":{"type":"string","description":"Calendar date YYYY-MM-DD. Use itinerary_id instead when available."}},"required":["trip_id","title"]}},{"name":"update_activity","description":"Update an existing activity. All fields except trip_id and activity_id are optional.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_id":{"type":"string","description":"UUID of the trip"},"activity_id":{"type":"string","description":"UUID of the activity (from get_trip itinerary)"},"title":{"type":"string"},"description":{"type":"string"},"activity_type":{"type":"string","enum":["general","sightseeing","dining","transport","accommodation","entertainment","shopping","outdoor","cultural","business"],"description":"Canonical enum. Common aliases are accepted and normalized, e.g. event -> entertainment, food/dinner -> dining, meeting/conference -> business."},"start_time":{"type":"string","description":"HH:MM 24-hour format. HH:MM:SS and ISO-8601 date-time strings are accepted and normalized to HH:MM."},"end_time":{"type":"string","description":"HH:MM 24-hour format. HH:MM:SS and ISO-8601 date-time strings are accepted and normalized to HH:MM."},"duration_minutes":{"type":"integer"},"location":{"type":"string"},"address":{"type":"string"},"estimated_cost":{"type":"number"},"priority":{"type":"integer"},"notes":{"type":"string"},"day_number":{"type":"integer","description":"Move the activity to a different trip day (1 = first day)."},"date":{"type":"string","description":"Move the activity to a specific calendar date YYYY-MM-DD."}},"required":["trip_id","activity_id"]}},{"name":"list_activities","description":"List all activities for a trip as a flat array.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_id":{"type":"string","description":"UUID of the trip"}},"required":["trip_id"]}},{"name":"delete_activity","description":"Delete an activity from a trip.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_id":{"type":"string"},"activity_id":{"type":"string"}},"required":["trip_id","activity_id"]}},{"name":"list_bookings","description":"List bookings for the authenticated user.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{},"required":[]}},{"name":"get_booking","description":"Get a single booking by id.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"booking_id":{"type":"string"}},"required":["booking_id"]}},{"name":"create_booking","description":"Record a booking the user made elsewhere (flight, hotel, activity) so it stays with the trip. Goyondo does not place bookings or take payment.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"destination":{"type":"object"},"dates":{"type":"object"},"guests":{"type":"object"},"preferences":{"type":"object"}},"required":["destination","dates","guests","preferences"]}},{"name":"check_availability","description":"Check the user's existing Goyondo booking records for a destination and dates. This is not live supplier availability or pricing.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"destination":{},"dates":{},"guests":{}},"required":[]}},{"name":"get_user_preferences","description":"Get the authenticated user's travel preferences (interests, dietary, accessibility, etc.).","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{},"required":[]}},{"name":"list_itinerary_segments","description":"List business traveler segments for a trip (flights, hotels, car rentals, meetings).","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_id":{"type":"string","description":"UUID of the trip"}},"required":["trip_id"]}},{"name":"get_trip_document","description":"Get a trip document by id or search by name (e.g. boarding pass for flight SQ321).","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_id":{"type":"string","description":"UUID of the trip"},"document_id":{"type":"string","description":"UUID of the document (optional if name provided)"},"name":{"type":"string","description":"Search by name substring (optional)"}},"required":["trip_id"]}},{"name":"get_trip_expense_summary","description":"Get expense summary for a trip (for expense report export).","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_id":{"type":"string","description":"UUID of the trip"}},"required":["trip_id"]}},{"name":"ingest_confirmation","description":"Parse a raw booking confirmation email; extracts flights, hotels, car rentals, and meetings and stores them as segments on the matched or created trip.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"text":{"type":"string","description":"Raw confirmation email text (plain text)"},"trip_id":{"type":"string","description":"UUID of an existing trip to attach segments to (optional — omit to auto-match or create)"}},"required":["text"]}},{"name":"submit_group_preferences","description":"Submit group trip preferences for alignment (Group Harmony). Use trip_code from the share link.\nThe caller must already be a member (call join_group_trip first).\nCanonical preferences shape (all keys optional, but alignment only reconciles dates, destination, and budget):\n  dates: { ideal_start: \"YYYY-MM-DD\", ideal_end: \"YYYY-MM-DD\", flexibility_days: integer, blackout_dates: [\"YYYY-MM-DD\"] }\n  destination: { top_choices: [\"City, Country\", ...], veto: [\"City, Country\", ...] }\n  budget: { min: number, max: number } — MUST be an object. A scalar budget (e.g. 2000) is ignored by alignment and treated as 0.\n  accommodation: string   activities: [string, ...]\nFull example:\n  {\"dates\":{\"ideal_start\":\"2026-04-10\",\"ideal_end\":\"2026-04-17\",\"flexibility_days\":3,\"blackout_dates\":[\"2026-04-13\"]},\n   \"destination\":{\"top_choices\":[\"Tokyo, Japan\",\"Osaka, Japan\"],\"veto\":[\"Bangkok, Thailand\"]},\n   \"budget\":{\"min\":1500,\"max\":2500},\"accommodation\":\"hotel\",\"activities\":[\"food tours\",\"museums\"]}\nResponse echoes trip_code and join_url so agents can persist context.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_code":{"type":"string","description":"Group trip code (e.g. from join URL)"},"preferences":{"type":"object","description":"Canonical preference object — see capability description for the full shape and example.","properties":{"dates":{"type":"object","properties":{"ideal_start":{"type":"string","description":"ISO date YYYY-MM-DD"},"ideal_end":{"type":"string","description":"ISO date YYYY-MM-DD"},"flexibility_days":{"type":"integer"},"blackout_dates":{"type":"array","items":{"type":"string","description":"ISO date YYYY-MM-DD"}}}},"destination":{"type":"object","properties":{"top_choices":{"type":"array","items":{"type":"string"},"description":"e.g. \"Tokyo, Japan\""},"veto":{"type":"array","items":{"type":"string"}}}},"budget":{"type":"object","description":"Object only — a scalar is ignored by alignment and treated as 0.","properties":{"min":{"type":"number"},"max":{"type":"number"}}},"accommodation":{"type":"string"},"activities":{"type":"array","items":{"type":"string"}}}}},"required":["trip_code","preferences"]}},{"name":"submit_group_inspiration","description":"Submit a link, raw text, or explicit place to a group inspiration board. Use trip_code from the group share link. Provide exactly one of url, text, or place.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_code":{"type":"string","description":"Group trip code"},"url":{"type":"string","description":"URL to ingest into the board"},"text":{"type":"string","description":"Raw note/text to ingest into the board (max 5000 chars)"},"place":{"type":"object","description":"Explicit place candidate to add. Shape: { name: string (required), city: string, neighborhood: string, category: enum, blurb: string, confidence: enum }","properties":{"name":{"type":"string","description":"Required. Max 255 chars."},"city":{"type":"string","description":"Max 120 chars"},"neighborhood":{"type":"string","description":"Max 120 chars"},"category":{"type":"string","enum":["food","sight","activity","shopping","nature","nightlife","stay","other"]},"blurb":{"type":"string","description":"Max 500 chars"},"confidence":{"type":"string","enum":["high","medium","low"]}},"required":["name"]}},"required":["trip_code"]}},{"name":"get_group_board","description":"Get the collaborative inspiration board for a group trip, including candidates, reactions, review lane, and readiness signals.\nreadiness signals report whether the board is ready to generate an itinerary, evaluated against readiness_policy:\n  alignment_min_score (number) — required alignment_score before generation\n  min_locked_in_places (integer) — how many \"locked_in\" candidates are required\n  unresolved_splits / failed_imports / review_candidates — each \"block\" | \"warn\" | \"ignore\"\n  creator_override (boolean) — when true, blockers become warnings\nThe response carries ready (boolean), blockers[], warnings[], and counts. The creator sets readiness_policy via PUT /api/group-trips/:trip_code/board-status.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_code":{"type":"string","description":"Group trip code"}},"required":["trip_code"]}},{"name":"react_to_place","description":"Vote on a place candidate in a group inspiration board. Agent votes are advisory until the human member confirms them.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_code":{"type":"string","description":"Group trip code"},"candidate_id":{"type":"string","description":"Place candidate UUID"},"vote":{"type":"string","enum":["in","unsure","out"],"description":"One of \"in\", \"unsure\", \"out\"."},"starred":{"type":"boolean","default":false},"reason":{"type":"string","description":"Required (non-empty) when vote is \"out\"; the request is rejected with a 400 otherwise. Max 500 chars."}},"required":["trip_code","candidate_id","vote"]}},{"name":"update_place_candidate","description":"Confirm, edit, or ignore a review-lane place candidate on a group inspiration board. When a human member calls this with action=\"confirm\" the place becomes binding for the trip (agent react_to_place votes are advisory only).","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_code":{"type":"string","description":"Group trip code"},"candidate_id":{"type":"string","description":"Place candidate UUID"},"action":{"type":"string","enum":["confirm","edit","ignore"],"description":"\"confirm\" accepts the candidate as-is (binding when done by a human), \"edit\" accepts it with the field overrides below, \"ignore\" drops it from the review lane."},"name":{"type":"string","description":"Applies to action=\"edit\". Max 255 chars."},"city":{"type":"string","description":"Applies to action=\"edit\". Max 120 chars."},"neighborhood":{"type":"string","description":"Applies to action=\"edit\". Max 120 chars."},"category":{"type":"string","enum":["food","sight","activity","shopping","nature","nightlife","stay","other"],"description":"Applies to action=\"edit\"."},"blurb":{"type":"string","description":"Applies to action=\"edit\". Max 500 chars."}},"required":["trip_code","candidate_id","action"]}},{"name":"update_user_preferences","description":"Update the authenticated user's travel preferences.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"interests":{"type":"array","items":{"type":"string"}},"travel_style":{"type":"string","enum":["budget","comfort","luxury","adventure","cultural","relaxation"]},"accommodation_type":{"type":"string","enum":["hotel","hostel","airbnb","resort","camping","guesthouse","apartment"]},"budget_range":{"type":"string","enum":["under-500","500-1000","1000-2000","2000-5000","over-5000"]},"preferred_destinations":{"type":"array","items":{"type":"string"}},"dietary_restrictions":{"type":"array","items":{"type":"string"}},"accessibility_needs":{"type":"array","items":{"type":"string"}}},"required":[]}},{"name":"create_group_trip","description":"Create a new group trip. Returns trip_code and join_url that members use to join. The caller becomes the creator (and default lead agent). Agent actions on this trip are attributed to the creating member.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"title":{"type":"string","description":"Group trip name, max 255 chars"},"tentative_dates":{"type":"object","description":"Object with start/end date ranges under consideration, e.g. { start: \"2026-04-10\", end: \"2026-04-20\" }"},"destination_options":{"type":"array","items":{"type":"string"},"description":"Array of candidate destination name STRINGS, e.g. [\"Tokyo\", \"Osaka\"]. NOT objects."},"budget_min":{"type":"number","description":"Lower bound of the group budget per person"},"budget_max":{"type":"number","description":"Upper bound of the group budget per person"}},"required":[]}},{"name":"list_group_trips","description":"List all group trips the authenticated user belongs to. Returns an array of { trip_code, status, role, title, id, ... }. role is one of \"creator\" or \"member\". Requires the group_trips:read scope.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{},"required":[]}},{"name":"delete_group_trip","description":"Delete a group trip and its members, preferences, and alignment data. Creator only — other members receive a 403. Requires the group_trips:write scope.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_code":{"type":"string","description":"Group trip code"}},"required":["trip_code"]}},{"name":"join_group_trip","description":"Join an existing group trip using its trip_code.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_code":{"type":"string","description":"Group trip code from the share link"}},"required":["trip_code"]}},{"name":"get_group_trip","description":"Get a group trip by its trip_code, including members and current preferences.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_code":{"type":"string","description":"Group trip code"}},"required":["trip_code"]}},{"name":"run_alignment","description":"Trigger Group Harmony alignment — AI reconciles all member preferences (only dates, destination, and budget are reconciled) and produces a consensus plan. Lead agent only. Each call runs the next round; the default max is 2 rounds before the trip moves to escalation_pending (the creator can then call extend-rounds). Every round sets a response_deadline 48h out; members who have not resubmitted a response by the time the next round runs are auto-accepted and returned in timed_out_members.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_code":{"type":"string","description":"Group trip code"}},"required":["trip_code"]}},{"name":"get_alignment","description":"Get the latest Group Harmony alignment result for a group trip: consensus_items, conflicts, proposed_destination, proposed_dates, round_number, response_deadline (48h per round), and alignment_score (20/45/70/95 for 0/1/2/3 of dates+destination+budget reconciled). Use GET /api/group-trips/:trip_code/alignment/rounds for full per-round history and GET .../alignment/status for the poll view including timed_out / pending members.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_code":{"type":"string","description":"Group trip code"}},"required":["trip_code"]}},{"name":"list_proposals","description":"List pending change proposals for a trip (editors and owners only).","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_id":{"type":"string","description":"UUID of the trip"}},"required":["trip_id"]}},{"name":"approve_proposal","description":"Approve a pending change proposal on a trip.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_id":{"type":"string","description":"UUID of the trip"},"proposal_id":{"type":"string","description":"UUID of the proposal"}},"required":["trip_id","proposal_id"]}},{"name":"reject_proposal","description":"Reject a pending change proposal on a trip.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_id":{"type":"string","description":"UUID of the trip"},"proposal_id":{"type":"string","description":"UUID of the proposal"}},"required":["trip_id","proposal_id"]}},{"name":"get_edit_log","description":"Get the edit history for a trip, including agent and human changes.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_id":{"type":"string","description":"UUID of the trip"}},"required":["trip_id"]}},{"name":"set_user_preference","description":"Record a travel preference on behalf of the authenticated user. Use this when the user states a preference during conversation (e.g. \"I prefer window seats\", \"I am vegetarian\", \"I like boutique hotels\"). The preference is stored as a signal and their AI travel profile is updated automatically.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"type":{"type":"string","description":"Preference category, e.g. \"dietary_restriction\", \"accommodation_style\", \"pacing\", \"activity_preference\", \"transport_preference\""},"value":{"description":"The preference value (string, number, or boolean)"},"confidence":{"type":"number","description":"0.0–1.0. Default 1.0 for explicit statements."}},"required":["type","value"]}},{"name":"get_preference_profile","description":"Get the authenticated user's AI-rendered travel preference profile (full markdown doc). Useful before creating a trip or making personalised recommendations.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{},"required":[]}},{"name":"update_day_notes","description":"Set or replace the notes for a specific itinerary day. Day notes are free-text and visible in the travel view. Use this to record how a day went, note plan deviations, or add context for a day. Provide either day_id (UUID from get_trip itinerary.days[n].id) or day_number (1-based integer); day_id is preferred when available.","auth_required":true,"implementation_status":"implemented","exposed_via":["task","mcp"],"input_schema":{"type":"object","properties":{"trip_id":{"type":"string","description":"UUID of the trip"},"day_id":{"type":"string","description":"UUID of the itinerary day (itinerary.days[n].id from get_trip). Preferred over day_number."},"day_number":{"type":"integer","description":"Day of the trip (1 = first day). Used to look up the day UUID if day_id is not provided."},"notes":{"type":"string","description":"Free-text notes for the day. Pass an empty string to clear existing notes."}},"required":["trip_id","notes"]}}]}}}}}}},"/api/agent-auth/device":{"post":{"summary":"Initiate device authorization","description":"Start quick connect. Agents should call this first, then direct the user to verification_uri_complete. Do not start by scraping /connect.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceAuthRequest"}}}},"responses":{"200":{"description":"Device authorization request created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceAuthResponse"}}}},"500":{"description":"Failed to initiate device auth","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceTokenError"}}}}}}},"/api/agent-auth/token":{"post":{"summary":"Poll for approved device token","description":"Poll every interval seconds using the device_code returned by POST /api/agent-auth/device. Treat authorization_pending as a normal in-progress state.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceTokenRequest"}}}},"responses":{"200":{"description":"Approved access token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceTokenResponse"}}}},"400":{"description":"Pending, denied, expired, or invalid device code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceTokenError"}}}},"500":{"description":"Failed to poll for token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceTokenError"}}}}}}},"/api/agent/task":{"post":{"summary":"Execute an agent capability","description":"Preferred agent execution path. Fetch GET /api/agent/capabilities first to discover valid capability names and argument schemas.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentTaskRequest"}}}},"responses":{"200":{"description":"Capability executed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentTaskResponse"}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Capability not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/openapi.json":{"get":{"summary":"OpenAPI spec","description":"This document","security":[],"responses":{"200":{"description":"OpenAPI 3.0 JSON"}}}},"/api/trips":{"get":{"summary":"List trips","responses":{"200":{"description":"List of trips"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create trip","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripCreate"}}}},"responses":{"201":{"description":"Trip created (with itinerary if not skip_ai_generation)"},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/trips/{id}":{"get":{"summary":"Get trip and itinerary","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Trip with itinerary"},"404":{"description":"Trip not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"summary":"Update trip","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripCreate"}}}},"responses":{"200":{"description":"Updated trip"},"404":{"description":"Trip not found"}}},"delete":{"summary":"Delete trip","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted"},"404":{"description":"Trip not found"}}}},"/api/trips/{id}/regenerate":{"post":{"summary":"Regenerate itinerary","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"New itinerary"},"404":{"description":"Trip not found"}}}},"/api/trips/{id}/activities":{"get":{"summary":"Get trip activities","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Activities"}}},"post":{"summary":"Add activity","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["title"],"properties":{"title":{"type":"string"},"description":{"type":"string"},"activity_type":{"type":"string"},"start_time":{"type":"string"},"location":{"type":"string"},"day_number":{"type":"integer"}}}}}},"responses":{"201":{"description":"Activity added"}}}},"/api/trips/{id}/activities/{activityId}":{"put":{"summary":"Update activity","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"activityId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Updated"}}},"delete":{"summary":"Delete activity","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"activityId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted"}}}},"/api/bookings":{"get":{"summary":"List bookings","responses":{"200":{"description":"List of bookings"}}},"post":{"summary":"Record a booking made elsewhere (record-keeping only; no live booking)","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingCreate"}}}},"responses":{"201":{"description":"Booking created"},"400":{"description":"Validation failed"}}}},"/api/bookings/availability":{"post":{"summary":"Check existing booking records for destination and dates (not live availability)","requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Availability result"}}}},"/api/bookings/{id}":{"get":{"summary":"Get booking","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Booking"},"404":{"description":"Booking not found"}}},"patch":{"summary":"Update booking","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Updated"}}},"delete":{"summary":"Cancel booking","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Cancelled"}}}},"/api/users/me/preferences":{"get":{"summary":"Get user preferences","responses":{"200":{"description":"Preferences"}}},"put":{"summary":"Update user preferences","requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Updated"}}}},"/api/trips/{id}/concierge/state":{"get":{"summary":"Get personal Concierge state for the authenticated traveler","description":"Returns this user's settings, context, and location only. Coordinates are never placed on the trip WebSocket room.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"trip_revision","in":"query","schema":{"type":"integer"}},{"name":"context_revision","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"Personal concierge state envelope"},"404":{"description":"Trip not found"}}}},"/api/trips/{id}/concierge/settings":{"patch":{"summary":"Update personal Concierge settings","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Updated settings"},"422":{"description":"Monitoring requires a confirmed transport mode"}}}},"/api/trips/{id}/concierge/location":{"post":{"summary":"Share a foreground GPS sample","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Updated context revision"},"422":{"description":"Invalid or expired sample"}}},"delete":{"summary":"Clear personal location and stop sharing","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Location cleared"}}}},"/api/trips/{id}/concierge/events":{"post":{"summary":"Report a personal status event (idempotent by client_event_id)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"202":{"description":"Accepted"}}}},"/api/trips/{id}/concierge/evaluate":{"post":{"summary":"Request an evaluation (1/min/user/trip)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"202":{"description":"Queued"},"429":{"description":"Throttled"}}}},"/api/trips/{id}/concierge/recommendations/{rid}/respond":{"post":{"summary":"Dismiss, keep plan, or snooze a personal recommendation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"rid","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Updated recommendation"},"409":{"description":"Stale version"},"410":{"description":"Expired"}}}},"/api/trips/{id}/concierge/recommendations/{rid}/apply":{"post":{"summary":"Atomically apply a validated recovery option (editors only)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"rid","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","schema":{"type":"string"}}],"responses":{"200":{"description":"Applied; duplicate key returns the original result"},"403":{"description":"Viewer cannot apply"},"409":{"description":"Stale trip or option; refresh"},"410":{"description":"Expired or no longer actionable"}}}},"/api/trips/{id}/concierge/recommendations/{rid}/undo":{"post":{"summary":"Undo an applied option if the trip revision is unchanged","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"rid","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Inverse patch applied"},"409":{"description":"Intervening edit; undo refused"}}}},"/api/users/me/push-subscriptions":{"get":{"summary":"List this user's Web Push subscriptions (no p256dh/auth in the response)","responses":{"200":{"description":"Subscriptions and VAPID public key if configured"}}},"post":{"summary":"Create or update a Web Push subscription for the authenticated user","responses":{"200":{"description":"Saved"},"409":{"description":"Endpoint belongs to another user"}}}},"/api/public/trips/{token}":{"get":{"summary":"Get shared trip (no auth)","security":[],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Shared trip and itinerary"},"404":{"description":"Not found or expired"}}}}}}