{"externalDocs":{"description":"Inbloom Wiki","url":"https://wiki.inbloom.one"},"info":{"description":"The API for Inbloom mobile app. All endpoints require Bearer authentication using Supabase JWT tokens.","title":"Inbloom API","version":"1.0.0"},"openapi":"3.0.0","security":[{"bearerAuth":[]}],"servers":[{"description":"Local development","url":"http://localhost:8080"},{"description":"Production Landscape","url":"http://api.inbloom.one"}],"tags":[{"description":"User profile management - create, update, and view profiles","name":"Profiles"},{"description":"Friend requests, blocking, and friendship management","name":"Friends"},{"description":"Event creation, discovery, and lifecycle management","name":"Events"},{"description":"Like groups for event attendance and approval workflow","name":"Groups"},{"description":"Chat conversations and message history","name":"Chats"},{"description":"Signed upload and download URLs for the private media bucket","name":"Media"},{"description":"People discovery, interactions, location sharing, blocking, and reports","name":"People"},{"description":"In-app notifications; new ones also arrive over Supabase Realtime","name":"Notifications"},{"description":"Push token registration; every in-app notification is also pushed to registered devices","name":"Push"},{"description":"Complaints against users, events, and chats","name":"Reports"}],"components":{"securitySchemes":{"bearerAuth":{"bearerFormat":"JWT","description":"Supabase JWT access token from user authentication","scheme":"bearer","type":"http"}},"schemas":{"OwnProfileResponse":{"allOf":[{"$ref":"#/components/schemas/ProfileResponse"},{"type":"object","properties":{"currentTerms":{"$ref":"#/components/schemas/Terms"},"missingFields":{"type":"array","items":{"type":"string"},"description":"Answers still required to complete","example":["sex","height"]},"termsOutdated":{"type":"boolean","description":"Whether a document was re-published since acceptance","example":false}},"required":["currentTerms","missingFields","termsOutdated"]}]},"ProfileEducation":{"type":"object","properties":{"inProgress":{"type":"boolean","description":"Whether the education is currently in progress","example":false},"level":{"type":"string","enum":["basic_general","average_general","secondary_special","bachelor","specialty","master","postgraduate"],"description":"Education level","example":"bachelor"}},"required":["inProgress","level"]},"ProfileLocation":{"type":"object","nullable":true,"properties":{"latitude":{"type":"number","description":"Latitude","example":43.238},"longitude":{"type":"number","description":"Longitude","example":76.945}},"required":["latitude","longitude"],"description":"Approximate position, snapped to a ~500 m grid. On your own profile it is the exact point you set."},"ProfilePhoto":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Photo ID"},"isAvatar":{"type":"boolean","description":"Whether this is the photo that stands for the profile in feeds, chats and rosters. Set it with `avatarPhotoId` on PATCH /profiles; at most one photo carries it.","example":false},"position":{"type":"integer","description":"Gallery order; unrelated to the avatar","example":0},"thumbUrl":{"type":"string","nullable":true,"description":"Signed URL of a 256×256 crop — use it for lists and rosters. Falls back to the full-size URL for photos stored before previews existed."},"url":{"type":"string","nullable":true,"description":"Signed download URL, or null when the object is no longer in the bucket"}},"required":["id","isAvatar","position","thumbUrl","url"]},"Terms":{"type":"object","properties":{"privacyPolicyVersion":{"type":"string","description":"Published privacy policy version","example":"1"},"userAgreementVersion":{"type":"string","description":"Published user agreement version","example":"1"}},"required":["privacyPolicyVersion","userAgreementVersion"],"description":"Versions the user has to accept"},"ProfileResponse":{"type":"object","properties":{"alcoholAttitude":{"type":"string","nullable":true,"enum":["positive","neutral","negative",null],"description":"Attitude towards alcohol","example":"neutral"},"avatarPhotoId":{"type":"string","nullable":true,"format":"uuid","description":"Photo chosen as the avatar; null when none was picked"},"bio":{"type":"string","nullable":true,"description":"User biography","example":"Love hiking and coffee"},"birthdate":{"type":"string","nullable":true,"description":"Date of birth (YYYY-MM-DD)","example":"1995-06-15"},"bodyType":{"type":"string","nullable":true,"enum":["slim","normal","fit","sportive","muscular","stocky","chubby",null],"description":"Body type","example":"fit"},"city":{"type":"string","nullable":true,"description":"Current city","example":"Алматы"},"datingPurpose":{"type":"string","nullable":true,"enum":["dating","relationship","friendship","chatting",null],"description":"Dating purpose. Null on someone else's profile when they set `showDatingPurpose: false` — the value is withheld, not just flagged.","example":"relationship"},"educations":{"type":"array","items":{"$ref":"#/components/schemas/ProfileEducation"},"description":"Education history"},"familyPlan":{"type":"string","nullable":true,"enum":["no-dont-want","no-not-soon","no-maybe-in-future","no-but-want-to","already-have",null],"description":"Family plans","example":"no-maybe-in-future"},"financialState":{"type":"string","nullable":true,"enum":["unemployed","studying","irregular-income","low-income","middle-income","wealthy",null],"description":"Financial state","example":"middle-income"},"firstName":{"type":"string","description":"First name","example":"Alex"},"height":{"type":"number","nullable":true,"description":"Height in centimeters","example":175},"hometown":{"type":"string","nullable":true,"description":"Hometown","example":"Berlin"},"id":{"type":"string","format":"uuid","description":"Profile ID"},"isCompleted":{"type":"boolean","description":"Whether onboarding is finished","example":true},"isVisibleOnMap":{"type":"boolean","description":"Whether this person appears on the map and may be watched live. Off unless turned on.","example":false},"languages":{"type":"array","items":{"type":"string","enum":["ru","en","de","fr","es","it","pt","tr","ar","zh","ja","ko","uk","be","kk","uz","az","hy","ka","ky"]},"description":"Languages spoken","example":["en","de"]},"location":{"$ref":"#/components/schemas/ProfileLocation"},"photos":{"type":"array","items":{"$ref":"#/components/schemas/ProfilePhoto"},"description":"Photos in gallery order; the one flagged `isAvatar` stands for the profile elsewhere. URLs are signed and expire in an hour."},"religion":{"type":"string","nullable":true,"enum":["catholicism","orthodoxy","islam","protestantism","buddhism","hinduism","atheism","agnosticism","judaism",null],"description":"Religion","example":"agnosticism"},"sex":{"type":"string","nullable":true,"enum":["male","female",null],"description":"Sex","example":"male"},"showDatingPurpose":{"type":"boolean","description":"Whether to show dating purpose publicly","example":true},"showZodiac":{"type":"boolean","description":"Whether to show zodiac sign publicly","example":false},"smokingAttitude":{"type":"string","nullable":true,"enum":["positive","neutral","negative",null],"description":"Attitude towards smoking","example":"negative"},"updatedAt":{"type":"string","format":"date-time","description":"When the profile last changed, including its photos, educations and languages. Use it to expire a cached copy."},"userId":{"type":"string","format":"uuid","description":"User ID"},"zodiac":{"type":"string","nullable":true,"enum":["aquarius","pisces","aries","taurus","gemini","cancer","leo","virgo","libra","scorpio","sagittarius","capricorn",null],"description":"Zodiac sign, derived from the birth date","example":"capricorn"}},"required":["alcoholAttitude","avatarPhotoId","bio","birthdate","bodyType","city","datingPurpose","educations","familyPlan","financialState","firstName","height","hometown","id","isCompleted","isVisibleOnMap","languages","location","photos","religion","sex","showDatingPurpose","showZodiac","smokingAttitude","updatedAt","userId","zodiac"]},"ErrorResponse":{"type":"object","properties":{"error":{"type":"string","example":"Unauthorized"},"errorId":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"}},"required":["error"]},"ProfilePhotoList":{"type":"array","items":{"$ref":"#/components/schemas/ProfilePhoto"}},"ProfilePhotoRef":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Photo ID"},"isAvatar":{"type":"boolean","description":"True when this was the first photo, which becomes the avatar","example":false},"position":{"type":"integer","description":"Gallery order; unrelated to the avatar","example":1}},"required":["id","isAvatar","position"]},"ValidationErrorResponse":{"type":"object","properties":{"error":{"type":"string","enum":["Validation has failed"]},"fields":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","example":"email"},"message":{"type":"string","example":"Invalid email format"}},"required":["field","message"]}}},"required":["error","fields"]},"AddPhotoRequest":{"type":"object","properties":{"key":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[0-9a-f-]{36}\\/[0-9a-f-]{36}$","description":"Storage key handed back by POST /media/upload-url, shaped `{userId}/{uuid}`","example":"550e8400-e29b-41d4-a716-446655440000/6ba7b810-9dad-11d1-80b4-00c04fd430c8"}},"required":["key"]},"ReorderPhotosResponse":{"type":"object","properties":{"order":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Photo ids in their new order"}},"required":["order"]},"ReorderPhotosRequest":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"description":"Every photo of the profile, exactly once, in the new order"}},"required":["ids"]},"PhotoDeletedResponse":{"type":"object","properties":{"deleted":{"type":"boolean","example":true}},"required":["deleted"]},"BatchProfilesResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ProfileResponse"},"description":"Profiles that exist, in no particular order; match them on userId"}},"required":["data"]},"BatchProfilesRequest":{"type":"object","properties":{"userIds":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":100,"description":"User ids to look up (max 100)"}},"required":["userIds"]},"CreateProfileRequest":{"type":"object","properties":{"alcoholAttitude":{"type":"string","enum":["positive","neutral","negative"],"description":"Attitude towards alcohol","example":"neutral"},"bio":{"type":"string","nullable":true,"description":"User biography","example":"Love hiking and coffee"},"birthdate":{"type":"string","nullable":true,"format":"date-time","description":"Date of birth","example":"1995-06-15"},"bodyType":{"type":"string","enum":["slim","normal","fit","sportive","muscular","stocky","chubby"],"description":"Body type","example":"fit"},"city":{"type":"string","maxLength":120,"description":"Current city","example":"Алматы"},"datingPurpose":{"type":"string","enum":["dating","relationship","friendship","chatting"],"description":"Dating purpose","example":"relationship"},"educations":{"type":"array","items":{"$ref":"#/components/schemas/ProfileEducation"},"default":[],"description":"Education history"},"familyPlan":{"type":"string","enum":["no-dont-want","no-not-soon","no-maybe-in-future","no-but-want-to","already-have"],"description":"Family plans","example":"no-maybe-in-future"},"financialState":{"type":"string","enum":["unemployed","studying","irregular-income","low-income","middle-income","wealthy"],"description":"Financial state","example":"middle-income"},"firstName":{"type":"string","minLength":1,"description":"First name","example":"Alex"},"height":{"type":"number","minimum":55,"maximum":272,"description":"Height in cm (55-272)","example":175},"hometown":{"type":"string","description":"Hometown","example":"Berlin"},"isVisibleOnMap":{"type":"boolean","default":false,"description":"Appear on the map, and let others watch your live position while sharing is on","example":false},"languages":{"type":"array","items":{"type":"string","enum":["ru","en","de","fr","es","it","pt","tr","ar","zh","ja","ko","uk","be","kk","uz","az","hy","ka","ky"]},"default":[],"description":"Languages spoken","example":["en","de"]},"latitude":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude; must be sent together with longitude","example":43.238},"longitude":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude; must be sent together with latitude","example":76.945},"religion":{"type":"string","enum":["catholicism","orthodoxy","islam","protestantism","buddhism","hinduism","atheism","agnosticism","judaism"],"description":"Religion","example":"agnosticism"},"sex":{"type":"string","enum":["male","female"],"description":"Sex","example":"male"},"showDatingPurpose":{"type":"boolean","default":false,"description":"Whether to show dating purpose publicly","example":true},"showZodiac":{"type":"boolean","default":false,"description":"Whether to show zodiac sign publicly","example":false},"smokingAttitude":{"type":"string","enum":["positive","neutral","negative"],"description":"Attitude towards smoking","example":"negative"}},"required":["firstName"]},"UpdateProfileRequest":{"type":"object","properties":{"alcoholAttitude":{"type":"string","enum":["positive","neutral","negative"],"description":"Attitude towards alcohol"},"avatarPhotoId":{"type":"string","nullable":true,"format":"uuid","description":"Which of your photos stands for the profile in feeds, chats and rosters. Any photo qualifies, whatever its position. Null clears the choice and readers fall back to the first photo."},"bio":{"type":"string","nullable":true,"description":"User biography"},"birthdate":{"type":"string","nullable":true,"format":"date-time","description":"Date of birth"},"bodyType":{"type":"string","enum":["slim","normal","fit","sportive","muscular","stocky","chubby"],"description":"Body type"},"city":{"type":"string","maxLength":120,"description":"Current city","example":"Алматы"},"datingPurpose":{"type":"string","enum":["dating","relationship","friendship","chatting"],"description":"Dating purpose"},"educations":{"type":"array","items":{"$ref":"#/components/schemas/ProfileEducation"},"description":"Education history; replaces the whole list. Send [] to clear it."},"familyPlan":{"type":"string","enum":["no-dont-want","no-not-soon","no-maybe-in-future","no-but-want-to","already-have"],"description":"Family plans"},"financialState":{"type":"string","enum":["unemployed","studying","irregular-income","low-income","middle-income","wealthy"],"description":"Financial state"},"firstName":{"type":"string","description":"First name"},"height":{"type":"number","minimum":55,"maximum":272,"description":"Height in cm (55-272)"},"hometown":{"type":"string","description":"Hometown"},"isVisibleOnMap":{"type":"boolean","description":"Appear on the map, and let others watch your live position while sharing is on"},"languages":{"type":"array","items":{"type":"string","enum":["ru","en","de","fr","es","it","pt","tr","ar","zh","ja","ko","uk","be","kk","uz","az","hy","ka","ky"]},"description":"Languages spoken; replaces the whole list. Send [] to clear it.","example":["en"]},"latitude":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude; must be sent together with longitude","example":43.238},"longitude":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude; must be sent together with latitude","example":76.945},"religion":{"type":"string","enum":["catholicism","orthodoxy","islam","protestantism","buddhism","hinduism","atheism","agnosticism","judaism"],"description":"Religion"},"sex":{"type":"string","enum":["male","female"],"description":"Sex"},"showDatingPurpose":{"type":"boolean","description":"Whether to show dating purpose publicly"},"showZodiac":{"type":"boolean","description":"Whether to show zodiac sign publicly"},"smokingAttitude":{"type":"string","enum":["positive","neutral","negative"],"description":"Attitude towards smoking"}}},"CompleteProfileResponse":{"type":"object","properties":{"acceptedTerms":{"type":"object","properties":{"privacyPolicyVersion":{"type":"string","description":"Published privacy policy version","example":"1"},"userAgreementVersion":{"type":"string","description":"Published user agreement version","example":"1"}}},"isCompleted":{"type":"boolean","example":true},"missingFields":{"type":"array","items":{"type":"string"},"example":[]}},"required":["acceptedTerms","isCompleted","missingFields"]},"CompleteProfileRequest":{"type":"object","properties":{"privacyPolicyVersion":{"type":"string","description":"Published privacy policy version","example":"1"},"userAgreementVersion":{"type":"string","description":"Published user agreement version","example":"1"}}},"SuccessResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true}},"required":["success"]},"ListFriendshipsResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/FriendshipResponse"}},"nextCursor":{"type":"string","nullable":true,"example":"eyJpZCI6IjEyMyIsImNyZWF0ZWRBdCI6IjIwMjQtMDEtMDFUMDA6MDA6MDBaIn0"}},"required":["data","nextCursor"]},"FriendshipResponse":{"type":"object","properties":{"addresseeId":{"type":"string","format":"uuid","description":"ID of the user who received the friend request"},"blockedBy":{"type":"string","nullable":true,"format":"uuid","description":"ID of user who blocked (if blocked)"},"createdAt":{"type":"string","format":"date-time","description":"Friendship creation timestamp"},"id":{"type":"string","format":"uuid","description":"Friendship ID"},"requesterId":{"type":"string","format":"uuid","description":"ID of the user who sent the friend request"},"status":{"type":"string","enum":["pending","accepted","rejected","blocked"],"description":"Current friendship status","example":"accepted"},"updatedAt":{"type":"string","format":"date-time","description":"Last update timestamp"}},"required":["addresseeId","blockedBy","createdAt","id","requesterId","status","updatedAt"]},"FriendRequestResult":{"type":"object","properties":{"friendship":{"$ref":"#/components/schemas/FriendshipResponse"},"result":{"type":"string","enum":["created","already_pending","already_friends"],"description":"What asking did: `created` a new request, or found one `already_pending`, or found you `already_friends`. The last two write nothing.","example":"created"}},"required":["friendship","result"]},"SendFriendRequestBody":{"type":"object","properties":{"addresseeId":{"type":"string","format":"uuid","description":"ID of the user to send friend request to"}},"required":["addresseeId"]},"EventFeedResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EventFeedItem"}},"nextCursor":{"type":"string","nullable":true,"example":"eyJpZCI6IjEyMyIsImNyZWF0ZWRBdCI6IjIwMjQtMDEtMDFUMDA6MDA6MDBaIn0"}},"required":["data","nextCursor"]},"EventFeedItem":{"allOf":[{"$ref":"#/components/schemas/EventResponse"},{"type":"object","properties":{"creatorAvatarUrl":{"type":"string","nullable":true,"description":"Signed URL of the creator's avatar preview; null without a profile photo"},"creatorName":{"type":"string","nullable":true,"description":"Display name of the creator; null if they have no profile","example":"Anna"},"distance":{"type":"number","description":"Distance from query location in meters","example":1500},"femaleOrganizerCount":{"type":"integer","description":"Organizers whose profile says female. This is what femaleFrom/femaleTo filter on.","example":2},"guestCount":{"type":"integer","description":"Everyone admitted through an approved like group — the crowd, not the hosts","example":7},"maleOrganizerCount":{"type":"integer","description":"Organizers whose profile says male. This is what maleFrom/maleTo filter on.","example":1},"organizerAvatarUrls":{"type":"array","items":{"type":"string"},"description":"Avatar previews of the organizers, at most 5: the creator first, then co-authors in the order they were invited. Organizers without a photo are absent, so this can be shorter than organizerCount."},"organizerCount":{"type":"integer","description":"Creator plus co-authors who accepted","example":3}},"required":["creatorAvatarUrl","creatorName","distance","femaleOrganizerCount","guestCount","maleOrganizerCount","organizerAvatarUrls","organizerCount"]}]},"Location":{"type":"object","properties":{"address":{"type":"string","nullable":true,"description":"Street address","example":"Невский проспект, 1"},"city":{"type":"string","nullable":true,"description":"City","example":"Санкт-Петербург"},"latitude":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude coordinate","example":52.52},"longitude":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude coordinate","example":13.405}},"required":["address","city","latitude","longitude"],"description":"Event location coordinates"},"EventResponse":{"type":"object","properties":{"capacity":{"type":"number","description":"Maximum number of attendees","example":50},"consentThreshold":{"type":"number","description":"Deprecated and no longer read: the consents a like group needs are derived from its own size — T = min(S, N), S being the people in the like and N the event's organizers. Kept so existing clients keep working.","example":2},"createdAt":{"type":"string","format":"date-time","description":"Event creation timestamp"},"creator":{"type":"object","nullable":true,"properties":{"avatarUrl":{"type":"string","nullable":true,"description":"Signed URL of their avatar preview"},"name":{"type":"string"},"profileId":{"type":"string","format":"uuid"},"userId":{"type":"string","format":"uuid"}},"required":["avatarUrl","name","profileId","userId"],"description":"Who made the event; null when they have no profile"},"creatorId":{"type":"string","format":"uuid","description":"ID of the event creator"},"deletedAt":{"type":"string","nullable":true,"format":"date-time","description":"Soft deletion timestamp"},"description":{"type":"string","nullable":true,"description":"Event description","example":"Join us for an amazing party!"},"effectiveStatus":{"type":"string","enum":["draft","published","started","finished","cancelled"],"description":"Computed status based on time","example":"published"},"endAt":{"type":"string","nullable":true,"format":"date-time","description":"Event end time"},"id":{"type":"string","format":"uuid","description":"Event ID"},"imageThumbUrl":{"type":"string","nullable":true,"description":"Signed URL of an 800×450 crop of the cover — use it for feed cards. Falls back to the full-size URL when no preview exists."},"imageUrl":{"type":"string","nullable":true,"description":"Signed URL of the cover image, valid for an hour. For events created before covers were uploadable this is whatever external URL the client stored."},"location":{"$ref":"#/components/schemas/Location"},"startAt":{"type":"string","format":"date-time","description":"Event start time"},"status":{"type":"string","enum":["draft","published","started","finished","cancelled"],"description":"Stored event status","example":"published"},"title":{"type":"string","description":"Event title","example":"Friday Night Party"},"type":{"type":"string","enum":["concert_musician","concert_stand_up","concert_symphony","concert_jazz","concert_acoustic","concert_rock","concert_electronic","concert_street_musicians","concert_organ","concert_local_festival","concert_lit_music","exhibition_art","exhibition_museum","exhibition_modern_art","exhibition_photography","exhibition_science","exhibition_history","exhibition_fair","exhibition_interactive","exhibition_books","exhibition_flowers","exhibition_sculptures","party_repost","party_private","party_theme","party_home","party_birthday","party_after","party_pool","party_club_foam","party_night_walk","party_game_night","club_dance","club_theme","club_karaoke","club_dj","club_techno","club_stand_up","club_art","club_salsa","club_mafia","bar_board_games","bar_quiz","bar_live_music","bar_theme_night","bar_karaoke","bar_drink_tasting","bar_lounge","bar_darts","cinema_premiere","cinema_outdoor","cinema_author","cinema_retro","cinema_universe","cinema_shorts","cinema_immersive","cinema_marathon","cinema_family","theatre_play","theatre_musical","theatre_improv","theatre_puppet","theatre_experimental","theatre_street","theatre_radio","theatre_shadow","theatre_improvisation","festival_music","festival_cinema","festival_gastro","festival_theatre","festival_crafts","festival_sports","festival_ballet","festival_medieval","festival_ethnic","festival_tech","sport_match","sport_esports","sport_race","sport_yoga","sport_dance","sport_fanzone","sport_sup","sport_battle","sport_cycling","nature_hiking","nature_photo_safari","nature_star_gazing","nature_picnic","nature_cycling","nature_waterfalls","nature_yoga","nature_farm_tour","workshop_ceramics","workshop_calligraphy","workshop_cooking","workshop_sketching","children_science","children_quests","children_cartoons","children_pets","quest_horror","quest_city","quest_orienteering","quest_vr","education_lectures","education_meetings","education_history","education_photo","immersive_escape","immersive_theatre","immersive_tours","immersive_roleplay","art_light","art_open_workshops","art_sound","art_performances","undefined","other"],"description":"Event type","example":"party"},"typeLabel":{"type":"string","description":"Human-readable event type label","example":"Concert Acoustic"},"updatedAt":{"type":"string","format":"date-time","description":"Last update timestamp"},"visibility":{"type":"string","enum":["public","private"],"description":"Event visibility","example":"public"}},"required":["capacity","consentThreshold","createdAt","creator","creatorId","deletedAt","description","effectiveStatus","endAt","id","imageThumbUrl","imageUrl","location","startAt","status","title","type","typeLabel","updatedAt","visibility"]},"CreateEventRequest":{"type":"object","properties":{"address":{"type":"string","maxLength":255,"description":"Street address shown on the card","example":"Невский проспект, 1"},"capacity":{"type":"integer","minimum":1,"maximum":10000,"default":200,"description":"Maximum attendees (1-10000)","example":50},"city":{"type":"string","maxLength":120,"description":"City","example":"Санкт-Петербург"},"consentThreshold":{"type":"integer","minimum":1,"description":"Deprecated and no longer read: the consents a like group needs are derived from its own size — T = min(S, N), S being the people in the like and N the event's organizers. Kept so existing clients keep working."},"description":{"type":"string","maxLength":2000,"description":"Event description (max 2000 chars)"},"endAt":{"type":"string","format":"date-time","description":"Event end time (ISO 8601)"},"imageKey":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[0-9a-f-]{36}\\/[0-9a-f-]{36}$","description":"Storage key of an already-uploaded cover, from POST /media/upload-url. Must be an image under your own prefix."},"imageUrl":{"type":"string","format":"uri","description":"Deprecated: external cover URL. Upload the cover and pass `imageKey` instead."},"latitude":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude coordinate","example":52.52},"longitude":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude coordinate","example":13.405},"startAt":{"type":"string","format":"date-time","description":"Event start time (ISO 8601)"},"title":{"type":"string","minLength":1,"maxLength":100,"description":"Event title (max 100 chars)","example":"Friday Night Party"},"type":{"type":"string","enum":["concert_musician","concert_stand_up","concert_symphony","concert_jazz","concert_acoustic","concert_rock","concert_electronic","concert_street_musicians","concert_organ","concert_local_festival","concert_lit_music","exhibition_art","exhibition_museum","exhibition_modern_art","exhibition_photography","exhibition_science","exhibition_history","exhibition_fair","exhibition_interactive","exhibition_books","exhibition_flowers","exhibition_sculptures","party_repost","party_private","party_theme","party_home","party_birthday","party_after","party_pool","party_club_foam","party_night_walk","party_game_night","club_dance","club_theme","club_karaoke","club_dj","club_techno","club_stand_up","club_art","club_salsa","club_mafia","bar_board_games","bar_quiz","bar_live_music","bar_theme_night","bar_karaoke","bar_drink_tasting","bar_lounge","bar_darts","cinema_premiere","cinema_outdoor","cinema_author","cinema_retro","cinema_universe","cinema_shorts","cinema_immersive","cinema_marathon","cinema_family","theatre_play","theatre_musical","theatre_improv","theatre_puppet","theatre_experimental","theatre_street","theatre_radio","theatre_shadow","theatre_improvisation","festival_music","festival_cinema","festival_gastro","festival_theatre","festival_crafts","festival_sports","festival_ballet","festival_medieval","festival_ethnic","festival_tech","sport_match","sport_esports","sport_race","sport_yoga","sport_dance","sport_fanzone","sport_sup","sport_battle","sport_cycling","nature_hiking","nature_photo_safari","nature_star_gazing","nature_picnic","nature_cycling","nature_waterfalls","nature_yoga","nature_farm_tour","workshop_ceramics","workshop_calligraphy","workshop_cooking","workshop_sketching","children_science","children_quests","children_cartoons","children_pets","quest_horror","quest_city","quest_orienteering","quest_vr","education_lectures","education_meetings","education_history","education_photo","immersive_escape","immersive_theatre","immersive_tours","immersive_roleplay","art_light","art_open_workshops","art_sound","art_performances","undefined","other"],"description":"Event type","example":"party"},"visibility":{"type":"string","enum":["public","private"],"default":"public","description":"Event visibility","example":"public"}},"required":["consentThreshold","latitude","longitude","startAt","title","type"]},"MyEventsResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EventResponse"},"description":"The caller's own events, newest first"}},"required":["data"]},"UpdateEventRequest":{"type":"object","properties":{"address":{"type":"string","nullable":true,"maxLength":255,"description":"Street address"},"capacity":{"type":"integer","minimum":1,"maximum":10000,"description":"Maximum attendees"},"city":{"type":"string","nullable":true,"maxLength":120,"description":"City"},"consentThreshold":{"type":"integer","minimum":1,"description":"Deprecated and no longer read: the consents a like group needs are derived from its own size — T = min(S, N), S being the people in the like and N the event's organizers. Kept so existing clients keep working."},"description":{"type":"string","nullable":true,"maxLength":2000,"description":"Event description"},"endAt":{"type":"string","nullable":true,"format":"date-time","description":"Event end time"},"imageKey":{"type":"string","nullable":true,"minLength":1,"maxLength":255,"pattern":"^[0-9a-f-]{36}\\/[0-9a-f-]{36}$","description":"Storage key of an already-uploaded cover, from POST /media/upload-url; null clears the cover. Same effect as PUT/DELETE /events/{eventId}/cover."},"imageUrl":{"type":"string","nullable":true,"format":"uri","description":"Deprecated: external cover URL. Use `imageKey` for an uploaded cover."},"latitude":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude coordinate"},"longitude":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude coordinate"},"startAt":{"type":"string","format":"date-time","description":"Event start time"},"title":{"type":"string","minLength":1,"maxLength":100,"description":"Event title"},"type":{"type":"string","enum":["concert_musician","concert_stand_up","concert_symphony","concert_jazz","concert_acoustic","concert_rock","concert_electronic","concert_street_musicians","concert_organ","concert_local_festival","concert_lit_music","exhibition_art","exhibition_museum","exhibition_modern_art","exhibition_photography","exhibition_science","exhibition_history","exhibition_fair","exhibition_interactive","exhibition_books","exhibition_flowers","exhibition_sculptures","party_repost","party_private","party_theme","party_home","party_birthday","party_after","party_pool","party_club_foam","party_night_walk","party_game_night","club_dance","club_theme","club_karaoke","club_dj","club_techno","club_stand_up","club_art","club_salsa","club_mafia","bar_board_games","bar_quiz","bar_live_music","bar_theme_night","bar_karaoke","bar_drink_tasting","bar_lounge","bar_darts","cinema_premiere","cinema_outdoor","cinema_author","cinema_retro","cinema_universe","cinema_shorts","cinema_immersive","cinema_marathon","cinema_family","theatre_play","theatre_musical","theatre_improv","theatre_puppet","theatre_experimental","theatre_street","theatre_radio","theatre_shadow","theatre_improvisation","festival_music","festival_cinema","festival_gastro","festival_theatre","festival_crafts","festival_sports","festival_ballet","festival_medieval","festival_ethnic","festival_tech","sport_match","sport_esports","sport_race","sport_yoga","sport_dance","sport_fanzone","sport_sup","sport_battle","sport_cycling","nature_hiking","nature_photo_safari","nature_star_gazing","nature_picnic","nature_cycling","nature_waterfalls","nature_yoga","nature_farm_tour","workshop_ceramics","workshop_calligraphy","workshop_cooking","workshop_sketching","children_science","children_quests","children_cartoons","children_pets","quest_horror","quest_city","quest_orienteering","quest_vr","education_lectures","education_meetings","education_history","education_photo","immersive_escape","immersive_theatre","immersive_tours","immersive_roleplay","art_light","art_open_workshops","art_sound","art_performances","undefined","other"],"description":"Event type"},"visibility":{"type":"string","enum":["public","private"],"description":"Event visibility"}}},"SetEventCoverRequest":{"type":"object","properties":{"key":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[0-9a-f-]{36}\\/[0-9a-f-]{36}$","description":"Storage key of the uploaded image, under your own prefix"}},"required":["key"]},"EventAuthorResponse":{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time","description":"When the author was invited"},"eventId":{"type":"string","format":"uuid","description":"Event ID"},"status":{"type":"string","enum":["pending","accepted","refused"],"description":"Invitation status. Only `accepted` co-authors count as organizers for quorum, voting and the chat.","example":"pending"},"userId":{"type":"string","format":"uuid","description":"Author user ID"}},"required":["createdAt","eventId","status","userId"]},"AddAuthorRequest":{"type":"object","properties":{"userId":{"type":"string","format":"uuid","description":"User ID to add as co-author"}},"required":["userId"]},"AuthorInviteDecision":{"type":"object","properties":{"eventId":{"type":"string","format":"uuid","description":"Event ID"},"status":{"type":"string","enum":["pending","accepted","refused"],"description":"Resulting invitation status"},"userId":{"type":"string","format":"uuid","description":"The invited user"}},"required":["eventId","status","userId"]},"EventAuthorsList":{"type":"object","properties":{"authors":{"type":"array","items":{"$ref":"#/components/schemas/EventAuthorResponse"},"description":"List of co-authors"},"creatorId":{"type":"string","format":"uuid","description":"Event creator ID"}},"required":["authors","creatorId"]},"LikeGroupResponse":{"type":"object","properties":{"chatId":{"type":"string","nullable":true,"format":"uuid","description":"Chat ID (created when approved)"},"createdAt":{"type":"string","format":"date-time","description":"Group creation timestamp"},"creatorId":{"type":"string","format":"uuid","description":"User who created the group"},"eventId":{"type":"string","format":"uuid","description":"Associated event ID"},"id":{"type":"string","format":"uuid","description":"Like group ID"},"memberIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"IDs of group members"},"status":{"type":"string","enum":["waiting","pending","approved","rejected","expired"],"description":"Group status","example":"pending"}},"required":["chatId","createdAt","creatorId","eventId","id","memberIds","status"]},"CreateLikeGroupRequest":{"type":"object","properties":{"memberIds":{"type":"array","items":{"type":"string","format":"uuid"},"maxItems":9,"description":"Friend IDs to include in the group"}}},"LikeGroupsListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LikeGroupResponse"}},"nextCursor":{"type":"string","nullable":true,"example":"eyJpZCI6IjEyMyIsImNyZWF0ZWRBdCI6IjIwMjQtMDEtMDFUMDA6MDA6MDBaIn0"}},"required":["data","nextCursor"]},"ListGroupsResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LikeGroupResponse"}},"nextCursor":{"type":"string","nullable":true,"example":"eyJpZCI6IjEyMyIsImNyZWF0ZWRBdCI6IjIwMjQtMDEtMDFUMDA6MDA6MDBaIn0"}},"required":["data","nextCursor"]},"GroupMemberResponse":{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time","description":"When the member was added"},"groupId":{"type":"string","format":"uuid","description":"Group ID"},"userId":{"type":"string","format":"uuid","description":"Member user ID"}},"required":["createdAt","groupId","userId"]},"AddMemberRequest":{"type":"object","properties":{"userId":{"type":"string","format":"uuid","description":"User ID to add to the group"}},"required":["userId"]},"GroupApprovalsList":{"type":"array","items":{"$ref":"#/components/schemas/GroupApprovalResponse"}},"GroupApprovalResponse":{"type":"object","properties":{"authorId":{"type":"string","format":"uuid","description":"Event author who made the decision"},"createdAt":{"type":"string","format":"date-time","description":"Decision timestamp"},"decision":{"type":"string","enum":["consent","refuse"],"description":"Approval decision","example":"consent"},"groupId":{"type":"string","format":"uuid","description":"Group ID"},"id":{"type":"string","format":"uuid","description":"Approval record ID"}},"required":["authorId","createdAt","decision","groupId","id"]},"ApproveResult":{"type":"object","properties":{"approved":{"type":"boolean","description":"Whether the group reached approval threshold","example":true},"chatId":{"type":"string","nullable":true,"format":"uuid","description":"Chat ID if approved"},"id":{"type":"string","format":"uuid","description":"Group ID"},"status":{"type":"string","enum":["waiting","pending","approved","rejected","expired"],"description":"Updated group status","example":"approved"}},"required":["approved","chatId","id","status"]},"AttachmentDownloadUrlResponse":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Short-lived signed download URL"}},"required":["url"]},"CreateDirectChatResponse":{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time","description":"Chat creation timestamp"},"id":{"type":"string","format":"uuid","description":"Chat ID"},"type":{"type":"string","enum":["approved_group","direct","event_authors"],"description":"Chat type","example":"direct"}},"required":["createdAt","id","type"]},"CreateDirectChatRequest":{"type":"object","properties":{"userId":{"type":"string","format":"uuid","description":"The user to start a conversation with"}},"required":["userId"]},"ListChatsResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ChatSummaryResponse"}}},"required":["data"]},"ChatSummaryResponse":{"type":"object","properties":{"avatarUrl":{"type":"string","nullable":true,"description":"Signed URL of the picture standing for the chat — the event cover for a group chat, the other person's avatar for a direct one. Null when there is none."},"createdAt":{"type":"string","format":"date-time","description":"Chat creation timestamp"},"eventCreatorId":{"type":"string","nullable":true,"format":"uuid","description":"Who created the event behind the chat; null when there is no event. Read from the event itself — a chat role cannot tell the creator from a co-author, both being `organizer`."},"eventId":{"type":"string","nullable":true,"format":"uuid","description":"Event this group chat belongs to; null for a direct chat"},"id":{"type":"string","format":"uuid","description":"Chat ID"},"lastMessageAt":{"type":"string","nullable":true,"format":"date-time","description":"Timestamp of the last message"},"lastMessageContent":{"type":"string","nullable":true,"description":"Preview of the last message content"},"lastMessageSender":{"type":"object","nullable":true,"properties":{"name":{"type":"string","nullable":true},"profileId":{"type":"string","nullable":true,"format":"uuid"},"userId":{"type":"string","format":"uuid"}},"required":["name","profileId","userId"],"description":"Who wrote the previewed message. Null when the chat has none. Given separately from `participantPreview`, which may not include them."},"lastMessageSenderId":{"type":"string","nullable":true,"format":"uuid","description":"ID of the last message sender"},"name":{"type":"string","nullable":true,"description":"Display name: the chat's own name when set, otherwise the event's current title. Null only for chats with neither (e.g. a direct chat, rendered from its participant list)."},"participantCount":{"type":"integer","description":"Everyone currently in the chat, the caller included","example":3},"participantPreview":{"type":"array","items":{"type":"object","properties":{"avatarUrl":{"type":"string","nullable":true},"name":{"type":"string","nullable":true},"profileId":{"type":"string","nullable":true,"format":"uuid"},"role":{"type":"string","enum":["organizer","member"]},"userId":{"type":"string","format":"uuid"}},"required":["avatarUrl","name","profileId","role","userId"]},"description":"Up to three other participants, for the row's stack of faces. The caller is left out; the event's creator comes first, then joining order. Avatars are signed thumbnail URLs, null when the person has no picture."},"peer":{"type":"object","nullable":true,"properties":{"avatarUrl":{"type":"string","nullable":true},"name":{"type":"string","nullable":true},"profileId":{"type":"string","nullable":true,"format":"uuid"},"userId":{"type":"string","format":"uuid"}},"required":["avatarUrl","name","profileId","userId"],"description":"The other participant of a direct chat; null for a group chat"},"type":{"type":"string","enum":["approved_group","direct","event_authors"],"description":"`approved_group` — the chat of an approved like group; `event_authors` — the organizers' own room, opened with the event; `direct` — one to one","example":"approved_group"},"unreadCount":{"type":"integer","description":"Messages from other participants, sent since the user joined, that they have no read receipt for. The user's own messages never count.","example":3},"viewerEventRole":{"type":"string","nullable":true,"enum":["creator","author","participant",null],"description":"Where the caller stands in the event: `creator` of it, an accepted `author` of it, or a `participant` who came in through a group. Null when the chat has no event.","example":"creator"}},"required":["avatarUrl","createdAt","eventCreatorId","eventId","id","lastMessageAt","lastMessageContent","lastMessageSender","lastMessageSenderId","name","participantCount","participantPreview","peer","type","unreadCount","viewerEventRole"]},"ChatDetailResponse":{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time","description":"Chat creation timestamp"},"eventId":{"type":"string","nullable":true,"format":"uuid","description":"Event this group chat belongs to; null for a direct chat"},"id":{"type":"string","format":"uuid","description":"Chat ID"},"name":{"type":"string","nullable":true,"description":"The chat's own name when set, otherwise the event's current title"},"participants":{"type":"array","items":{"$ref":"#/components/schemas/ChatParticipantResponse"},"description":"Chat participants with their roles"},"type":{"type":"string","enum":["approved_group","direct","event_authors"],"description":"`approved_group` — the chat of an approved like group; `event_authors` — the organizers' own room, opened with the event; `direct` — one to one","example":"approved_group"}},"required":["createdAt","eventId","id","name","participants","type"]},"ChatParticipantResponse":{"type":"object","properties":{"avatarUrl":{"type":"string","nullable":true,"description":"Signed URL of their avatar preview; null without a profile photo"},"name":{"type":"string","nullable":true,"description":"Display name from the profile; null if the user has none","example":"Anna"},"profileId":{"type":"string","nullable":true,"format":"uuid","description":"Their profile ID; null if they have none"},"role":{"type":"string","enum":["organizer","member"],"description":"Role within the chat","example":"member"},"userId":{"type":"string","format":"uuid","description":"Participant user ID"}},"required":["avatarUrl","name","profileId","role","userId"]},"ListMessagesResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MessageResponse"}},"nextCursor":{"type":"string","nullable":true,"example":"eyJpZCI6IjEyMyIsImNyZWF0ZWRBdCI6IjIwMjQtMDEtMDFUMDA6MDA6MDBaIn0"}},"required":["data","nextCursor"]},"MessageResponse":{"type":"object","properties":{"attachments":{"type":"array","items":{"$ref":"#/components/schemas/AttachmentResponse"},"description":"Files attached to this message"},"chatId":{"type":"string","format":"uuid","description":"ID of the chat this message belongs to"},"content":{"type":"string","description":"Message content","example":"Hello everyone!"},"createdAt":{"type":"string","format":"date-time","description":"Message creation timestamp"},"deletedAt":{"type":"string","nullable":true,"format":"date-time","description":"Soft deletion timestamp"},"id":{"type":"string","format":"uuid","description":"Message ID"},"readBy":{"type":"array","items":{"type":"string","format":"uuid"},"description":"User IDs that have read this message; compare against the participant list"},"replyTo":{"$ref":"#/components/schemas/ReplyPreviewResponse"},"replyToId":{"type":"string","nullable":true,"format":"uuid","description":"ID of the message this one replies to"},"senderId":{"type":"string","format":"uuid","description":"ID of the message sender"},"updatedAt":{"type":"string","format":"date-time","description":"Last update timestamp"}},"required":["attachments","chatId","content","createdAt","deletedAt","id","readBy","replyTo","replyToId","senderId","updatedAt"]},"AttachmentResponse":{"type":"object","properties":{"fileName":{"type":"string","nullable":true,"description":"Original file name, if the client supplied one"},"id":{"type":"string","format":"uuid","description":"Attachment ID"},"mimeType":{"type":"string","description":"MIME type","example":"image/jpeg"},"sizeBytes":{"type":"integer","description":"File size in bytes"},"type":{"type":"string","enum":["image","video","audio","file"],"description":"Attachment kind","example":"image"}},"required":["fileName","id","mimeType","sizeBytes","type"]},"ReplyPreviewResponse":{"type":"object","nullable":true,"properties":{"content":{"type":"string","description":"Content of the message being replied to"},"deletedAt":{"type":"string","nullable":true,"format":"date-time","description":"Soft deletion timestamp of the original"},"id":{"type":"string","format":"uuid","description":"ID of the message being replied to"},"senderId":{"type":"string","format":"uuid","description":"Sender of the message being replied to"}},"required":["content","deletedAt","id","senderId"],"description":"Inline quote of the replied-to message"},"UploadUrlResponse":{"type":"object","properties":{"key":{"type":"string","description":"Storage object key to persist on the owning resource, shaped `{userId}/{uuid}`"},"signedUrl":{"type":"string","description":"One-time signed URL the client PUTs the file bytes to"},"token":{"type":"string","description":"Token for supabase-js `uploadToSignedUrl(path, token, file)`"}},"required":["key","signedUrl","token"]},"CreateUploadUrlRequest":{"type":"object","properties":{"mime":{"type":"string","minLength":1,"maxLength":255,"description":"MIME type of the file. Accepted: image/, video/, audio/ prefixes and application/pdf. Max size 10485760 bytes.","example":"image/jpeg"}},"required":["mime"]},"MediaDownloadUrlResponse":{"type":"object","properties":{"url":{"type":"string","description":"Signed download URL, valid for 3600 seconds"}},"required":["url"]},"PeopleFeedResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PeopleFeedItem"}},"nextCursor":{"type":"string","nullable":true,"example":"eyJpZCI6IjEyMyIsImNyZWF0ZWRBdCI6IjIwMjQtMDEtMDFUMDA6MDA6MDBaIn0"}},"required":["data","nextCursor"]},"PeopleFeedItem":{"type":"object","properties":{"age":{"type":"number","nullable":true},"city":{"type":"string","nullable":true},"datingPurpose":{"type":"string","nullable":true},"distanceKm":{"type":"number","nullable":true},"firstName":{"type":"string"},"id":{"type":"string","format":"uuid"},"location":{"type":"object","nullable":true,"properties":{"latitude":{"type":"number"},"longitude":{"type":"number"}},"required":["latitude","longitude"]},"photos":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"isAvatar":{"type":"boolean"},"position":{"type":"integer"},"url":{"type":"string","nullable":true}},"required":["id","isAvatar","position","url"]}},"userId":{"type":"string","format":"uuid"}},"required":["age","city","datingPurpose","distanceKm","firstName","id","location","photos","userId"]},"PeopleInteraction":{"type":"object","properties":{"chatId":{"type":"string","nullable":true,"format":"uuid","description":"Direct chat with that person, opened by the match. Null unless this like matched."},"expiresAt":{"type":"string","format":"date-time"},"id":{"type":"string","format":"uuid"},"isMatch":{"type":"boolean"},"matchId":{"type":"string","nullable":true,"format":"uuid"},"targetProfileId":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["like","dislike"]},"updatedAt":{"type":"string","format":"date-time"}},"required":["expiresAt","id","targetProfileId","type","updatedAt"]},"PeopleIncomingLikesResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time"},"interactionId":{"type":"string","format":"uuid"},"profile":{"type":"object","properties":{"age":{"type":"number","nullable":true},"city":{"type":"string","nullable":true},"firstName":{"type":"string"},"id":{"type":"string","format":"uuid"},"photos":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"isAvatar":{"type":"boolean"},"position":{"type":"integer"},"url":{"type":"string","nullable":true}},"required":["id","isAvatar","position","url"]}},"photoUrl":{"type":"string","nullable":true},"userId":{"type":"string","format":"uuid"}},"required":["age","city","firstName","id","photos","photoUrl","userId"]}},"required":["createdAt","interactionId","profile"]}},"nextCursor":{"type":"string","nullable":true,"example":"eyJpZCI6IjEyMyIsImNyZWF0ZWRBdCI6IjIwMjQtMDEtMDFUMDA6MDA6MDBaIn0"}},"required":["data","nextCursor"]},"PeopleMatchesResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"chatId":{"type":"string","nullable":true,"format":"uuid","description":"Direct chat with that person — what the row's message button opens"},"id":{"type":"string","format":"uuid"},"matchedAt":{"type":"string","format":"date-time"},"profiles":{"type":"array","items":{"type":"object","properties":{"age":{"type":"number","nullable":true},"city":{"type":"string","nullable":true},"firstName":{"type":"string"},"id":{"type":"string","format":"uuid"},"photos":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"isAvatar":{"type":"boolean"},"position":{"type":"integer"},"url":{"type":"string","nullable":true}},"required":["id","isAvatar","position","url"]}},"photoUrl":{"type":"string","nullable":true},"userId":{"type":"string","format":"uuid"}},"required":["age","city","firstName","id","photos","photoUrl","userId"]}}},"required":["chatId","id","matchedAt","profiles"]}},"nextCursor":{"type":"string","nullable":true,"example":"eyJpZCI6IjEyMyIsImNyZWF0ZWRBdCI6IjIwMjQtMDEtMDFUMDA6MDA6MDBaIn0"}},"required":["data","nextCursor"]},"PeopleProfileDetail":{"allOf":[{"$ref":"#/components/schemas/PeopleFeedItem"},{"type":"object","properties":{"alcoholAttitude":{"type":"string","nullable":true},"bio":{"type":"string","nullable":true},"bodyType":{"type":"string","nullable":true},"educations":{"type":"array","items":{"type":"object","properties":{"inProgress":{"type":"boolean"},"level":{"type":"string"}},"required":["inProgress","level"]}},"familyPlan":{"type":"string","nullable":true},"financialState":{"type":"string","nullable":true},"height":{"type":"number","nullable":true},"hometown":{"type":"string","nullable":true},"languages":{"type":"array","items":{"type":"string"}},"religion":{"type":"string","nullable":true},"sex":{"type":"string","nullable":true},"smokingAttitude":{"type":"string","nullable":true},"zodiac":{"type":"string","nullable":true}},"required":["alcoholAttitude","bio","bodyType","educations","familyPlan","financialState","height","hometown","languages","religion","sex","smokingAttitude","zodiac"]}]},"UnreadCountResponse":{"type":"object","properties":{"count":{"type":"integer","description":"Unread notifications","example":3}},"required":["count"]},"MarkReadResponse":{"type":"object","properties":{"updated":{"type":"integer","description":"How many rows changed","example":5}},"required":["updated"]},"MarkReadRequest":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":200,"description":"Notifications to mark read. Omit to mark everything read."}}},"ListNotificationsResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/NotificationResponse"}},"nextCursor":{"type":"string","nullable":true,"example":"eyJpZCI6IjEyMyIsImNyZWF0ZWRBdCI6IjIwMjQtMDEtMDFUMDA6MDA6MDBaIn0"}},"required":["data","nextCursor"]},"NotificationResponse":{"type":"object","properties":{"actorId":{"type":"string","nullable":true,"format":"uuid","description":"Who caused it; null for system events"},"chatId":{"type":"string","nullable":true,"format":"uuid","description":"Chat to open, when relevant"},"createdAt":{"type":"string","format":"date-time","description":"When it happened"},"eventId":{"type":"string","nullable":true,"format":"uuid","description":"Event to open, when relevant"},"groupId":{"type":"string","nullable":true,"format":"uuid","description":"Like group involved, when relevant"},"id":{"type":"string","format":"uuid","description":"Notification ID"},"isRead":{"type":"boolean","description":"Whether the user has read it","example":false},"payload":{"$ref":"#/components/schemas/NotificationPayload"},"type":{"type":"string","enum":["event_application","event_application_approved","event_application_rejected","event_coauthor_invite","event_coauthor_accepted","event_coauthor_refused","friend_request","friend_request_accepted","chat_created","chat_message","people_like","people_match","event_invite","event_invite_accepted"],"description":"What happened","example":"event_application"},"userId":{"type":"string","format":"uuid","description":"Recipient"}},"required":["actorId","chatId","createdAt","eventId","groupId","id","isRead","payload","type","userId"]},"NotificationPayload":{"type":"object","properties":{"actorName":{"type":"string","nullable":true,"description":"Who caused it","example":"Анна"},"chatName":{"type":"string","nullable":true,"description":"Chat name at the time"},"eventTitle":{"type":"string","nullable":true,"description":"Event title at the time","example":"Rooftop dinner"},"messagePreview":{"type":"string","nullable":true,"description":"First line of the message"}},"description":"What this row displays, captured when it was written. Names here are historical on purpose — renaming an event does not rewrite a past notification."},"PushStatusResponse":{"type":"object","properties":{"enabled":{"type":"array","items":{"type":"string","enum":["apns","fcm","rustore"]},"description":"Providers with credentials, able to deliver","example":["fcm"]}},"required":["enabled"]},"RegisterDeviceResponse":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Registration ID"}},"required":["id"]},"RegisterDeviceRequest":{"type":"object","properties":{"appVersion":{"type":"string","maxLength":32,"description":"App build, for triage","example":"1.4.0"},"locale":{"type":"string","maxLength":16,"description":"BCP-47 tag of the device","example":"ru-RU"},"platform":{"type":"string","enum":["android","ios"],"description":"OS of the device","example":"android"},"provider":{"type":"string","enum":["apns","fcm","rustore"],"description":"Service that issued the token. Android picks `fcm` when Google Play Services are available and `rustore` otherwise; iOS is always `apns`.","example":"fcm"},"token":{"type":"string","minLength":1,"maxLength":512,"description":"Token issued by the provider"}},"required":["platform","provider","token"]},"UnregisterDeviceRequest":{"type":"object","properties":{"token":{"type":"string","minLength":1,"maxLength":512,"description":"Token to forget"}},"required":["token"]},"ReportResponse":{"type":"object","properties":{"createdAt":{"type":"string","format":"date-time","description":"When the report was filed"},"description":{"type":"string","nullable":true,"description":"Free-text detail"},"eventId":{"type":"string","nullable":true,"format":"uuid","description":"Reported event, when targetType is `event`"},"id":{"type":"string","format":"uuid","description":"Report ID"},"reason":{"type":"string","enum":["fake_profile","unacceptable_content","toxic_behavior","incorrect_age","behavior_outside_social_network","other","offensive_behavior","harassment","spam","fake_news_or_disinformation","flooding_in_private_messages","fake_account_or_false_info","using_someone_elses_photo","unauthorized_promotion","age_restriction_violation","fraud_or_scam","violence_or_extremism","illegal_content","hacking_or_suspicious_activity","multiple_user_complaints","frequent_blocks","verified_moderator_complaint","bot_or_automation_suspected","api_abuse_or_restriction_bypass","account_hacked"],"description":"Reported reason"},"reportAuthorId":{"type":"string","format":"uuid","description":"Who filed the report"},"reportedChatId":{"type":"string","nullable":true,"format":"uuid","description":"Reported chat, when targetType is `chat`"},"reportedUserId":{"type":"string","nullable":true,"format":"uuid","description":"Reported user, when targetType is `user`"}},"required":["createdAt","description","eventId","id","reason","reportAuthorId","reportedChatId","reportedUserId"]},"CreateReportRequest":{"type":"object","properties":{"description":{"type":"string","maxLength":250,"description":"Free-text detail (max 250 chars)"},"reason":{"type":"string","enum":["fake_profile","unacceptable_content","toxic_behavior","incorrect_age","behavior_outside_social_network","other","offensive_behavior","harassment","spam","fake_news_or_disinformation","flooding_in_private_messages","fake_account_or_false_info","using_someone_elses_photo","unauthorized_promotion","age_restriction_violation","fraud_or_scam","violence_or_extremism","illegal_content","hacking_or_suspicious_activity","multiple_user_complaints","frequent_blocks","verified_moderator_complaint","bot_or_automation_suspected","api_abuse_or_restriction_bypass","account_hacked"],"description":"Why the target is being reported","example":"spam"},"targetId":{"type":"string","format":"uuid","description":"ID of the reported user, event, or chat"},"targetType":{"type":"string","enum":["user","event","chat"],"description":"What is being reported","example":"user"}},"required":["reason","targetId","targetType"]}},"parameters":{}},"paths":{"/profiles":{"get":{"description":"Retrieves the authenticated user's profile with all personal information.","operationId":"getOwnProfile","security":[{"bearerAuth":[]}],"summary":"Get own profile","tags":["Profiles"],"responses":{"200":{"description":"Profile retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnProfileResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"description":"Creates a new profile for the authenticated user. Each user can only have one profile — a second call answers 409, so a re-submitted sign-up should fall back to PATCH /profiles.","operationId":"createProfile","security":[{"bearerAuth":[]}],"summary":"Create profile","tags":["Profiles"],"requestBody":{"description":"Profile data","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProfileRequest"}}}},"responses":{"201":{"description":"Profile created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnProfileResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict - The resource is already claimed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"description":"Updates the authenticated user's profile — this is how the survey gets answered after sign-up. Only provided fields are touched; `educations` and `languages` replace their whole list. Unknown field names are ignored, and a body that names none of the fields below is a 400 rather than a silent no-op.","operationId":"updateProfile","security":[{"bearerAuth":[]}],"summary":"Update profile","tags":["Profiles"],"requestBody":{"description":"Fields to update","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProfileRequest"}}}},"responses":{"200":{"description":"Profile updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OwnProfileResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"description":"Deletes the account of whoever is calling, and everything it left behind: the profile and its photos, the events they were running (with the applications and chats those carried), likes, matches, friendships, reports, notifications, devices and last known position.\n\nConversations are left alone. What somebody said to you is your copy of it, so the messages and the chat stay; with the profile gone there is simply no name to show for that side, which is how a client can tell the account has been deleted.\n\nThere is no undo and no grace period — the account in Supabase goes too, so the token stops working the moment this returns.","operationId":"deleteProfile","security":[{"bearerAuth":[]}],"summary":"Delete account","tags":["Profiles"],"responses":{"200":{"description":"Profile deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/profiles/photos":{"get":{"description":"Lists the caller's profile photos in gallery order. `isAvatar` marks the one that stands for the profile elsewhere — it is a choice, not the first position. Each photo carries a short-lived signed URL; `url` is null when the underlying object has gone missing from the bucket.","operationId":"listProfilePhotos","security":[{"bearerAuth":[]}],"summary":"List own profile photos","tags":["Profiles"],"responses":{"200":{"description":"Photos retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfilePhotoList"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"description":"Attaches an already-uploaded object to the profile, appending it after the last photo. The key comes from POST /media/upload-url and must belong to the caller.","operationId":"addProfilePhoto","security":[{"bearerAuth":[]}],"summary":"Attach profile photo","tags":["Profiles"],"requestBody":{"description":"Storage key of the uploaded object","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddPhotoRequest"}}}},"responses":{"201":{"description":"Photo attached successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfilePhotoRef"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"description":"Reorders the whole photo set. The list must name each of the profile's photos exactly once. This is gallery order only — the avatar is picked with `avatarPhotoId` on PATCH /profiles.","operationId":"reorderProfilePhotos","security":[{"bearerAuth":[]}],"summary":"Reorder profile photos","tags":["Profiles"],"requestBody":{"description":"Photo ids in their new order","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderPhotosRequest"}}}},"responses":{"200":{"description":"Photos reordered successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderPhotosResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/profiles/photos/{photoId}":{"delete":{"description":"Detaches a photo from the profile and drops the underlying object from storage.","operationId":"deleteProfilePhoto","security":[{"bearerAuth":[]}],"summary":"Delete profile photo","tags":["Profiles"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Photo ID"},"required":true,"description":"Photo ID","name":"photoId","in":"path"}],"responses":{"200":{"description":"Photo deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhotoDeletedResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/profiles/batch":{"get":{"description":"The same lookup as POST /profiles/batch, in a form that can be cached — a POST never is, by the client, a proxy or a CDN. Prefer this one; fall back to the POST when the ids do not fit in a URL. Responds with `Cache-Control: private` plus an ETag; send the ETag back as `If-None-Match` and an unchanged roster costs a 304 instead of a payload.","operationId":"listProfiles","security":[{"bearerAuth":[]}],"summary":"Get profiles in bulk (cacheable)","tags":["Profiles"],"parameters":[{"schema":{"type":"string","description":"Comma-separated user ids (max 100)","example":"550e8400-e29b-41d4-a716-446655440000,6ba7b810-9dad-11d1-80b4-00c04fd430c8"},"required":true,"description":"Comma-separated user ids (max 100)","name":"userIds","in":"query"}],"responses":{"200":{"description":"Profiles retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchProfilesResponse"}}}},"304":{"description":"Not modified — the ETag you sent is still current"},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"description":"Resolves many profiles in one request, by USER id — which is what a client actually holds: chat `participantIds`, an event's `creatorId`, like-group `memberIds`. (GET /profiles/{id} takes a profile id, a different value.) Ids with no profile are simply absent from the response.","operationId":"getProfilesBatch","security":[{"bearerAuth":[]}],"summary":"Get profiles in bulk","tags":["Profiles"],"requestBody":{"description":"User ids to resolve","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchProfilesRequest"}}}},"responses":{"200":{"description":"Profiles retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchProfilesResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/profiles/{id}":{"get":{"description":"Retrieves another user's profile by PROFILE id (`profiles.id`), not by user id. When you hold a user id — chat participants, an event creator, like-group members — use POST /profiles/batch instead.","operationId":"getProfileById","security":[{"bearerAuth":[]}],"summary":"Get profile by ID","tags":["Profiles"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Profile id (`profiles.id`) — not the user id","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Profile id (`profiles.id`) — not the user id","name":"id","in":"path"}],"responses":{"200":{"description":"Profile retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfileResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/profiles/complete":{"post":{"description":"Marks onboarding as finished. Fails with 400 while required answers are missing. Accepting the legal documents is optional: send the versions the client displayed and they are recorded, or send an empty body.","operationId":"completeProfile","security":[{"bearerAuth":[]}],"summary":"Complete profile","tags":["Profiles"],"requestBody":{"description":"Versions of the documents the client displayed, if any","required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteProfileRequest"}}}},"responses":{"200":{"description":"Profile completed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteProfileResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/friends":{"get":{"description":"Returns a paginated list of the user's friendships. Can filter by status (pending, accepted, rejected, blocked).","operationId":"listFriendships","security":[{"bearerAuth":[]}],"summary":"List friendships","tags":["Friends"],"parameters":[{"schema":{"type":"string","description":"Pagination cursor"},"required":false,"description":"Pagination cursor","name":"cursor","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":100,"default":20,"description":"Number of items per page","example":20},"required":false,"description":"Number of items per page","name":"limit","in":"query"},{"schema":{"type":"string","enum":["pending","accepted","rejected","blocked"],"description":"Filter by friendship status"},"required":false,"description":"Filter by friendship status","name":"status","in":"query"}],"responses":{"200":{"description":"Friendships retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListFriendshipsResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/friends/requests":{"post":{"description":"Asks somebody to be bloomers, and the addressee is notified.\n\nThe QR code in a profile is a permanent link, so the same person may well be scanned twice. Asking again is not an error: an existing request comes back as `already_pending` and an existing friendship as `already_friends`, both with 200 and neither writing anything. A genuine refusal carries a stable `code` — `self_request`, `user_not_found`, `request_unavailable` (a block, either way round, without saying which), `previously_rejected` or `limit_reached` — so a client never has to read the English message.","operationId":"sendFriendRequest","security":[{"bearerAuth":[]}],"summary":"Send friend request","tags":["Friends"],"requestBody":{"description":"Friend request data","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendFriendRequestBody"}}}},"responses":{"200":{"description":"Nothing to do: the request or the friendship was already there","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FriendRequestResult"}}}},"201":{"description":"Friend request sent successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FriendRequestResult"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict - The resource is already claimed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/friends/requests/{id}/accept":{"post":{"description":"Accepts a pending friend request. Both users will become friends.","operationId":"acceptFriendRequest","security":[{"bearerAuth":[]}],"summary":"Accept friend request","tags":["Friends"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Friendship ID to accept"},"required":true,"description":"Friendship ID to accept","name":"id","in":"path"}],"responses":{"200":{"description":"Friend request accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FriendshipResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/friends/requests/{id}/reject":{"post":{"description":"Rejects a pending friend request.","operationId":"rejectFriendRequest","security":[{"bearerAuth":[]}],"summary":"Reject friend request","tags":["Friends"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Friendship ID to reject"},"required":true,"description":"Friendship ID to reject","name":"id","in":"path"}],"responses":{"200":{"description":"Friend request rejected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FriendshipResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/friends/{userId}/block":{"post":{"description":"Blocks a user. Blocked users cannot send friend requests or interact with you.","operationId":"blockUser","security":[{"bearerAuth":[]}],"summary":"Block user","tags":["Friends"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"User ID to block"},"required":true,"description":"User ID to block","name":"userId","in":"path"}],"responses":{"200":{"description":"User blocked successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FriendshipResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"description":"Unblocks a previously blocked user.","operationId":"unblockUser","security":[{"bearerAuth":[]}],"summary":"Unblock user","tags":["Friends"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"User ID to unblock"},"required":true,"description":"User ID to unblock","name":"userId","in":"path"}],"responses":{"200":{"description":"User unblocked successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/friends/{userId}":{"delete":{"description":"Removes a user from your friends list.","operationId":"unfriend","security":[{"bearerAuth":[]}],"summary":"Unfriend user","tags":["Friends"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"User ID to unfriend"},"required":true,"description":"User ID to unfriend","name":"userId","in":"path"}],"responses":{"200":{"description":"User unfriended successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/events":{"get":{"description":"Browse published events near a location. Results are sorted by distance. Supports filters for event type, date range, and audience composition.","operationId":"getEventsFeed","security":[{"bearerAuth":[]}],"summary":"Browse events feed","tags":["Events"],"parameters":[{"schema":{"type":"string","description":"Pagination cursor"},"required":false,"description":"Pagination cursor","name":"cursor","in":"query"},{"schema":{"type":"string","format":"date","description":"Latest calendar day an event may start on (YYYY-MM-DD), inclusive and interpreted in UTC — an event starting at 23:00 that day still matches. Filters on `startAt`, not on when the event ends.","example":"2026-08-31"},"required":false,"description":"Latest calendar day an event may start on (YYYY-MM-DD), inclusive and interpreted in UTC — an event starting at 23:00 that day still matches. Filters on `startAt`, not on when the event ends.","name":"endDate","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Minimum femaleOrganizerCount: organizers (the creator plus co-authors who accepted) whose profile sex is female. Guests are not counted here."},"required":false,"description":"Minimum femaleOrganizerCount: organizers (the creator plus co-authors who accepted) whose profile sex is female. Guests are not counted here.","name":"femaleFrom","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Maximum femaleOrganizerCount"},"required":false,"description":"Maximum femaleOrganizerCount","name":"femaleTo","in":"query"},{"schema":{"type":"string","description":"Only show events from friends"},"required":false,"description":"Only show events from friends","name":"friendsOnly","in":"query"},{"schema":{"type":"number","nullable":true,"minimum":-90,"maximum":90,"description":"User latitude for distance calculation","example":52.52},"required":false,"description":"User latitude for distance calculation","name":"lat","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Items per page","example":20},"required":false,"description":"Items per page","name":"limit","in":"query"},{"schema":{"type":"number","nullable":true,"minimum":-180,"maximum":180,"description":"User longitude for distance calculation","example":13.405},"required":false,"description":"User longitude for distance calculation","name":"lng","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Minimum maleOrganizerCount: organizers (the creator plus co-authors who accepted) whose profile sex is male. Guests are not counted here."},"required":false,"description":"Minimum maleOrganizerCount: organizers (the creator plus co-authors who accepted) whose profile sex is male. Guests are not counted here.","name":"maleFrom","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Maximum maleOrganizerCount"},"required":false,"description":"Maximum maleOrganizerCount","name":"maleTo","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Minimum organizerCount: the creator plus co-authors who accepted their invitation. Always >= 1."},"required":false,"description":"Minimum organizerCount: the creator plus co-authors who accepted their invitation. Always >= 1.","name":"organizerFrom","in":"query"},{"schema":{"type":"integer","nullable":true,"minimum":0,"description":"Maximum organizerCount"},"required":false,"description":"Maximum organizerCount","name":"organizerTo","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":100,"description":"Search events whose title contains this text, case-insensitive","example":"rooftop"},"required":false,"description":"Search events whose title contains this text, case-insensitive","name":"q","in":"query"},{"schema":{"type":"integer","minimum":100,"maximum":50000,"default":5000,"description":"Search radius in meters (100-50000)","example":5000},"required":false,"description":"Search radius in meters (100-50000)","name":"radius","in":"query"},{"schema":{"type":"string","format":"date","description":"Earliest calendar day an event may start on (YYYY-MM-DD), inclusive and interpreted in UTC.","example":"2026-08-01"},"required":false,"description":"Earliest calendar day an event may start on (YYYY-MM-DD), inclusive and interpreted in UTC.","name":"startDate","in":"query"},{"schema":{"type":"string","enum":["concert_musician","concert_stand_up","concert_symphony","concert_jazz","concert_acoustic","concert_rock","concert_electronic","concert_street_musicians","concert_organ","concert_local_festival","concert_lit_music","exhibition_art","exhibition_museum","exhibition_modern_art","exhibition_photography","exhibition_science","exhibition_history","exhibition_fair","exhibition_interactive","exhibition_books","exhibition_flowers","exhibition_sculptures","party_repost","party_private","party_theme","party_home","party_birthday","party_after","party_pool","party_club_foam","party_night_walk","party_game_night","club_dance","club_theme","club_karaoke","club_dj","club_techno","club_stand_up","club_art","club_salsa","club_mafia","bar_board_games","bar_quiz","bar_live_music","bar_theme_night","bar_karaoke","bar_drink_tasting","bar_lounge","bar_darts","cinema_premiere","cinema_outdoor","cinema_author","cinema_retro","cinema_universe","cinema_shorts","cinema_immersive","cinema_marathon","cinema_family","theatre_play","theatre_musical","theatre_improv","theatre_puppet","theatre_experimental","theatre_street","theatre_radio","theatre_shadow","theatre_improvisation","festival_music","festival_cinema","festival_gastro","festival_theatre","festival_crafts","festival_sports","festival_ballet","festival_medieval","festival_ethnic","festival_tech","sport_match","sport_esports","sport_race","sport_yoga","sport_dance","sport_fanzone","sport_sup","sport_battle","sport_cycling","nature_hiking","nature_photo_safari","nature_star_gazing","nature_picnic","nature_cycling","nature_waterfalls","nature_yoga","nature_farm_tour","workshop_ceramics","workshop_calligraphy","workshop_cooking","workshop_sketching","children_science","children_quests","children_cartoons","children_pets","quest_horror","quest_city","quest_orienteering","quest_vr","education_lectures","education_meetings","education_history","education_photo","immersive_escape","immersive_theatre","immersive_tours","immersive_roleplay","art_light","art_open_workshops","art_sound","art_performances","undefined","other"],"description":"Filter by a single event type"},"required":false,"description":"Filter by a single event type","name":"type","in":"query"},{"schema":{"type":"string","description":"Comma-separated list of event types; an unknown value is rejected rather than ignored","example":"party_home,concert_musician"},"required":false,"description":"Comma-separated list of event types; an unknown value is rejected rather than ignored","name":"types","in":"query"}],"responses":{"200":{"description":"Events feed retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventFeedResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"description":"Creates a new event in draft status. The event must be published before it becomes visible to other users.\n\nCreating it also opens the organizers' chat (`type: event_authors`), with the creator in it; co-authors join as they accept their invitation. It is not the same chat as the one an approved like group gets — an event may have both.","operationId":"createEvent","security":[{"bearerAuth":[]}],"summary":"Create event (draft)","tags":["Events"],"requestBody":{"description":"Event data","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEventRequest"}}}},"responses":{"201":{"description":"Event created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict - The resource is already claimed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/events/mine":{"get":{"description":"Returns every event the caller owns — as creator or accepted co-organizer — in all statuses including drafts. No geo filter and no pagination: this is the user's own, bounded set.","operationId":"listMyEvents","security":[{"bearerAuth":[]}],"summary":"List my events","tags":["Events"],"responses":{"200":{"description":"Own events retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyEventsResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/events/{id}":{"get":{"description":"Retrieves detailed information about a specific event.","operationId":"getEvent","security":[{"bearerAuth":[]}],"summary":"Get event details","tags":["Events"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"}],"responses":{"200":{"description":"Event details retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"description":"Updates event details. Some fields cannot be modified after the event has started.","operationId":"updateEvent","security":[{"bearerAuth":[]}],"summary":"Update event","tags":["Events"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"}],"requestBody":{"description":"Fields to update","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateEventRequest"}}}},"responses":{"200":{"description":"Event updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict - The resource is already claimed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"description":"Soft-deletes an event. The event can be restored within a grace period using the restore endpoint.","operationId":"deleteEvent","security":[{"bearerAuth":[]}],"summary":"Delete event","tags":["Events"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"}],"responses":{"200":{"description":"Event deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/events/{id}/restore":{"post":{"description":"Restores a soft-deleted event.","operationId":"restoreEvent","security":[{"bearerAuth":[]}],"summary":"Restore deleted event","tags":["Events"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"}],"responses":{"200":{"description":"Event restored successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/events/{id}/publish":{"post":{"description":"Publishes a draft event, making it visible in the events feed.","operationId":"publishEvent","security":[{"bearerAuth":[]}],"summary":"Publish event","tags":["Events"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"}],"responses":{"200":{"description":"Event published successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/events/{id}/cancel":{"post":{"description":"Cancels a published or started event. Cancelled events are no longer visible in the feed.","operationId":"cancelEvent","security":[{"bearerAuth":[]}],"summary":"Cancel event","tags":["Events"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"}],"responses":{"200":{"description":"Event cancelled successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/events/{id}/cover":{"put":{"description":"Sets or replaces the cover image from an object already uploaded via POST /media/upload-url. A downscaled preview is generated and served as `imageThumbUrl`; the previous cover is dropped from storage. Creator only.","operationId":"setEventCover","security":[{"bearerAuth":[]}],"summary":"Set event cover","tags":["Events"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"}],"requestBody":{"description":"Key of the uploaded cover","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetEventCoverRequest"}}}},"responses":{"200":{"description":"Cover set","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict - The resource is already claimed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"description":"Removes the cover image and deletes the underlying objects from storage. Creator only.","operationId":"removeEventCover","security":[{"bearerAuth":[]}],"summary":"Remove event cover","tags":["Events"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"}],"responses":{"200":{"description":"Cover removed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/events/{id}/authors":{"post":{"description":"Invites a friend as co-author/organizer for the event. Co-authors can manage the event and approve like groups.","operationId":"addAuthor","security":[{"bearerAuth":[]}],"summary":"Add co-author","tags":["Events"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"}],"requestBody":{"description":"User to add as co-author","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddAuthorRequest"}}}},"responses":{"201":{"description":"Co-author added successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventAuthorResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"description":"Lists all authors (creator and co-authors) for an event.","operationId":"listAuthors","security":[{"bearerAuth":[]}],"summary":"List event authors","tags":["Events"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"}],"responses":{"200":{"description":"Authors list retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventAuthorsList"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/events/{id}/authors/accept":{"post":{"description":"The invited user accepts a co-author invitation. Only accepted co-authors count as organizers for quorum, voting and the group chat.","operationId":"acceptAuthorInvite","security":[{"bearerAuth":[]}],"summary":"Accept co-author invite","tags":["Events"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"}],"responses":{"200":{"description":"Invitation accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorInviteDecision"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/events/{id}/authors/refuse":{"post":{"description":"The invited user refuses a co-author invitation. A refused invite never counts toward the quorum.","operationId":"refuseAuthorInvite","security":[{"bearerAuth":[]}],"summary":"Refuse co-author invite","tags":["Events"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"}],"responses":{"200":{"description":"Invitation refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorInviteDecision"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/events/{id}/authors/{userId}":{"delete":{"description":"Removes a co-author from the event. Only the event creator can remove co-authors.","operationId":"removeAuthor","security":[{"bearerAuth":[]}],"summary":"Remove co-author","tags":["Events"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"},{"schema":{"type":"string","format":"uuid","description":"User ID to remove"},"required":true,"description":"User ID to remove","name":"userId","in":"path"}],"responses":{"200":{"description":"Co-author removed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/events/{id}/like":{"post":{"description":"Creates a like group for an event, expressing interest in attending. You can optionally include friends in the group.","operationId":"createLikeGroup","security":[{"bearerAuth":[]}],"summary":"Create like group","tags":["Events"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"}],"requestBody":{"description":"Like group data","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLikeGroupRequest"}}}},"responses":{"201":{"description":"Like group created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LikeGroupResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/events/{id}/likes":{"get":{"description":"Lists like groups for an event. Only event authors can view all groups; regular users see only their own groups.","operationId":"listLikeGroups","security":[{"bearerAuth":[]}],"summary":"List like groups","tags":["Events"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Pagination cursor"},"required":false,"description":"Pagination cursor","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Items per page","example":20},"required":false,"description":"Items per page","name":"limit","in":"query"},{"schema":{"type":"string","enum":["waiting","pending","approved","rejected","expired"],"description":"Filter by group status"},"required":false,"description":"Filter by group status","name":"status","in":"query"}],"responses":{"200":{"description":"Like groups retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LikeGroupsListResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/groups":{"get":{"description":"Returns a paginated list of like groups the user is a member of. Can filter by status.","operationId":"listGroups","security":[{"bearerAuth":[]}],"summary":"List my groups","tags":["Groups"],"parameters":[{"schema":{"type":"string","description":"Pagination cursor"},"required":false,"description":"Pagination cursor","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Items per page","example":20},"required":false,"description":"Items per page","name":"limit","in":"query"},{"schema":{"type":"string","enum":["waiting","pending","approved","rejected","expired"],"description":"Filter by group status"},"required":false,"description":"Filter by group status","name":"status","in":"query"}],"responses":{"200":{"description":"Groups retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListGroupsResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/groups/{id}":{"get":{"description":"Retrieves detailed information about a specific like group. User must be a member of the group.","operationId":"getGroup","security":[{"bearerAuth":[]}],"summary":"Get group details","tags":["Groups"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Group ID"},"required":true,"description":"Group ID","name":"id","in":"path"}],"responses":{"200":{"description":"Group details retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LikeGroupResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"description":"Drops the whole invitation while it is still collecting answers. Only the person who started the like may do this; once sent, it is out of their hands.","operationId":"deleteLikeGroup","security":[{"bearerAuth":[]}],"summary":"Delete the invitation","tags":["Groups"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Group ID"},"required":true,"description":"Group ID","name":"id","in":"path"}],"responses":{"200":{"description":"Invitation deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict - The resource is already claimed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/groups/{id}/members":{"post":{"description":"Adds a friend to the like group. Only the group creator or existing members can add new members. Maximum group size is 10 members.","operationId":"addGroupMember","security":[{"bearerAuth":[]}],"summary":"Add member to group","tags":["Groups"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Group ID"},"required":true,"description":"Group ID","name":"id","in":"path"}],"requestBody":{"description":"Member to add","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddMemberRequest"}}}},"responses":{"201":{"description":"Member added successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupMemberResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/groups/{id}/members/{userId}":{"delete":{"description":"Removes a member from the like group. Group creator can remove any member; members can remove themselves.","operationId":"removeGroupMember","security":[{"bearerAuth":[]}],"summary":"Remove member from group","tags":["Groups"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Group ID"},"required":true,"description":"Group ID","name":"id","in":"path"},{"schema":{"type":"string","format":"uuid","description":"User ID to remove"},"required":true,"description":"User ID to remove","name":"userId","in":"path"}],"responses":{"200":{"description":"Member removed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/groups/{id}/approvals":{"get":{"description":"Lists all approval decisions for a like group. Shows which event authors have consented or refused.","operationId":"getGroupApprovals","security":[{"bearerAuth":[]}],"summary":"List group approvals","tags":["Groups"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Group ID"},"required":true,"description":"Group ID","name":"id","in":"path"}],"responses":{"200":{"description":"Approvals list retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupApprovalsList"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/groups/{id}/approve":{"post":{"description":"Approves (consents to) a like group as an event author. When enough authors consent (reaching the event's consent threshold), the group is approved and a chat is created.","operationId":"approveGroup","security":[{"bearerAuth":[]}],"summary":"Approve group (consent)","tags":["Groups"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Group ID"},"required":true,"description":"Group ID","name":"id","in":"path"}],"responses":{"200":{"description":"Consent recorded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveResult"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/groups/{id}/invite/accept":{"post":{"description":"Accepts an invitation to come along to a joint like. Until everybody invited has answered, the like stays out of the organizers' sight; the person who started it then sends it on with whoever agreed.","operationId":"acceptUserInvite","security":[{"bearerAuth":[]}],"summary":"Accept an invitation to a joint like","tags":["Groups"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Group ID"},"required":true,"description":"Group ID","name":"id","in":"path"}],"responses":{"200":{"description":"Invitation accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LikeGroupResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict - The resource is already claimed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/groups/{id}/invite/refuse":{"post":{"description":"Turns down an invitation to a joint like. The like can still be sent without you.","operationId":"refuseUserInvite","security":[{"bearerAuth":[]}],"summary":"Refuse an invitation to a joint like","tags":["Groups"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Group ID"},"required":true,"description":"Group ID","name":"id","in":"path"}],"responses":{"200":{"description":"Invitation refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LikeGroupResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict - The resource is already claimed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/groups/{id}/send":{"post":{"description":"Sends the like to the event's organizers with the people who agreed. Everyone still deciding or having declined is dropped, so the consent threshold T = min(S, N) is counted off the party that is actually coming. Only the person who started the like may do this, and only while it is still `waiting`.","operationId":"sendLikeGroup","security":[{"bearerAuth":[]}],"summary":"Send the like with those who agreed","tags":["Groups"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Group ID"},"required":true,"description":"Group ID","name":"id","in":"path"}],"responses":{"200":{"description":"Like sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LikeGroupResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict - The resource is already claimed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/groups/{id}/reject":{"post":{"description":"Rejects (refuses) a like group as an event author. If enough authors refuse making approval impossible, the group is rejected.","operationId":"rejectGroup","security":[{"bearerAuth":[]}],"summary":"Reject group (refuse)","tags":["Groups"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Group ID"},"required":true,"description":"Group ID","name":"id","in":"path"}],"responses":{"200":{"description":"Refusal recorded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveResult"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/chats/attachments/{attachmentId}/download-url":{"get":{"description":"Mints a short-lived signed URL for a message attachment. The `media` bucket is private, so the API authorizes the chain attachment → message → chat → membership before signing. The stored key must also sit under the sender's own prefix — a row naming somebody else's object is refused with 403 rather than signed.","operationId":"getAttachmentDownloadUrl","security":[{"bearerAuth":[]}],"summary":"Get attachment download URL","tags":["Chats"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Attachment ID"},"required":true,"description":"Attachment ID","name":"attachmentId","in":"path"}],"responses":{"200":{"description":"Signed download URL issued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachmentDownloadUrlResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/chats/direct":{"post":{"description":"Starts a one-to-one conversation with another user. Carried over from the old stack: no friendship or block is required — anyone may write to anyone. The caller's own survey does have to be finished, otherwise a blank profile could open a chat with anybody. Group chats are still created only when a like group is approved.\n\nA pair has at most one direct chat: a mutual like opens it by itself, and calling this again — from either side — returns that same chat instead of splitting the conversation.","operationId":"createDirectChat","security":[{"bearerAuth":[]}],"summary":"Start a direct chat","tags":["Chats"],"requestBody":{"description":"Who to talk to","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDirectChatRequest"}}}},"responses":{"201":{"description":"Chat created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDirectChatResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/chats":{"get":{"description":"Returns the user's chats sorted by most recent activity. Each chat includes a preview of the latest message. Chats the user hid, left, or that were soft-deleted are omitted.","operationId":"listChats","security":[{"bearerAuth":[]}],"summary":"List chats","tags":["Chats"],"responses":{"200":{"description":"Chats retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListChatsResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/chats/{id}":{"get":{"description":"Retrieves detailed information about a specific chat, including the full list of participants and their roles.","operationId":"getChat","security":[{"bearerAuth":[]}],"summary":"Get chat details","tags":["Chats"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Chat ID"},"required":true,"description":"Chat ID","name":"id","in":"path"}],"responses":{"200":{"description":"Chat details retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatDetailResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"description":"Organizer-only: soft-deletes the whole chat for every participant. Reversible via restore.","operationId":"deleteChat","security":[{"bearerAuth":[]}],"summary":"Delete chat","tags":["Chats"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Chat ID"},"required":true,"description":"Chat ID","name":"id","in":"path"}],"responses":{"204":{"description":"Chat soft-deleted"},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/chats/{id}/messages":{"get":{"description":"Returns paginated message history for a chat, sorted newest-first. Use the cursor to load older messages. New messages arrive via Supabase Realtime subscription.","operationId":"listMessages","security":[{"bearerAuth":[]}],"summary":"Get message history","tags":["Chats"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Chat ID"},"required":true,"description":"Chat ID","name":"id","in":"path"},{"schema":{"type":"string","description":"Pagination cursor for loading older messages"},"required":false,"description":"Pagination cursor for loading older messages","name":"cursor","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":100,"default":30,"description":"Number of messages per page","example":30},"required":false,"description":"Number of messages per page","name":"limit","in":"query"}],"responses":{"200":{"description":"Messages retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListMessagesResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/chats/{id}/hide":{"post":{"description":"Hides the chat from the caller's own list. Participation stays intact and the chat reappears once a newer message arrives.","operationId":"hideChat","security":[{"bearerAuth":[]}],"summary":"Hide chat","tags":["Chats"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Chat ID"},"required":true,"description":"Chat ID","name":"id","in":"path"}],"responses":{"204":{"description":"Chat hidden"},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/chats/{id}/unhide":{"post":{"description":"Reverses a hide, putting the chat back into the caller's list.","operationId":"unhideChat","security":[{"bearerAuth":[]}],"summary":"Un-hide chat","tags":["Chats"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Chat ID"},"required":true,"description":"Chat ID","name":"id","in":"path"}],"responses":{"204":{"description":"Chat un-hidden"},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/chats/{id}/leave":{"post":{"description":"Leaves the chat, removing the caller's membership. Rejoining requires a new approval.","operationId":"leaveChat","security":[{"bearerAuth":[]}],"summary":"Leave chat","tags":["Chats"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Chat ID"},"required":true,"description":"Chat ID","name":"id","in":"path"}],"responses":{"204":{"description":"Left the chat"},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/chats/{id}/restore":{"post":{"description":"Organizer-only: restores a soft-deleted chat.","operationId":"restoreChat","security":[{"bearerAuth":[]}],"summary":"Restore chat","tags":["Chats"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Chat ID"},"required":true,"description":"Chat ID","name":"id","in":"path"}],"responses":{"204":{"description":"Chat restored"},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/media/upload-url":{"post":{"description":"Mints a signed upload URL. The client uploads the bytes directly to Storage, then persists the returned `key` on the owning resource (profile photo, message attachment). The `media` bucket is private.","operationId":"createUploadUrl","security":[{"bearerAuth":[]}],"summary":"Create upload URL","tags":["Media"],"requestBody":{"description":"MIME type of the file to upload","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUploadUrlRequest"}}}},"responses":{"201":{"description":"Signed upload URL issued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadUrlResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/media/download-url":{"get":{"description":"Mints a short-lived signed download URL for one of the caller's own objects. Answers 404 when the key is well-formed and owned by the caller but no object sits behind it.","operationId":"createMediaDownloadUrl","security":[{"bearerAuth":[]}],"summary":"Get download URL","tags":["Media"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[0-9a-f-]{36}\\/[0-9a-f-]{36}$","description":"Storage object key","example":"550e8400-e29b-41d4-a716-446655440000/6ba7b810-9dad-11d1-80b4-00c04fd430c8"},"required":true,"description":"Storage object key","name":"key","in":"query"}],"responses":{"200":{"description":"Signed download URL issued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaDownloadUrlResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/media":{"delete":{"description":"Deletes one of the caller's own objects from Storage.","operationId":"deleteMediaObject","security":[{"bearerAuth":[]}],"summary":"Delete object","tags":["Media"],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[0-9a-f-]{36}\\/[0-9a-f-]{36}$","description":"Storage object key","example":"550e8400-e29b-41d4-a716-446655440000/6ba7b810-9dad-11d1-80b4-00c04fd430c8"},"required":true,"description":"Storage object key","name":"key","in":"query"}],"responses":{"204":{"description":"Object deleted"},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/people":{"get":{"operationId":"listPeople","security":[{"bearerAuth":[]}],"summary":"Discover people","tags":["People"],"parameters":[{"schema":{"type":"integer","minimum":19,"maximum":120},"required":false,"name":"ageMax","in":"query"},{"schema":{"type":"integer","minimum":19,"maximum":120},"required":false,"name":"ageMin","in":"query"},{"schema":{"type":"array","items":{"type":"string","enum":["positive","neutral","negative"]}},"required":false,"name":"alcohol","in":"query"},{"schema":{"type":"array","items":{"type":"string","enum":["slim","normal","fit","sportive","muscular","stocky","chubby"]}},"required":false,"name":"bodyType","in":"query"},{"schema":{"type":"string"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"array","items":{"type":"string","enum":["dating","relationship","friendship","chatting"]}},"required":false,"name":"datingPurpose","in":"query"},{"schema":{"type":"number","nullable":true,"minimum":0},"required":false,"name":"distanceMax","in":"query"},{"schema":{"type":"number","nullable":true,"minimum":0},"required":false,"name":"distanceMin","in":"query"},{"schema":{"type":"array","items":{"type":"string","enum":["basic_general","average_general","secondary_special","bachelor","specialty","master","postgraduate"]}},"required":false,"name":"education","in":"query"},{"schema":{"type":"array","items":{"type":"string","enum":["no-dont-want","no-not-soon","no-maybe-in-future","no-but-want-to","already-have"]}},"required":false,"name":"familyPlan","in":"query"},{"schema":{"type":"integer","minimum":55,"maximum":272},"required":false,"name":"heightMax","in":"query"},{"schema":{"type":"integer","minimum":55,"maximum":272},"required":false,"name":"heightMin","in":"query"},{"schema":{"type":"boolean","nullable":true},"required":false,"name":"isMapSelection","in":"query"},{"schema":{"type":"array","items":{"type":"string","enum":["ru","en","de","fr","es","it","pt","tr","ar","zh","ja","ko","uk","be","kk","uz","az","hy","ka","ky"]}},"required":false,"name":"language","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":50,"default":12},"required":false,"name":"limit","in":"query"},{"schema":{"type":"array","items":{"type":"string","enum":["catholicism","orthodoxy","islam","protestantism","buddhism","hinduism","atheism","agnosticism","judaism"]}},"required":false,"name":"religion","in":"query"},{"schema":{"type":"string","enum":["all","male","female"],"default":"all"},"required":false,"name":"sex","in":"query"},{"schema":{"type":"array","items":{"type":"string","enum":["positive","neutral","negative"]}},"required":false,"name":"smoking","in":"query"},{"schema":{"type":"array","items":{"type":"string","enum":["unemployed","studying","irregular-income","low-income","middle-income","wealthy"]}},"required":false,"name":"wealth","in":"query"},{"schema":{"type":"array","items":{"type":"string","enum":["aquarius","pisces","aries","taurus","gemini","cancer","leo","virgo","libra","scorpio","sagittarius","capricorn"]}},"required":false,"name":"zodiac","in":"query"}],"responses":{"200":{"description":"People feed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PeopleFeedResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/people/location":{"patch":{"operationId":"updatePeopleLocation","security":[{"bearerAuth":[]}],"summary":"Update location sharing","tags":["People"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"city":{"type":"string","nullable":true,"maxLength":120},"latitude":{"type":"number","minimum":-90,"maximum":90},"locationSharingMode":{"type":"string","enum":["disabled","static","live"]},"longitude":{"type":"number","minimum":-180,"maximum":180}},"required":["locationSharingMode"]}}}},"responses":{"200":{"description":"Location updated","content":{"application/json":{"schema":{"type":"object","properties":{"locationSharingMode":{"type":"string","enum":["disabled","static","live"]},"locationUpdatedAt":{"type":"string","nullable":true,"format":"date-time"}},"required":["locationSharingMode","locationUpdatedAt"]}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/people/interactions":{"get":{"operationId":"listPeopleInteractions","security":[{"bearerAuth":[]}],"summary":"List profile interactions","tags":["People"],"parameters":[{"schema":{"type":"string","enum":["like","dislike"]},"required":false,"name":"type","in":"query"}],"responses":{"200":{"description":"Active interactions","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PeopleInteraction"}}},"required":["data"]}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/people/incoming-likes":{"get":{"operationId":"listPeopleIncomingLikes","security":[{"bearerAuth":[]}],"summary":"List incoming likes","tags":["People"],"parameters":[{"schema":{"type":"string"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Incoming likes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PeopleIncomingLikesResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/people/matches":{"get":{"operationId":"listPeopleMatches","security":[{"bearerAuth":[]}],"summary":"List people matches","tags":["People"],"parameters":[{"schema":{"type":"string"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"People matches","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PeopleMatchesResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/people/{profileId}":{"get":{"operationId":"getPeopleProfile","security":[{"bearerAuth":[]}],"summary":"View discovered profile","tags":["People"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"profileId","in":"path"}],"responses":{"200":{"description":"Profile details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PeopleProfileDetail"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/people/{profileId}/interaction":{"put":{"operationId":"setPeopleInteraction","security":[{"bearerAuth":[]}],"summary":"Like or dislike profile","tags":["People"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"profileId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["like","dislike"]}},"required":["type"]}}}},"responses":{"200":{"description":"Interaction saved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PeopleInteraction"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"removePeopleInteraction","security":[{"bearerAuth":[]}],"summary":"Remove profile interaction","tags":["People"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"profileId","in":"path"}],"responses":{"200":{"description":"Interaction removed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/people/{userId}/block":{"post":{"operationId":"blockPeopleUser","security":[{"bearerAuth":[]}],"summary":"Block people user","tags":["People"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"userId","in":"path"}],"responses":{"200":{"description":"User blocked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"unblockPeopleUser","security":[{"bearerAuth":[]}],"summary":"Unblock people user","tags":["People"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"userId","in":"path"}],"responses":{"200":{"description":"User unblocked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/people/{userId}/report":{"post":{"operationId":"reportPeopleUser","security":[{"bearerAuth":[]}],"summary":"Report people user","tags":["People"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"userId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"description":{"type":"string","maxLength":250},"reason":{"type":"string","enum":["fake_profile","unacceptable_content","toxic_behavior","incorrect_age","behavior_outside_social_network","other"]}},"required":["reason"]}}}},"responses":{"201":{"description":"User reported","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"success":{"type":"boolean","enum":[true]}},"required":["id","success"]}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/notifications/unread-count":{"get":{"description":"How many unread notifications the user has — for the badge. Backed by a partial index, so it does not scan the read ones.","operationId":"getUnreadNotificationCount","security":[{"bearerAuth":[]}],"summary":"Unread notification count","tags":["Notifications"],"responses":{"200":{"description":"Count retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnreadCountResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/notifications/read":{"post":{"description":"Marks notifications read. Send `ids` for specific ones, or an empty body to clear the whole badge.","operationId":"markNotificationsRead","security":[{"bearerAuth":[]}],"summary":"Mark notifications read","tags":["Notifications"],"requestBody":{"description":"Which notifications to mark","required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkReadRequest"}}}},"responses":{"200":{"description":"Notifications marked read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkReadResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/notifications":{"get":{"description":"The notification feed, newest first, keyset-paginated. Everything a row displays is in `payload`, so no follow-up requests are needed to render a page. New notifications also arrive over Supabase Realtime — subscribe to `notifications` rather than polling this.","operationId":"listNotifications","security":[{"bearerAuth":[]}],"summary":"List notifications","tags":["Notifications"],"parameters":[{"schema":{"type":"string","description":"Keyset cursor from the previous page"},"required":false,"description":"Keyset cursor from the previous page","name":"cursor","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Items per page","example":20},"required":false,"description":"Items per page","name":"limit","in":"query"},{"schema":{"type":"string","enum":["event_application","event_application_approved","event_application_rejected","event_coauthor_invite","event_coauthor_accepted","event_coauthor_refused","friend_request","friend_request_accepted","chat_created","chat_message","people_like","people_match","event_invite","event_invite_accepted"],"description":"Only this kind of notification"},"required":false,"description":"Only this kind of notification","name":"type","in":"query"},{"schema":{"type":"string","description":"Only unread ones","example":"true"},"required":false,"description":"Only unread ones","name":"unreadOnly","in":"query"}],"responses":{"200":{"description":"Notifications retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListNotificationsResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/notifications/{id}":{"delete":{"description":"Dismisses a single notification.","operationId":"deleteNotification","security":[{"bearerAuth":[]}],"summary":"Dismiss a notification","tags":["Notifications"],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Notification ID"},"required":true,"description":"Notification ID","name":"id","in":"path"}],"responses":{"204":{"description":"Notification dismissed"},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/devices/status":{"get":{"description":"Which push providers this deployment can actually deliver through. Empty means notifications are stored and nothing reaches a device — normally because the credentials of a provider are missing from the environment. Senders are resolved once at startup, so this answers for the running process.","operationId":"getPushStatus","security":[{"bearerAuth":[]}],"summary":"Push delivery status","tags":["Push"],"responses":{"200":{"description":"Push status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushStatusResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/devices":{"post":{"description":"Registers this install for push, or refreshes what is already there. Safe to call on every launch and whenever the provider rotates the token. A token belongs to the install, so registering one that another account used moves it to the caller.","operationId":"registerDevice","security":[{"bearerAuth":[]}],"summary":"Register a push token","tags":["Push"],"requestBody":{"description":"Device and its push token","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDeviceRequest"}}}},"responses":{"200":{"description":"Device registered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDeviceResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"description":"Stops push to this install — on sign-out, or when the user turns notifications off.","operationId":"unregisterDevice","security":[{"bearerAuth":[]}],"summary":"Forget a push token","tags":["Push"],"requestBody":{"description":"Token to forget","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnregisterDeviceRequest"}}}},"responses":{"204":{"description":"Device unregistered"},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/reports":{"post":{"description":"Files a complaint against a user, event, or chat. A report targets exactly one entity, chosen by `targetType` + `targetId`.","operationId":"createReport","security":[{"bearerAuth":[]}],"summary":"File a report","tags":["Reports"],"requestBody":{"description":"Report details","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateReportRequest"}}}},"responses":{"201":{"description":"Report filed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportResponse"}}}},"400":{"description":"Bad request - Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Missing or invalid authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}