{"openapi":"3.1.0","info":{"title":"MotoQuest API","version":"0.2.0","description":"REST API for the MotoQuest web and mobile apps. All paths are under `/v1`.\n\nErrors are RFC 9457 problem details with a stable `code`; the apps translate the code.\n\nLists use cursors. Mobile writes may carry an `Idempotency-Key` header.\n\nThe oldest supported mobile app build is 0.1.0; below it the API answers 426 `upgrade_required`."},"servers":[{"url":"/v1"}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","bearerFormat":"access token"}},"schemas":{"Problem":{"type":"object","properties":{"type":{"type":"string","default":"about:blank"},"title":{"type":"string"},"status":{"type":"integer"},"code":{"$ref":"#/components/schemas/ErrorCode"},"detail":{"type":"string","description":"For developers and logs, never shown to users"},"errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"}},"required":["path","code"]},"description":"Field errors for validation_failed"}},"required":["title","status","code"]},"ErrorCode":{"type":"string","enum":["unauthenticated","invalid_token","token_expired","forbidden","not_found","validation_failed","conflict","rate_limited","idempotency_mismatch","invitation_invalid","invitation_expired","ride_full","already_member","route_failed","route_out_of_coverage","upgrade_required","not_implemented","internal"]},"TokenPair":{"type":"object","properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"},"expiresIn":{"type":"integer","description":"Seconds until the access token expires (900)"},"user":{"$ref":"#/components/schemas/User"},"minAppVersion":{"type":"string","description":"Oldest supported app version"}},"required":["accessToken","refreshToken","expiresIn","user","minAppVersion"]},"User":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Random UUID"},"email":{"type":"string","format":"email"},"name":{"type":["string","null"]},"language":{"$ref":"#/components/schemas/Language"},"platformAdmin":{"type":"boolean"},"twoStepEnabled":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 date and time, UTC"}},"required":["id","email","name","language","platformAdmin","twoStepEnabled","createdAt"]},"Language":{"type":"string","enum":["en","ro"]},"MobileGoogleSignIn":{"type":"object","properties":{"idToken":{"type":"string","minLength":10},"device":{"$ref":"#/components/schemas/DeviceInfo"},"invitationToken":{"type":"string","description":"Lets a new person create an account when they arrive through an invite link"}},"required":["idToken","device"]},"DeviceInfo":{"type":"object","properties":{"platform":{"$ref":"#/components/schemas/Platform"},"name":{"type":"string","maxLength":80},"appVersion":{"type":"string","maxLength":32},"pushToken":{"type":"string","maxLength":512}},"required":["platform"]},"Platform":{"type":"string","enum":["ios","android","web"]},"DevSignIn":{"type":"object","properties":{"email":{"type":"string","format":"email"},"name":{"type":"string","maxLength":60},"device":{"$ref":"#/components/schemas/DeviceInfo"},"invitationToken":{"type":"string"}},"required":["email","device"],"description":"Development only. Disabled when AUTH_DEV_LOGIN is not 1, and never available in production."},"RefreshRequest":{"type":"object","properties":{"refreshToken":{"type":"string"}},"required":["refreshToken"]},"SignOutRequest":{"type":"object","properties":{"refreshToken":{"type":"string"},"everywhere":{"type":"boolean","default":false}}},"UpdateMe":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":60},"language":{"$ref":"#/components/schemas/Language"}}},"DeviceList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Device"}}},"required":["items"]},"Device":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Random UUID"},"platform":{"$ref":"#/components/schemas/Platform"},"name":{"type":["string","null"]},"lastSeenAt":{"type":"string","format":"date-time","description":"ISO 8601 date and time, UTC"},"current":{"type":"boolean"}},"required":["id","platform","name","lastSeenAt","current"]},"OrganisationList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Organisation"}},"nextCursor":{"type":["string","null"],"maxLength":512,"description":"Null on the last page"}},"required":["items","nextCursor"]},"Organisation":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Random UUID"},"name":{"type":"string"},"myRole":{"$ref":"#/components/schemas/OrgRole"},"memberCount":{"type":"integer"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 date and time, UTC"}},"required":["id","name","myRole","memberCount","createdAt"]},"OrgRole":{"type":"string","enum":["owner","planner","member"],"description":"owner = Organisation owner; planner may create rides; member joins rides they are invited to"},"CreateOrganisation":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":80}},"required":["name"]},"OrgMemberList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/OrgMember"}},"nextCursor":{"type":["string","null"],"maxLength":512,"description":"Null on the last page"}},"required":["items","nextCursor"]},"OrgMember":{"type":"object","properties":{"userId":{"type":"string","format":"uuid","description":"Random UUID"},"name":{"type":["string","null"]},"email":{"type":"string","format":"email"},"role":{"$ref":"#/components/schemas/OrgRole"},"joinedAt":{"type":"string","format":"date-time","description":"ISO 8601 date and time, UTC"}},"required":["userId","name","email","role","joinedAt"]},"AddOrgMember":{"type":"object","properties":{"email":{"type":"string","format":"email"},"role":{"$ref":"#/components/schemas/OrgRole"}},"required":["email"],"description":"Registration is invite-only: the person is added by email and can then sign in"},"UpdateOrgMember":{"type":"object","properties":{"role":{"$ref":"#/components/schemas/OrgRole"}},"required":["role"]},"RideList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Ride"}},"nextCursor":{"type":["string","null"],"maxLength":512,"description":"Null on the last page"}},"required":["items","nextCursor"]},"Ride":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Random UUID"},"orgId":{"type":"string","format":"uuid","description":"Random UUID"},"name":{"type":"string"},"description":{"type":"string"},"startsAt":{"type":["string","null"],"format":"date-time","description":"ISO 8601 date and time, UTC"},"status":{"$ref":"#/components/schemas/RideStatus"},"capacity":{"type":["integer","null"]},"currentRouteVersionId":{"type":["string","null"],"format":"uuid","description":"Random UUID"},"myRoles":{"type":"array","items":{"$ref":"#/components/schemas/RideRole"},"description":"Empty when the caller only sees the ride as Organisation owner or Platform admin"},"myAttendance":{"$ref":"#/components/schemas/Attendance"},"counts":{"$ref":"#/components/schemas/RideCounts"},"createdBy":{"type":["string","null"],"format":"uuid","description":"Null when the person who created the ride has deleted their account"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 date and time, UTC"},"updatedAt":{"type":"string","format":"date-time","description":"ISO 8601 date and time, UTC"}},"required":["id","orgId","name","description","startsAt","status","capacity","currentRouteVersionId","myRoles","myAttendance","counts","createdBy","createdAt","updatedAt"]},"RideStatus":{"type":"string","enum":["draft","open","running","finished","cancelled"]},"RideRole":{"type":"string","enum":["ride_admin","ride_planner","road_captain","rider"]},"Attendance":{"type":["string","null"],"enum":["invited","going","maybe","not_going",null]},"RideCounts":{"type":"object","properties":{"invited":{"type":"integer"},"going":{"type":"integer"},"maybe":{"type":"integer"},"notGoing":{"type":"integer"}},"required":["invited","going","maybe","notGoing"]},"CreateRide":{"type":"object","properties":{"orgId":{"type":"string","format":"uuid","description":"Random UUID"},"name":{"type":"string","minLength":2,"maxLength":120},"description":{"type":"string","maxLength":4000,"default":""},"startsAt":{"type":"string","format":"date-time","description":"ISO 8601 date and time, UTC"},"capacity":{"type":"integer","minimum":1,"maximum":1000}},"required":["orgId","name"]},"UpdateRide":{"type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":120},"description":{"type":"string","maxLength":4000},"startsAt":{"type":["string","null"],"format":"date-time","description":"ISO 8601 date and time, UTC"},"capacity":{"type":["integer","null"],"minimum":1,"maximum":1000},"status":{"$ref":"#/components/schemas/RideStatus"}}},"RideMemberList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/RideMember"}},"nextCursor":{"type":["string","null"],"maxLength":512,"description":"Null on the last page"}},"required":["items","nextCursor"]},"RideMember":{"type":"object","properties":{"userId":{"type":"string","format":"uuid","description":"Random UUID"},"name":{"type":["string","null"]},"email":{"type":["string","null"],"format":"email","description":"Only visible to Ride Admins and Organisation owners"},"roles":{"type":"array","items":{"$ref":"#/components/schemas/RideRole"}},"attendance":{"$ref":"#/components/schemas/Attendance"},"checkedInAt":{"type":["string","null"],"format":"date-time","description":"ISO 8601 date and time, UTC"},"groupId":{"type":["string","null"],"format":"uuid","description":"Random UUID"}},"required":["userId","name","email","roles","attendance","checkedInAt","groupId"]},"AddRideMember":{"type":"object","properties":{"email":{"type":"string","format":"email"},"roles":{"type":"array","items":{"$ref":"#/components/schemas/RideRole"},"minItems":1,"default":["rider"]},"groupId":{"type":"string","format":"uuid","description":"Random UUID"}},"required":["email"]},"UpdateRideMember":{"type":"object","properties":{"roles":{"type":"array","items":{"$ref":"#/components/schemas/RideRole"},"minItems":1},"groupId":{"type":["string","null"],"format":"uuid","description":"Random UUID"}}},"SetAttendance":{"type":"object","properties":{"attendance":{"$ref":"#/components/schemas/Attendance"}},"required":["attendance"]},"GroupList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Group"}}},"required":["items"]},"Group":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Random UUID"},"name":{"type":"string"},"captainUserId":{"type":["string","null"],"format":"uuid","description":"Random UUID"}},"required":["id","name","captainUserId"]},"CreateGroup":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":40},"captainUserId":{"type":"string","format":"uuid","description":"Random UUID"}},"required":["name"]},"UpdateGroup":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":40},"captainUserId":{"type":["string","null"],"format":"uuid","description":"Random UUID"}}},"RouteVersionList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/RouteVersionSummary"}},"nextCursor":{"type":["string","null"],"maxLength":512,"description":"Null on the last page"}},"required":["items","nextCursor"]},"RouteVersionSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Random UUID"},"rideId":{"type":"string","format":"uuid","description":"Random UUID"},"number":{"type":"integer"},"state":{"$ref":"#/components/schemas/RouteVersionState"},"current":{"type":"boolean"},"authorId":{"type":["string","null"],"format":"uuid","description":"Null when the author has deleted their account"},"authorName":{"type":["string","null"]},"note":{"type":["string","null"]},"distanceM":{"type":"number"},"durationS":{"type":"number"},"stopCount":{"type":"integer"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 date and time, UTC"},"decidedAt":{"type":["string","null"],"format":"date-time","description":"ISO 8601 date and time, UTC"}},"required":["id","rideId","number","state","current","authorId","authorName","note","distanceM","durationS","stopCount","createdAt","decidedAt"]},"RouteVersionState":{"type":"string","enum":["proposed","approved","rejected"]},"RouteVersion":{"allOf":[{"$ref":"#/components/schemas/RouteVersionSummary"},{"type":"object","properties":{"stops":{"type":"array","items":{"$ref":"#/components/schemas/Stop"}},"geometry":{"type":"string","description":"Encoded polyline, precision 6"},"legs":{"type":"array","items":{"$ref":"#/components/schemas/RouteLeg"}},"motorwayAlternative":{"$ref":"#/components/schemas/MotorwayAlternative"}},"required":["stops","geometry","legs","motorwayAlternative"]}]},"Stop":{"allOf":[{"$ref":"#/components/schemas/StopInput"},{"type":"object","properties":{"toNext":{"$ref":"#/components/schemas/SegmentOptions"},"id":{"type":"string","format":"uuid","description":"Random UUID"},"position":{"type":"integer","minimum":0}},"required":["toNext","id","position"]}]},"StopType":{"type":"string","enum":["start","waypoint","fuel","lunch","hotel","sight","end"],"default":"waypoint"},"LngLat":{"type":"object","properties":{"lon":{"type":"number","minimum":-180,"maximum":180},"lat":{"type":"number","minimum":-90,"maximum":90}},"required":["lon","lat"]},"SegmentOptions":{"type":["object","null"],"properties":{"style":{"$ref":"#/components/schemas/RouteStyle"},"avoidHighways":{"type":"boolean","default":true},"avoidTolls":{"type":"boolean","default":true},"unpaved":{"$ref":"#/components/schemas/Unpaved"},"bikeType":{"$ref":"#/components/schemas/BikeType"},"maxSpeedKmh":{"type":"integer","minimum":30,"maximum":200}}},"RouteStyle":{"type":"string","enum":["fastest","fast_curvy","curvy","extra_curvy"],"default":"fast_curvy"},"Unpaved":{"type":"string","enum":["avoid","allow","prefer"],"default":"avoid"},"BikeType":{"type":"string","enum":["road","touring","adventure","offroad"],"default":"touring"},"StopInput":{"type":"object","properties":{"type":{"$ref":"#/components/schemas/StopType"},"name":{"type":"string","minLength":1,"maxLength":200},"location":{"$ref":"#/components/schemas/LngLat"},"placeId":{"type":"string","format":"uuid","description":"Set when the stop was picked from the places layer"},"day":{"type":"integer","minimum":1,"maximum":60,"default":1},"meetingTime":{"type":"string","format":"date-time","description":"ISO 8601 date and time, UTC"},"note":{"type":"string","maxLength":1000},"toNext":{"allOf":[{"$ref":"#/components/schemas/SegmentOptions"},{"description":"Options for the segment from this stop to the next. Defaults to the ride defaults. Ignored on the last stop"}]}},"required":["name","location"]},"RouteLeg":{"type":"object","properties":{"distanceM":{"type":"number"},"durationS":{"type":"number"},"usesMotorway":{"type":"boolean","description":"The leg could not avoid a motorway"}},"required":["distanceM","durationS","usesMotorway"]},"MotorwayAlternative":{"type":["object","null"],"properties":{"distanceM":{"type":"number"},"durationS":{"type":"number"},"geometry":{"type":"string","description":"Encoded polyline, precision 6"},"savesS":{"type":"number","description":"Time saved against the main route, in seconds"}},"required":["distanceM","durationS","geometry","savesS"]},"ProposeRouteVersion":{"type":"object","properties":{"stops":{"type":"array","items":{"$ref":"#/components/schemas/StopInput"},"minItems":2,"maxItems":100},"note":{"type":"string","maxLength":500}},"required":["stops"],"description":"The server computes the route. A Ride Admin or Organisation owner creates an approved version; a Ride Planner creates a proposal"},"RouteVersionDecision":{"type":"object","properties":{"note":{"type":"string","maxLength":500}}},"RouteVersionCompare":{"type":"object","properties":{"a":{"$ref":"#/components/schemas/RouteVersionSummary"},"b":{"$ref":"#/components/schemas/RouteVersionSummary"},"distanceDeltaM":{"type":"number"},"durationDeltaS":{"type":"number"},"addedStops":{"type":"array","items":{"$ref":"#/components/schemas/Stop"}},"removedStops":{"type":"array","items":{"$ref":"#/components/schemas/Stop"}},"movedStops":{"type":"array","items":{"type":"object","properties":{"before":{"$ref":"#/components/schemas/Stop"},"after":{"$ref":"#/components/schemas/Stop"}},"required":["before","after"]}}},"required":["a","b","distanceDeltaM","durationDeltaS","addedStops","removedStops","movedStops"]},"RouteResult":{"type":"object","properties":{"geometry":{"type":"string","description":"Encoded polyline, precision 6"},"distanceM":{"type":"number"},"durationS":{"type":"number"},"legs":{"type":"array","items":{"$ref":"#/components/schemas/RouteLeg"}},"motorwayAlternative":{"allOf":[{"$ref":"#/components/schemas/MotorwayAlternative"},{"description":"Present when highways were avoided and a motorway route is faster"}]},"engine":{"type":"object","properties":{"name":{"type":"string"},"graphVersion":{"type":"string"}},"required":["name","graphVersion"]}},"required":["geometry","distanceM","durationS","legs","motorwayAlternative","engine"]},"RouteRequest":{"type":"object","properties":{"waypoints":{"type":"array","items":{"$ref":"#/components/schemas/LngLat"},"minItems":2,"maxItems":101},"legs":{"type":"array","items":{"$ref":"#/components/schemas/SegmentOptions"},"description":"Options per leg (waypoints - 1). One entry applies to every leg"}},"required":["waypoints"]},"InvitationList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Invitation"}}},"required":["items"]},"Invitation":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Random UUID"},"rideId":{"type":"string","format":"uuid","description":"Random UUID"},"mode":{"$ref":"#/components/schemas/InvitationMode"},"email":{"type":["string","null"],"format":"email"},"roles":{"type":"array","items":{"$ref":"#/components/schemas/RideRole"}},"requiresApproval":{"type":"boolean"},"expiresAt":{"type":"string","format":"date-time","description":"ISO 8601 date and time, UTC"},"usedCount":{"type":"integer"},"revokedAt":{"type":["string","null"],"format":"date-time","description":"ISO 8601 date and time, UTC"}},"required":["id","rideId","mode","email","roles","requiresApproval","expiresAt","usedCount","revokedAt"]},"InvitationMode":{"type":"string","enum":["link","email"]},"CreatedInvitation":{"allOf":[{"$ref":"#/components/schemas/Invitation"},{"type":"object","properties":{"token":{"type":"string","description":"Shown once. Stored only as a hash"},"url":{"type":"string","description":"The universal link, also the QR code content"}},"required":["token","url"]}]},"CreateInvitation":{"type":"object","properties":{"mode":{"$ref":"#/components/schemas/InvitationMode"},"email":{"type":"string","format":"email","description":"Required for mode email"},"roles":{"type":"array","items":{"$ref":"#/components/schemas/RideRole"},"minItems":1,"default":["rider"]},"requiresApproval":{"type":"boolean","default":true,"description":"A link needs a Ride Admin to approve each join. Email invitations do not"},"expiresInDays":{"type":"integer","minimum":1,"maximum":60,"default":14}}},"InvitationPreview":{"type":"object","properties":{"rideName":{"type":"string"},"orgName":{"type":"string"},"startsAt":{"type":["string","null"],"format":"date-time","description":"ISO 8601 date and time, UTC"},"requiresApproval":{"type":"boolean"},"expiresAt":{"type":"string","format":"date-time","description":"ISO 8601 date and time, UTC"}},"required":["rideName","orgName","startsAt","requiresApproval","expiresAt"]},"AcceptInvitationResult":{"type":"object","properties":{"rideId":{"type":"string","format":"uuid","description":"Random UUID"},"status":{"type":"string","enum":["joined","pending_approval"]}},"required":["rideId","status"]},"JoinRequestList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/JoinRequest"}}},"required":["items"]},"JoinRequest":{"type":"object","properties":{"userId":{"type":"string","format":"uuid","description":"Random UUID"},"name":{"type":["string","null"]},"email":{"type":"string","format":"email"},"requestedAt":{"type":"string","format":"date-time","description":"ISO 8601 date and time, UTC"}},"required":["userId","name","email","requestedAt"]},"PlaceList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Place"}},"truncated":{"type":"boolean"}},"required":["items","truncated"]},"Place":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Random UUID"},"osmId":{"type":"string"},"category":{"$ref":"#/components/schemas/PlaceCategory"},"name":{"type":["string","null"]},"location":{"$ref":"#/components/schemas/LngLat"},"rank":{"type":"number"},"minZoom":{"type":"integer","minimum":0,"maximum":18}},"required":["id","osmId","category","name","location","rank","minZoom"]},"PlaceCategory":{"type":"string","enum":["fuel","motorcycle","pass","viewpoint","peak","nature","camping","lodging","food","worship","castle","sight","health","shop","atm","water"]}},"parameters":{}},"paths":{"/auth/mobile/google":{"post":{"operationId":"mobileGoogleSignIn","tags":["Auth"],"summary":"Exchange a Google identity token for a token pair (mobile)","description":"Registration is invite-only: a verified Google account that no organisation or ride has invited gets `forbidden`.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MobileGoogleSignIn"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenPair"}}}},"401":{"description":"Error codes: invalid_token","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"426":{"description":"Error codes: upgrade_required","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/auth/dev":{"post":{"operationId":"devSignIn","tags":["Auth"],"summary":"Development sign-in with an email address, no password","description":"Only when AUTH_DEV_LOGIN=1. Creates the user if needed. The API refuses to start with it on in production.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DevSignIn"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenPair"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/auth/refresh":{"post":{"operationId":"refresh","tags":["Auth"],"summary":"Rotate the refresh token and get a new access token","description":"A refresh token can be used once. Reuse of an old token ends the whole session chain.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenPair"}}}},"401":{"description":"Error codes: invalid_token, token_expired","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/auth/sign-out":{"post":{"operationId":"signOut","tags":["Auth"],"summary":"End this device session, or every session","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignOutRequest"}}}},"responses":{"204":{"description":"No content"},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/me":{"get":{"operationId":"getMe","tags":["Me"],"summary":"The signed-in user","security":[{"bearer":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateMe","tags":["Me"],"summary":"Update name and language","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMe"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"operationId":"deleteMe","tags":["Me"],"summary":"Mark the account for deletion; a job purges it after 7 days","security":[{"bearer":[]}],"responses":{"204":{"description":"No content"},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/me/devices":{"get":{"operationId":"listDevices","tags":["Me"],"summary":"Phones and browsers signed in for this user","security":[{"bearer":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceList"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/me/devices/current":{"put":{"operationId":"registerPushToken","tags":["Me"],"summary":"Set the push token of the calling device","description":"Phase 2: returns 501 until built.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceInfo"}}}},"responses":{"204":{"description":"No content"},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/me/export":{"get":{"operationId":"exportMyData","tags":["Me"],"summary":"A zip of all data held about the user","description":"Responds with application/zip.","security":[{"bearer":[]}],"responses":{"200":{"description":"Success"},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/orgs":{"get":{"operationId":"listOrgs","tags":["Organisations"],"summary":"Organisations the user belongs to","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","maxLength":512,"description":"Opaque cursor from a previous page"},"required":false,"description":"Opaque cursor from a previous page","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganisationList"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createOrg","tags":["Organisations"],"summary":"Create an organisation; the caller becomes its owner","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","minLength":8,"maxLength":128,"description":"Mobile writes send a random key; a retry with the same key returns the first result"},"required":false,"description":"Mobile writes send a random key; a retry with the same key returns the first result","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrganisation"}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Organisation"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/orgs/{orgId}":{"get":{"operationId":"getOrg","tags":["Organisations"],"summary":"One organisation","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"orgId","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Organisation"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/orgs/{orgId}/members":{"get":{"operationId":"listOrgMembers","tags":["Organisations"],"summary":"Members of an organisation","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"orgId","in":"path"},{"schema":{"type":"string","maxLength":512,"description":"Opaque cursor from a previous page"},"required":false,"description":"Opaque cursor from a previous page","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgMemberList"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"addOrgMember","tags":["Organisations"],"summary":"Invite a person into the organisation by email","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"orgId","in":"path"},{"schema":{"type":"string","minLength":8,"maxLength":128,"description":"Mobile writes send a random key; a retry with the same key returns the first result"},"required":false,"description":"Mobile writes send a random key; a retry with the same key returns the first result","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddOrgMember"}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgMember"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Error codes: already_member","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/orgs/{orgId}/members/{userId}":{"patch":{"operationId":"updateOrgMember","tags":["Organisations"],"summary":"Change a member role","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"orgId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"userId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrgMember"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrgMember"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Error codes: conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"operationId":"removeOrgMember","tags":["Organisations"],"summary":"Remove a member. The last owner cannot be removed","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"orgId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"userId","in":"path"}],"responses":{"204":{"description":"No content"},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Error codes: conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides":{"get":{"operationId":"listRides","tags":["Rides"],"summary":"Rides the user takes part in or manages","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","maxLength":512,"description":"Opaque cursor from a previous page"},"required":false,"description":"Opaque cursor from a previous page","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"required":false,"name":"limit","in":"query"},{"schema":{"$ref":"#/components/schemas/RideStatus"},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":false,"description":"Random UUID","name":"orgId","in":"query"},{"schema":{"type":["boolean","null"]},"required":false,"name":"upcoming","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RideList"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createRide","tags":["Rides"],"summary":"Create a ride; the caller becomes its Ride Admin","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","minLength":8,"maxLength":128,"description":"Mobile writes send a random key; a retry with the same key returns the first result"},"required":false,"description":"Mobile writes send a random key; a retry with the same key returns the first result","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRide"}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ride"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}":{"get":{"operationId":"getRide","tags":["Rides"],"summary":"One ride","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ride"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"operationId":"updateRide","tags":["Rides"],"summary":"Edit ride details or change its status","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRide"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ride"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Error codes: conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"operationId":"deleteRide","tags":["Rides"],"summary":"Delete a ride","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"}],"responses":{"204":{"description":"No content"},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/gpx":{"get":{"operationId":"getRideGpx","tags":["Rides"],"summary":"The current approved route as a GPX file","description":"Responds with application/gpx+xml.","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"}],"responses":{"200":{"description":"Success"},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/members":{"get":{"operationId":"listRideMembers","tags":["Participants"],"summary":"Participants of a ride","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","maxLength":512,"description":"Opaque cursor from a previous page"},"required":false,"description":"Opaque cursor from a previous page","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RideMemberList"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"addRideMember","tags":["Participants"],"summary":"Add a participant by email","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","minLength":8,"maxLength":128,"description":"Mobile writes send a random key; a retry with the same key returns the first result"},"required":false,"description":"Mobile writes send a random key; a retry with the same key returns the first result","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddRideMember"}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RideMember"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Error codes: already_member, ride_full","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/members/{userId}":{"patch":{"operationId":"updateRideMember","tags":["Participants"],"summary":"Assign roles or a group","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"userId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRideMember"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RideMember"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Error codes: conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"operationId":"removeRideMember","tags":["Participants"],"summary":"Remove a participant. A participant may remove themselves","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"userId","in":"path"}],"responses":{"204":{"description":"No content"},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Error codes: conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/me/attendance":{"put":{"operationId":"setMyAttendance","tags":["Participants"],"summary":"RSVP: going, maybe, not going","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","minLength":8,"maxLength":128,"description":"Mobile writes send a random key; a retry with the same key returns the first result"},"required":false,"description":"Mobile writes send a random key; a retry with the same key returns the first result","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetAttendance"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RideMember"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Error codes: ride_full","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/me/check-in":{"post":{"operationId":"checkIn","tags":["Participants"],"summary":"Tap to check in at the start","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","minLength":8,"maxLength":128,"description":"Mobile writes send a random key; a retry with the same key returns the first result"},"required":false,"description":"Mobile writes send a random key; a retry with the same key returns the first result","name":"Idempotency-Key","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RideMember"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Error codes: conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/groups":{"get":{"operationId":"listGroups","tags":["Participants"],"summary":"Named groups inside the ride","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupList"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createGroup","tags":["Participants"],"summary":"Create a group","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGroup"}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Group"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/groups/{groupId}":{"patch":{"operationId":"updateGroup","tags":["Participants"],"summary":"Rename a group or set its Road captain","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"groupId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGroup"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Group"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"operationId":"deleteGroup","tags":["Participants"],"summary":"Delete a group; its members become ungrouped","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"groupId","in":"path"}],"responses":{"204":{"description":"No content"},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/route-versions":{"get":{"operationId":"listRouteVersions","tags":["Route versions"],"summary":"History of route versions, newest first","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","maxLength":512,"description":"Opaque cursor from a previous page"},"required":false,"description":"Opaque cursor from a previous page","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteVersionList"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"proposeRouteVersion","tags":["Route versions"],"summary":"Save a route. Admins approve at once, planners create a proposal","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","minLength":8,"maxLength":128,"description":"Mobile writes send a random key; a retry with the same key returns the first result"},"required":false,"description":"Mobile writes send a random key; a retry with the same key returns the first result","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProposeRouteVersion"}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteVersion"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed, route_failed, route_out_of_coverage","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/route-versions/{versionId}":{"get":{"operationId":"getRouteVersion","tags":["Route versions"],"summary":"One version with stops and geometry","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"versionId","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteVersion"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/route-versions/{versionId}/approve":{"post":{"operationId":"approveRouteVersion","tags":["Route versions"],"summary":"Approve a proposal; it becomes the current route","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"versionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteVersionDecision"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteVersion"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Error codes: conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/route-versions/{versionId}/reject":{"post":{"operationId":"rejectRouteVersion","tags":["Route versions"],"summary":"Reject a proposal","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"versionId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteVersionDecision"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteVersion"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Error codes: conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/route-versions/{versionId}/restore":{"post":{"operationId":"restoreRouteVersion","tags":["Route versions"],"summary":"Make an old version current again, as a new version","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"versionId","in":"path"}],"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteVersion"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Error codes: conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/route-versions/{versionId}/compare/{otherId}":{"get":{"operationId":"compareRouteVersions","tags":["Route versions"],"summary":"Compare two versions","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"versionId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"otherId","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteVersionCompare"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/routes":{"post":{"operationId":"planRoute","tags":["Routing"],"summary":"Route request: waypoints and options in, geometry, time, distance and the motorway alternative out","description":"Responses are cached by a hash of waypoints, options and graph version.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteRequest"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RouteResult"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed, route_failed, route_out_of_coverage","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/invitations":{"get":{"operationId":"listInvitations","tags":["Invitations"],"summary":"Invitations of a ride","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationList"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createInvitation","tags":["Invitations"],"summary":"Create an invite link or an email invitation","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","minLength":8,"maxLength":128,"description":"Mobile writes send a random key; a retry with the same key returns the first result"},"required":false,"description":"Mobile writes send a random key; a retry with the same key returns the first result","name":"Idempotency-Key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInvitation"}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedInvitation"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/invitations/{invitationId}":{"delete":{"operationId":"revokeInvitation","tags":["Invitations"],"summary":"Revoke an invitation","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"invitationId","in":"path"}],"responses":{"204":{"description":"No content"},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/invitations/{token}":{"get":{"operationId":"previewInvitation","tags":["Invitations"],"summary":"What an invitation is for, before signing in","security":[],"parameters":[{"schema":{"type":"string","minLength":16,"maxLength":128},"required":true,"name":"token","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvitationPreview"}}}},"404":{"description":"Error codes: invitation_invalid","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"410":{"description":"Error codes: invitation_expired","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/invitations/{token}/accept":{"post":{"operationId":"acceptInvitation","tags":["Invitations"],"summary":"Accept an invitation. A link that needs approval creates a join request","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","minLength":16,"maxLength":128},"required":true,"name":"token","in":"path"},{"schema":{"type":"string","minLength":8,"maxLength":128,"description":"Mobile writes send a random key; a retry with the same key returns the first result"},"required":false,"description":"Mobile writes send a random key; a retry with the same key returns the first result","name":"Idempotency-Key","in":"header"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptInvitationResult"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: invitation_invalid","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Error codes: ride_full, already_member","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"410":{"description":"Error codes: invitation_expired","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/join-requests":{"get":{"operationId":"listJoinRequests","tags":["Invitations"],"summary":"People waiting for approval","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinRequestList"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/join-requests/{userId}/approve":{"post":{"operationId":"approveJoinRequest","tags":["Invitations"],"summary":"Approve a join request","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"userId","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RideMember"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Error codes: ride_full, conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/rides/{rideId}/join-requests/{userId}/reject":{"post":{"operationId":"rejectJoinRequest","tags":["Invitations"],"summary":"Reject a join request","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"rideId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Random UUID"},"required":true,"description":"Random UUID","name":"userId","in":"path"}],"responses":{"204":{"description":"No content"},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Error codes: forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"Error codes: not_found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"409":{"description":"Error codes: conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/places":{"get":{"operationId":"listPlaces","tags":["Places"],"summary":"Places by area and zoom, by category, or by text","description":"For map drawing prefer the places tile file; this endpoint serves search and lists.","security":[{"bearer":[]}],"parameters":[{"schema":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?,-?\\d+(\\.\\d+)?,-?\\d+(\\.\\d+)?,-?\\d+(\\.\\d+)?$","description":"west,south,east,north"},"required":false,"description":"west,south,east,north","name":"bbox","in":"query"},{"schema":{"type":["number","null"],"minimum":0,"maximum":22},"required":false,"name":"zoom","in":"query"},{"schema":{"type":"string","description":"Comma-separated place categories"},"required":false,"description":"Comma-separated place categories","name":"category","in":"query"},{"schema":{"type":"string","minLength":2,"maxLength":80,"description":"Text search on the name"},"required":false,"description":"Text search on the name","name":"q","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":200},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceList"}}}},"401":{"description":"Error codes: unauthenticated","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"Error codes: validation_failed","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Error codes: rate_limited","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"500":{"description":"Error codes: internal","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"webhooks":{}}