{"openapi":"3.1.0","info":{"title":"Wendily API","version":"0.6.0","summary":"Record trip itineraries on a user's behalf.","description":"The Wendily API is built for AI agents. A user talks to their agent about a trip\nthey are planning, or one they just took, and the agent writes the itinerary\ninto Wendily as the conversation goes.\n\n## Authentication\n\nEvery endpoint except `/health` and the OpenAPI documents requires a bearer\ncredential. AI agents use an agent token the user creates on the Connections\npage of the Wendily app:\n\n```\nAuthorization: Bearer trips_sk_...\n```\n\nA token is scoped to one user. Everything you create belongs to them.\n\nAgents that speak the Model Context Protocol can connect to the same\noperations as tools at `/functions/v1/mcp` on this host, with the same\nbearer token.\n\n## Shape of an itinerary\n\nA **trip** holds ordered **days**, and each day holds ordered **items** (a\nplace, an activity, a stay, a transport leg, or a plain note). Create the trip\nfirst, then its days, then the items on each day.\n\n## Workflow\n\n1. **Call `POST /trips/start` as your very first action**, before you ask the\n   user a single question. Planning usually takes minutes of conversation\n   before anything is written, and this call is what lets the user see in\n   Wendily that their agent is working. It creates a placeholder trip with\n   `status: drafting`. Send whatever you already know (often nothing) and\n   keep the returned `id`.\n2. Talk to the user. Nothing else needs to be written yet.\n3. Once you know the trip, `PATCH /trips/{tripId}` with the real `title`,\n   `destination`, dates, and so on. That first update turns the placeholder\n   into an ordinary trip (`status: ready`).\n4. Add days with `POST /trips/{tripId}/days`, then items on each day.\n5. Keep the itinerary current as the plan changes.\n\nOnly fall back to `POST /trips` when you already have the full picture and\ncan create the trip immediately.\n\n## Conventions\n\n- Request and response bodies use `camelCase`.\n- Successful reads and writes return `{ \"data\": ... }`.\n- Errors return `{ \"error\": { \"code\", \"message\", \"details\"? } }`.\n- Dates are `YYYY-MM-DD`; times are `HH:MM` on a 24-hour clock, with no timezone.\n- Trips are shared with named accounts as `editor` or `viewer`; there are no\n  anonymous share links. An invitation stays `pending` until the invited\n  person accepts it in Wendily.\n- Archived trips remain readable, but cannot be changed until the owner\n  restores them.\n"},"servers":[{"url":"https://edge-runtime.supabase.com/functions/v1/api","description":"This deployment"}],"tags":[{"name":"Service","description":"Unauthenticated endpoints for health checks and discovery."},{"name":"Trips","description":"Create and edit itineraries."},{"name":"Days","description":"The ordered days of a trip."},{"name":"Items","description":"The entries that make up a day."},{"name":"Members","description":"Account-based editor and viewer access, managed by the trip owner."}],"security":[{"agentToken":[]}],"paths":{"/health":{"get":{"operationId":"getHealth","tags":["Service"],"summary":"Check that the API is reachable","security":[],"responses":{"200":{"description":"The API is up.","content":{"application/json":{"schema":{"type":"object","required":["status","version"],"properties":{"status":{"type":"string","const":"ok"},"version":{"type":"string","examples":["0.6.0"]}}}}}}}}},"/openapi.json":{"get":{"operationId":"getOpenApiJson","tags":["Service"],"summary":"Fetch this specification as JSON","description":"The machine-readable contract. Load this to discover the available\noperations rather than hard-coding them.\n","security":[],"responses":{"200":{"description":"The OpenAPI document.","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/openapi.yaml":{"get":{"operationId":"getOpenApiYaml","tags":["Service"],"summary":"Fetch this specification as YAML","security":[],"responses":{"200":{"description":"The OpenAPI document.","content":{"application/yaml":{"schema":{"type":"string"}}}}}}},"/me":{"get":{"operationId":"getMe","tags":["Service"],"summary":"Identify the authenticated user","description":"Use this to confirm a token works before writing anything.","responses":{"200":{"description":"The token's owner.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","required":["userId","tokenName"],"properties":{"userId":{"type":"string","format":"uuid"},"email":{"type":["string","null"],"format":"email"},"displayName":{"type":["string","null"]},"tokenName":{"type":["string","null"],"description":"The name the user gave this token."}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/trips":{"get":{"operationId":"listTrips","tags":["Trips"],"summary":"List trips the user can access","parameters":[{"name":"kind","in":"query","description":"Only return trips of this kind.","schema":{"$ref":"#/components/schemas/TripKind"}},{"name":"archived","in":"query","description":"Exclude archived trips by default, include both, or return only archived trips.","schema":{"type":"string","enum":["exclude","include","only"],"default":"exclude"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Trips, newest first.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Trip"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"operationId":"createTrip","tags":["Trips"],"summary":"Create a trip","description":"Create a trip whose details you already know. If you are about to gather\nthose details in conversation, call `POST /trips/start` first instead and\nfill the trip in later with `PATCH /trips/{tripId}`.\n\nSet `kind` to `past` for a trip the user already took and wants to share,\nor `planned` for one being planned. Use `plannedFor` when the itinerary is\nfor someone other than the user.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTrip"}}}},"responses":{"201":{"description":"The created trip.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/trips/start":{"post":{"operationId":"startTrip","tags":["Trips"],"summary":"Announce that you have started working on a trip","description":"**Call this first**, before asking the user anything. It creates a\nplaceholder trip with `status: drafting` so the user sees that their\nagent is at work while you are still gathering requirements. Every field\nis optional; an empty object `{}` or no body at all is fine.\n\nThe placeholder is a real trip with a real `id`. When you know enough,\nupdate it with `PATCH /trips/{tripId}`, which also switches `status` to\n`ready`. Adding the first day has the same effect. Do not create a second\ntrip with `POST /trips` for the same plan.\n\nDefaults: `kind` is `planned`; `title` is `Trip to {destination}` when a\ndestination is given, otherwise `New trip`.\n","requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartTrip"},"examples":{"nothingKnownYet":{"summary":"The usual case","value":{}},"destinationKnown":{"summary":"The user already mentioned where","value":{"destination":"Kyoto, Japan"}}}}}},"responses":{"201":{"description":"The placeholder trip, with `status` set to `drafting`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/trips/{tripId}":{"parameters":[{"$ref":"#/components/parameters/TripId"}],"get":{"operationId":"getTrip","tags":["Trips"],"summary":"Fetch one trip with its days and items","responses":{"200":{"description":"The trip, including its full itinerary.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/TripDetail"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"operationId":"updateTrip","tags":["Trips"],"summary":"Update a trip","description":"Only the fields you send are changed. Updating a `drafting` placeholder\ncreated by `POST /trips/start` sets its `status` to `ready`.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTrip"}}}},"responses":{"200":{"description":"The updated trip.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/trips/{tripId}/archive":{"parameters":[{"$ref":"#/components/parameters/TripId"}],"post":{"operationId":"archiveTrip","tags":["Trips"],"summary":"Archive a trip","description":"Only the owner may archive a trip. Archiving is idempotent. The trip and\nits membership remain readable, but all writes are frozen until restore.\n","responses":{"200":{"description":"The archived trip.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/trips/{tripId}/restore":{"parameters":[{"$ref":"#/components/parameters/TripId"}],"post":{"operationId":"restoreTrip","tags":["Trips"],"summary":"Restore an archived trip","description":"Only the owner may restore a trip. Restoring is idempotent.","responses":{"200":{"description":"The active trip.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/trips/{tripId}/days":{"parameters":[{"$ref":"#/components/parameters/TripId"}],"post":{"operationId":"createTripDay","tags":["Days"],"summary":"Add a day to a trip","description":"`dayNumber` is the 1-based position of the day within the trip and must be\nunique for that trip. `date` is optional, which lets an agent sketch out\n\"day 1, day 2\" before any dates are settled.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTripDay"}}}},"responses":{"201":{"description":"The created day.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripDayResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"A day with that `dayNumber` already exists on the trip.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/trips/{tripId}/days/{dayId}":{"parameters":[{"$ref":"#/components/parameters/TripId"},{"$ref":"#/components/parameters/DayId"}],"patch":{"operationId":"updateTripDay","tags":["Days"],"summary":"Update a day","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTripDay"}}}},"responses":{"200":{"description":"The updated day.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripDayResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"operationId":"deleteTripDay","tags":["Days"],"summary":"Delete a day and its items","responses":{"204":{"description":"Deleted."},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/trips/{tripId}/days/{dayId}/items":{"parameters":[{"$ref":"#/components/parameters/TripId"},{"$ref":"#/components/parameters/DayId"}],"post":{"operationId":"createTripItem","tags":["Items"],"summary":"Add an item to a day","description":"One entry on the itinerary. `position` is a 0-based sort key within the\nday; if you omit it the item goes last. Positions need not be unique or\ncontiguous, and supplying one that is already taken does not renumber the\nexisting items — ties are broken by creation time, so the new item lands\nafter them. To guarantee an exact order, set `position` on every item.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTripItem"}}}},"responses":{"201":{"description":"The created item.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripItemResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/trips/{tripId}/days/{dayId}/items/{itemId}":{"parameters":[{"$ref":"#/components/parameters/TripId"},{"$ref":"#/components/parameters/DayId"},{"$ref":"#/components/parameters/ItemId"}],"patch":{"operationId":"updateTripItem","tags":["Items"],"summary":"Update an item","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTripItem"}}}},"responses":{"200":{"description":"The updated item.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripItemResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"operationId":"deleteTripItem","tags":["Items"],"summary":"Delete an item","responses":{"204":{"description":"Deleted."},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/trips/{tripId}/members":{"parameters":[{"$ref":"#/components/parameters/TripId"}],"get":{"operationId":"listTripMembers","tags":["Members"],"summary":"List collaborators and pending invitations","description":"Any active trip member may list the owner and active collaborators. Only\nthe owner receives pending invitations in the response.\n","responses":{"200":{"description":"Members and invitations, oldest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripMemberListResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"post":{"operationId":"inviteTripMember","tags":["Members"],"summary":"Invite a collaborator by email","description":"Creates a `pending` invitation and emails the address. Access is granted\nonly when the invited person accepts in Wendily, so `status` is always\n`pending` here, whether or not the address already has an account.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteTripMember"}}}},"responses":{"201":{"description":"The new membership or pending invitation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripMemberDeliveryResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}}}},"/trips/{tripId}/members/me":{"parameters":[{"$ref":"#/components/parameters/TripId"}],"delete":{"operationId":"leaveTrip","tags":["Members"],"summary":"Leave a trip you were invited to","description":"Removes the caller's own access. Owners cannot leave; they archive the\ntrip instead.\n","responses":{"204":{"description":"Left."},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The caller is the owner.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/trips/{tripId}/members/{memberId}":{"parameters":[{"$ref":"#/components/parameters/TripId"},{"$ref":"#/components/parameters/MemberId"}],"patch":{"operationId":"updateTripMember","tags":["Members"],"summary":"Change a collaborator's role","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTripMember"}}}},"responses":{"200":{"description":"The updated membership.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripMemberResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}}},"delete":{"operationId":"removeTripMember","tags":["Members"],"summary":"Remove a collaborator or cancel an invitation","responses":{"204":{"description":"Removed."},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}}}},"/trips/{tripId}/members/{memberId}/resend":{"parameters":[{"$ref":"#/components/parameters/TripId"},{"$ref":"#/components/parameters/MemberId"}],"post":{"operationId":"resendTripInvitation","tags":["Members"],"summary":"Resend a pending invitation email","responses":{"200":{"description":"The invitation and its latest delivery outcome.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripMemberDeliveryResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"description":"Sent less than ten minutes ago, or already sent four times.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"agentToken":{"type":"http","scheme":"bearer","description":"An agent token generated by the user in the Wendily app. Tokens look like\n`trips_sk_` followed by 64 hex characters and do not expire until revoked.\n"}},"parameters":{"TripId":{"name":"tripId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"DayId":{"name":"dayId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"ItemId":{"name":"itemId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},"MemberId":{"name":"memberId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}},"responses":{"BadRequest":{"description":"The request body or parameters were invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"Missing, unknown, or revoked agent token.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The caller can access the trip, but their role cannot perform this action.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource, or it is not visible to the caller.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The request conflicts with the current resource state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable machine-readable identifier.","examples":["bad_request","unauthorized","forbidden","not_found","trip_archived"]},"message":{"type":"string","description":"Human-readable explanation, safe to show to a user."},"details":{"type":"object","description":"Per-field problems, keyed by request body field.","additionalProperties":{"type":"string"}}}}}},"TripKind":{"type":"string","enum":["past","planned"],"description":"`past` — a trip the user already took and wants to share.\n`planned` — a trip being planned.\n"},"TripStatus":{"type":"string","enum":["drafting","ready"],"description":"`drafting` — a placeholder created by `POST /trips/start`; the agent has\nstarted but has not written the itinerary yet.\n`ready` — an ordinary trip. Set automatically by the first update to the\ntrip or the first day added to it.\n"},"TripAccessRole":{"type":"string","enum":["owner","editor","viewer"]},"CollaboratorRole":{"type":"string","enum":["editor","viewer"]},"TripAccessStatus":{"type":"string","enum":["pending","active"]},"TripItemKind":{"type":"string","enum":["place","activity","stay","transport","note"]},"TripTravelMode":{"type":"string","enum":["walk","transit","taxi","drive","bicycle","ferry","flight","other"]},"Trip":{"type":"object","required":["id","title","kind","status","accessRole","createdAt","updatedAt"],"properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string","examples":["Three days in Lisbon"]},"summary":{"type":["string","null"]},"kind":{"$ref":"#/components/schemas/TripKind"},"status":{"$ref":"#/components/schemas/TripStatus"},"accessRole":{"$ref":"#/components/schemas/TripAccessRole","description":"The authenticated user's effective role on this trip."},"destination":{"type":["string","null"],"examples":["Lisbon, Portugal"]},"startDate":{"type":["string","null"],"format":"date"},"endDate":{"type":["string","null"],"format":"date"},"plannedFor":{"type":["string","null"],"description":"Who the itinerary is for, when it is not the user.","examples":["My parents"]},"coverImageUrl":{"type":["string","null"],"format":"uri","description":"A representative image used for the trip card and hero."},"timeZone":{"type":["string","null"],"description":"IANA time-zone name used to present the itinerary locally.","examples":["Asia/Tokyo"]},"archivedAt":{"type":["string","null"],"format":"date-time","description":"When present, the trip is read-only until the owner restores it."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"TripDetail":{"allOf":[{"$ref":"#/components/schemas/Trip"},{"type":"object","required":["days"],"properties":{"days":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/TripDay"},{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/TripItem"}}}}]}}}}]},"TripDay":{"type":"object","required":["id","tripId","dayNumber"],"properties":{"id":{"type":"string","format":"uuid"},"tripId":{"type":"string","format":"uuid"},"dayNumber":{"type":"integer","minimum":1},"date":{"type":["string","null"],"format":"date"},"title":{"type":["string","null"],"examples":["Alfama and the waterfront"]},"summary":{"type":["string","null"],"description":"Short traveler-facing introduction to the day."},"notes":{"type":["string","null"]}}},"TripItem":{"type":"object","required":["id","tripDayId","position","kind","title"],"properties":{"id":{"type":"string","format":"uuid"},"tripDayId":{"type":"string","format":"uuid"},"position":{"type":"integer","minimum":0},"kind":{"$ref":"#/components/schemas/TripItemKind"},"title":{"type":"string","examples":["Lunch at Time Out Market"]},"description":{"type":["string","null"],"description":"Traveler-facing context about what to expect at this stop."},"notes":{"type":["string","null"],"description":"Concise practical advice or a note from the agent."},"startTime":{"type":["string","null"],"examples":["12:30"]},"endTime":{"type":["string","null"],"examples":["13:45"]},"locationName":{"type":["string","null"]},"locationAddress":{"type":["string","null"]},"latitude":{"type":["number","null"],"minimum":-90,"maximum":90},"longitude":{"type":["number","null"],"minimum":-180,"maximum":180},"url":{"type":["string","null"],"format":"uri"},"imageUrl":{"type":["string","null"],"format":"uri","description":"Image used in itinerary cards and place details."},"travelMode":{"description":"How to reach this item from the previous itinerary item.","oneOf":[{"$ref":"#/components/schemas/TripTravelMode"},{"type":"null"}]},"travelDurationMinutes":{"type":["integer","null"],"minimum":0,"description":"Expected travel time from the previous itinerary item."},"travelDistanceMeters":{"type":["integer","null"],"minimum":0,"description":"Expected distance from the previous itinerary item."},"provider":{"type":["string","null"],"description":"Hotel, carrier, operator, or booking provider."},"phone":{"type":["string","null"]},"reservationCode":{"type":["string","null"],"description":"Private booking or confirmation reference. Never shared publicly."},"bookingUrl":{"type":["string","null"],"format":"uri","description":"Private link for managing a reservation. Never shared publicly."}}},"TripResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Trip"}}},"TripDayResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/TripDay"}}},"TripItemResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/TripItem"}}},"TripMember":{"type":"object","required":["id","email","role","status","createdAt"],"properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"userId":{"type":["string","null"],"format":"uuid"},"role":{"$ref":"#/components/schemas/CollaboratorRole"},"status":{"$ref":"#/components/schemas/TripAccessStatus"},"acceptedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"inviteSentAt":{"type":["string","null"],"format":"date-time","description":"When the invitation email was last attempted."},"inviteSendCount":{"type":"integer","description":"Attempts so far, initial plus resends. A resend is refused within ten\nminutes of the last attempt (`429 resend_too_soon`) and after four\nattempts in total (`429 resend_limit_reached`).\n"}}},"TripMemberResponse":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/TripMember"}}},"TripOwner":{"type":"object","required":["userId","email","displayName"],"properties":{"userId":{"type":"string","format":"uuid"},"email":{"type":["string","null"],"format":"email"},"displayName":{"type":["string","null"]}}},"TripMemberListResponse":{"type":"object","required":["data","owner"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TripMember"}},"owner":{"$ref":"#/components/schemas/TripOwner"}}},"TripMemberDeliveryResponse":{"allOf":[{"$ref":"#/components/schemas/TripMemberResponse"},{"type":"object","required":["emailDelivery"],"properties":{"emailDelivery":{"type":"string","enum":["sent","failed","not_configured"]}}}]},"InviteTripMember":{"type":"object","required":["email","role"],"additionalProperties":false,"properties":{"email":{"type":"string","format":"email","maxLength":320},"role":{"$ref":"#/components/schemas/CollaboratorRole"}}},"UpdateTripMember":{"type":"object","required":["role"],"additionalProperties":false,"properties":{"role":{"$ref":"#/components/schemas/CollaboratorRole"}}},"CreateTrip":{"type":"object","required":["title","kind"],"additionalProperties":false,"properties":{"title":{"type":"string","minLength":1,"maxLength":200},"kind":{"$ref":"#/components/schemas/TripKind"},"summary":{"type":"string","maxLength":2000},"destination":{"type":"string","maxLength":200},"startDate":{"type":"string","format":"date"},"endDate":{"type":"string","format":"date"},"plannedFor":{"type":"string","maxLength":200},"coverImageUrl":{"type":"string","format":"uri","maxLength":2000},"timeZone":{"type":"string","maxLength":100,"examples":["Asia/Tokyo"]}}},"StartTrip":{"type":"object","additionalProperties":false,"description":"Whatever is already known about the trip. Usually nothing.","properties":{"title":{"type":"string","minLength":1,"maxLength":200},"kind":{"$ref":"#/components/schemas/TripKind","description":"Defaults to `planned`."},"summary":{"type":"string","maxLength":2000},"destination":{"type":"string","maxLength":200},"startDate":{"type":"string","format":"date"},"endDate":{"type":"string","format":"date"},"plannedFor":{"type":"string","maxLength":200},"coverImageUrl":{"type":"string","format":"uri","maxLength":2000},"timeZone":{"type":"string","maxLength":100}}},"UpdateTrip":{"type":"object","minProperties":1,"additionalProperties":false,"properties":{"title":{"type":"string","minLength":1,"maxLength":200},"kind":{"$ref":"#/components/schemas/TripKind"},"summary":{"type":["string","null"],"maxLength":2000},"destination":{"type":["string","null"],"maxLength":200},"startDate":{"type":["string","null"],"format":"date"},"endDate":{"type":["string","null"],"format":"date"},"plannedFor":{"type":["string","null"],"maxLength":200},"coverImageUrl":{"type":["string","null"],"format":"uri","maxLength":2000},"timeZone":{"type":["string","null"],"maxLength":100}}},"CreateTripDay":{"type":"object","required":["dayNumber"],"additionalProperties":false,"properties":{"dayNumber":{"type":"integer","minimum":1},"date":{"type":"string","format":"date"},"title":{"type":"string","maxLength":200},"summary":{"type":"string","maxLength":500},"notes":{"type":"string","maxLength":2000}}},"UpdateTripDay":{"type":"object","minProperties":1,"additionalProperties":false,"properties":{"dayNumber":{"type":"integer","minimum":1},"date":{"type":["string","null"],"format":"date"},"title":{"type":["string","null"],"maxLength":200},"summary":{"type":["string","null"],"maxLength":500},"notes":{"type":["string","null"],"maxLength":2000}}},"CreateTripItem":{"type":"object","required":["kind","title"],"additionalProperties":false,"properties":{"kind":{"$ref":"#/components/schemas/TripItemKind"},"title":{"type":"string","minLength":1,"maxLength":200},"position":{"type":"integer","minimum":0},"description":{"type":"string","maxLength":2000},"notes":{"type":"string","maxLength":2000},"startTime":{"type":"string","examples":["09:00"]},"endTime":{"type":"string","examples":["10:30"]},"locationName":{"type":"string","maxLength":200},"locationAddress":{"type":"string","maxLength":500},"latitude":{"type":"number","minimum":-90,"maximum":90},"longitude":{"type":"number","minimum":-180,"maximum":180},"url":{"type":"string","format":"uri"},"imageUrl":{"type":"string","format":"uri","maxLength":2000},"travelMode":{"$ref":"#/components/schemas/TripTravelMode"},"travelDurationMinutes":{"type":"integer","minimum":0},"travelDistanceMeters":{"type":"integer","minimum":0},"provider":{"type":"string","maxLength":200},"phone":{"type":"string","maxLength":100},"reservationCode":{"type":"string","maxLength":200},"bookingUrl":{"type":"string","format":"uri","maxLength":2000}}},"UpdateTripItem":{"type":"object","minProperties":1,"additionalProperties":false,"properties":{"kind":{"$ref":"#/components/schemas/TripItemKind"},"title":{"type":"string","minLength":1,"maxLength":200},"position":{"type":"integer","minimum":0},"description":{"type":["string","null"],"maxLength":2000},"notes":{"type":["string","null"],"maxLength":2000},"startTime":{"type":["string","null"]},"endTime":{"type":["string","null"]},"locationName":{"type":["string","null"],"maxLength":200},"locationAddress":{"type":["string","null"],"maxLength":500},"latitude":{"type":["number","null"],"minimum":-90,"maximum":90},"longitude":{"type":["number","null"],"minimum":-180,"maximum":180},"url":{"type":["string","null"],"format":"uri"},"imageUrl":{"type":["string","null"],"format":"uri","maxLength":2000},"travelMode":{"oneOf":[{"$ref":"#/components/schemas/TripTravelMode"},{"type":"null"}]},"travelDurationMinutes":{"type":["integer","null"],"minimum":0},"travelDistanceMeters":{"type":["integer","null"],"minimum":0},"provider":{"type":["string","null"],"maxLength":200},"phone":{"type":["string","null"],"maxLength":100},"reservationCode":{"type":["string","null"],"maxLength":200},"bookingUrl":{"type":["string","null"],"format":"uri","maxLength":2000}}}}}}