{"openapi":"3.1.0","info":{"title":"SwingersPlayLive API","version":"1.0.0","description":"JSON API used by the SwingersPlayLive web app and mobile apps. Auth: `Authorization: Bearer <token>` from POST /api/v1/auth/login (kind=app). Envelope: `{ data, meta? }` or `{ error: { code, message } }`. An unknown endpoint answers 404 `not_found` in the same envelope; input containing U+0000 answers 422 `validation_failed`. Language: send `Accept-Language` (the iPhone app sends the BCP 47 tag of its language: de, pt-PT, sr-Latn, zh-Hant…; a `sp_lang` cookie wins over it). Error messages (`message`, `details.fieldErrors`, zod issue messages; codes never change) and the server-built texts of private responses (places, activity and distance labels, plan names in a membership) come in that language when the site serves it, else English, and say so in `Content-Language`; without the header everything is English. Publicly cached endpoints (GET /plans, /places, /clubs, /nudist, /destinations) are English unless an explicit `?locale=<site code>` is given (GET /plans). The iPhone app also sends `X-SP-App: ios` and `X-SP-App-Version` with every request."},"servers":[{"url":"https://swingersplaylive.com/api/v1"}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer"}},"schemas":{"AuthApiError":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}},"AuthCheckInbox":{"type":"object","properties":{"status":{"const":"check_inbox"}}},"AuthSession":{"type":"object","required":["userId","next","expiresAt"],"properties":{"userId":{"type":"string","format":"uuid"},"next":{"type":"string","description":"Where to go next: /verify, /onboarding or /account"},"expiresAt":{"type":"string","format":"date-time","description":"Sliding: extended by use, at most once a day"},"token":{"type":"string","description":"kind=app only. Send as `Authorization: Bearer <token>`."}}},"AuthSignup":{"type":"object","required":["email","password","birthDate","acceptTerms","sensitiveConsent"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":10,"maxLength":200},"birthDate":{"type":"string","format":"date","description":"Must be 18+"},"acceptTerms":{"const":true,"description":"18 or older, and agrees to the Terms of Service and Privacy Policy"},"sensitiveConsent":{"const":true,"description":"Separate checkbox: consent to use profile details that can reveal sexual preferences (GDPR Art. 9)"},"marketingOptIn":{"type":"boolean","default":false},"next":{"type":"string","description":"Site-relative path to land on after verification and onboarding (e.g. /search?place=123)"},"locale":{"type":"string","maxLength":16,"examples":["de","pt","zh-hant"],"description":"The account's language for e-mails (and push notifications of phones registered without a language). Site language code (en, de, fr, … zh-hant). A language the site does not serve is ignored. Absent: the request's language (Accept-Language), else English."}}},"AuthLogin":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"},"kind":{"type":"string","enum":["web","app"],"default":"web"}}},"AuthEmail":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"}}},"AuthMagicLink":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"},"next":{"type":"string","description":"Site-relative path the e-mailed link lands on after login (e.g. /search?place=123); default /account"}}},"AuthTokenKind":{"type":"object","required":["token"],"properties":{"token":{"type":"string"},"kind":{"type":"string","enum":["web","app"],"default":"web"}}},"AuthVerifyEmail":{"type":"object","required":["token"],"properties":{"token":{"type":"string"},"password":{"type":"string","minLength":10,"maxLength":200,"description":"Required on the account's first confirmation; ignored afterwards"},"kind":{"type":"string","enum":["web","app"],"default":"web"}}},"AuthReset":{"type":"object","required":["token","password"],"properties":{"token":{"type":"string"},"password":{"type":"string","minLength":10,"maxLength":200},"kind":{"type":"string","enum":["web","app"],"default":"web"}}},"AuthClaim":{"type":"object","required":["token","acceptTerms","sensitiveConsent"],"properties":{"token":{"type":"string"},"acceptTerms":{"const":true},"sensitiveConsent":{"const":true,"description":"Separate checkbox: consent to use profile details that can reveal sexual preferences (GDPR Art. 9)"},"kind":{"type":"string","enum":["web","app"],"default":"web"},"locale":{"type":"string","maxLength":16,"examples":["de","pt","zh-hant"],"description":"The new account's language for e-mails (and push notifications of phones registered without a language). Site language code (en, de, fr, … zh-hant). A language the site does not serve is ignored. Absent: the request's language (Accept-Language), else English."}}},"Me":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string"},"emailVerified":{"type":"boolean"},"role":{"type":"string"},"ageVerified":{"type":"boolean"},"idVerified":{"type":"boolean"},"marketingOptIn":{"type":"boolean"},"emailNotifications":{"type":"boolean"},"hasPassword":{"type":"boolean"},"locale":{"type":"string","description":"The account's language (site code) for e-mails (and push notifications of phones registered without a language). The app sends PATCH /me/settings { locale } when it differs from the app's language."},"hasSensitiveDataConsent":{"type":"boolean","description":"Explicit consent to sensitive data (GDPR Art. 9) on record. While false, POST /me/profile needs `sensitiveConsent: true`: show the consent checkbox on the onboarding screen."},"createdAt":{"type":"string","format":"date-time"}}},"member":{"oneOf":[{"type":"null"},{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"username":{"type":"string"},"kind":{"type":"string"},"person1BirthYear":{"type":["integer","null"]},"person1BirthMonth":{"type":["integer","null"]},"person2BirthYear":{"type":["integer","null"]},"person2BirthMonth":{"type":["integer","null"]},"headline":{"type":["string","null"]},"about":{"type":["string","null"]},"aboutModeration":{"type":"string","enum":["pending","approved","rejected","removed"]},"lookingFor":{"type":"array","items":{"type":"string"}},"interests":{"type":"array","items":{"type":"string"}},"experience":{"type":["string","null"]},"homePlace":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/MePlaceOption"}]},"publicListing":{"type":"boolean"},"listingStatus":{"type":"string","enum":["listed","hidden","suspended","evicted"]},"incognito":{"type":"boolean","description":"\"Browse privately\" switch as stored; it only takes effect while the account has Premium"},"profileCompleteness":{"type":"integer","minimum":0,"maximum":100},"verified":{"type":"boolean"},"source":{"type":"string","enum":["native","network"]},"claimedAt":{"type":["string","null"],"format":"date-time"},"usernameChangeAvailableAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}}}]},"access":{"type":"object","properties":{"premium":{"type":"boolean"},"travel":{"type":"boolean"},"boost":{"type":"boolean"},"premiumUntil":{"type":["string","null"],"format":"date-time","description":"null with premium=true → lifetime"},"planCode":{"type":["string","null"]}}},"session":{"type":["object","null"],"properties":{"kind":{"type":"string","enum":["web","app"],"default":"web"},"expiresAt":{"type":"string","format":"date-time"}}}}},"MeDelete":{"type":"object","required":["confirm"],"properties":{"confirm":{"const":"DELETE"},"password":{"type":"string"}}},"MeProfileRef":{"type":"object","properties":{"memberId":{"type":"string","format":"uuid"},"username":{"type":"string"}}},"MeProfileCreate":{"type":"object","required":["username","kind","homePlaceId","lookingFor","publicListing"],"properties":{"username":{"type":"string","pattern":"^[A-Za-z0-9_.-]{3,20}$"},"kind":{"type":"string","enum":["couple_mf","couple_ff","couple_mm","woman","man","trans","nonbinary"]},"person1BirthYear":{"type":"integer","description":"Only when the account has no date of birth; otherwise derived from it"},"person1BirthMonth":{"type":"integer","minimum":1,"maximum":12},"person2BirthYear":{"type":"integer","description":"Required for couples; partner must be 18+"},"person2BirthMonth":{"type":"integer","minimum":1,"maximum":12},"homePlaceId":{"type":"integer","description":"From GET /me/place-suggestions"},"lookingFor":{"type":"array","minItems":1,"items":{"type":"string","enum":["couple_mf","couple_ff","couple_mm","woman","man","trans","nonbinary"]}},"about":{"type":"string","maxLength":2000},"publicListing":{"type":"boolean","description":"Explicit consent to appear on public city pages"},"sensitiveConsent":{"const":true,"description":"Separate checkbox: consent to use profile details that can reveal sexual preferences (GDPR Art. 9). Required while GET /me says `user.hasSensitiveDataConsent: false`; omit it otherwise."}}},"MeProfileUpdate":{"type":"object","properties":{"username":{"type":"string","pattern":"^[A-Za-z0-9_.-]{3,20}$"},"kind":{"type":"string","enum":["couple_mf","couple_ff","couple_mm","woman","man","trans","nonbinary"]},"person2BirthYear":{"type":["integer","null"]},"person2BirthMonth":{"type":["integer","null"],"minimum":1,"maximum":12},"homePlaceId":{"type":"integer"},"lookingFor":{"type":"array","minItems":1,"items":{"type":"string","enum":["couple_mf","couple_ff","couple_mm","woman","man","trans","nonbinary"]}},"experience":{"type":["string","null"],"enum":["new","some","experienced",null]},"headline":{"type":["string","null"],"maxLength":80},"about":{"type":["string","null"],"maxLength":2000}}},"MeSettings":{"type":"object","properties":{"emailNotifications":{"type":"boolean"},"marketingOptIn":{"type":"boolean"},"publicListing":{"type":"boolean"},"paused":{"type":"boolean","description":"Hide the profile from search and lists"},"incognito":{"type":"boolean","description":"Premium \"Browse privately\": your profile visits are not recorded. Turning it on without Premium → 402 premium_required; turning it off is always allowed."},"locale":{"type":"string","maxLength":16,"examples":["de","pt","zh-hant"],"description":"The language the member uses the app in, stored for e-mails (and push notifications of phones registered without a language) (GET /me → user.locale). Site language code (en, de, fr, … zh-hant). A language the site does not serve is ignored."}}},"MePassword":{"type":"object","required":["newPassword"],"properties":{"currentPassword":{"type":"string","description":"Required when a password is already set"},"newPassword":{"type":"string","minLength":10,"maxLength":200}}},"MeEmail":{"type":"object","required":["newEmail"],"properties":{"newEmail":{"type":"string","format":"email"},"password":{"type":"string"}}},"MePlaceOption":{"type":"object","properties":{"id":{"type":"integer"},"label":{"type":"string","examples":["Austin, TX","Lyon, France"]},"hint":{"type":["string","null"]},"countryIso2":{"type":"string"}}},"BlockedMember":{"type":"object","required":["username","kind","blockedAt"],"properties":{"username":{"type":"string"},"kind":{"type":"string","enum":["couple_mf","couple_ff","couple_mm","couple","woman","man","trans","nonbinary"]},"blockedAt":{"type":"string","format":"date-time"}}},"ErrorEnvelope":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}},"MemberCard":{"type":"object","description":"Member summary. Never contains coordinates. The profile text only as public pages show it (approved, nothing explicit, no contact details), cut short; a photo thumbnail only for logged-in callers.","required":["username","kind","person1BirthYear"],"properties":{"username":{"type":"string"},"kind":{"type":"string","enum":["couple_mf","couple_ff","couple_mm","couple","woman","man","trans","nonbinary"]},"person1BirthYear":{"anyOf":[{"type":"integer"},{"type":"null"}]},"person1BirthMonth":{"anyOf":[{"type":"integer"},{"type":"null"}]},"person2BirthYear":{"anyOf":[{"type":"integer"},{"type":"null"}]},"person2BirthMonth":{"anyOf":[{"type":"integer"},{"type":"null"}]},"placeName":{"anyOf":[{"type":"string"},{"type":"null"}]},"distanceLabel":{"anyOf":[{"type":"string","description":"Coarse, e.g. \"in town\", \"~5 mi\""},{"type":"null"}]},"lastActivityAt":{"anyOf":[{"type":"string","format":"date-time","description":"UTC day precision (privacy)"},{"type":"null"}]},"verified":{"type":"boolean"},"boosted":{"type":"boolean","description":"Paid Boost placement (shown labelled)"},"about":{"anyOf":[{"type":"string","description":"Start of the profile text public pages show, or null"},{"type":"null"}]},"indexable":{"type":"boolean","description":"The profile page is offered to search engines"},"photoUrl":{"anyOf":[{"type":"string","description":"Access-checked thumbnail (logged-in callers only; GET /api/v1/photos/{id}/file)"},{"type":"null"}]}}},"MemberSearchCard":{"type":"object","description":"Public member card: computed ages (never birth dates), activity to the UTC day, the profile text only as public pages show it.","required":["username","kind","ages","activityLabel","profilePath"],"properties":{"username":{"type":"string"},"kind":{"type":"string","enum":["couple_mf","couple_ff","couple_mm","couple","woman","man","trans","nonbinary"]},"ages":{"type":"array","items":{"type":"integer"}},"placeName":{"anyOf":[{"type":"string"},{"type":"null"}]},"distanceLabel":{"anyOf":[{"type":"string","description":"Coarse, e.g. \"in town\", \"~5 mi\""},{"type":"null"}]},"lastActivityAt":{"anyOf":[{"type":"string","format":"date-time","description":"UTC day precision (privacy)"},{"type":"null"}]},"activityLabel":{"type":"string"},"verified":{"type":"boolean"},"boosted":{"type":"boolean","description":"Paid Boost placement (shown labelled)"},"about":{"anyOf":[{"type":"string","description":"Start of the profile text public pages show, or null"},{"type":"null"}]},"indexable":{"type":"boolean","description":"The profile page is offered to search engines"},"photoUrl":{"type":"string","description":"Logged-in callers only: access-checked thumbnail (GET /api/v1/photos/{id}/file)"},"profilePath":{"type":"string"}}},"MemberSearchMeta":{"type":"object","properties":{"total":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Null for anonymous callers when `restricted`."},"offset":{"type":"integer"},"limit":{"type":"integer"},"totalIsLowerBound":{"type":"boolean","description":"true when more than 2,500 members match (total is then 2500)"},"radiusKm":{"anyOf":[{"type":"number"},{"type":"null"}]},"unit":{"type":"string","enum":["mi","km"]},"center":{"anyOf":[{"type":"object","properties":{"lat":{"type":"number"},"lng":{"type":"number"},"place":{"anyOf":[{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"label":{"type":"string"},"path":{"type":"string"}}},{"type":"null"}]}}},{"type":"null"}]},"restricted":{"type":"boolean","description":"Anonymous callers only: no member list and no number around a place in a high-risk jurisdiction (or for such a country), or around coordinates with no known place nearby"},"restrictedReason":{"anyOf":[{"type":"string","enum":["high_risk_jurisdiction","unknown_jurisdiction"]},{"type":"null"}]}}},"MemberProfileTeaser":{"type":"object","description":"Basic facts; no member id (anonymous and unverified callers).","required":["username","kind","ages","activityLabel","verified","joined","photoCounts"],"properties":{"username":{"type":"string"},"kind":{"type":"string","enum":["couple_mf","couple_ff","couple_mm","couple","woman","man","trans","nonbinary"]},"ages":{"type":"array","items":{"type":"integer"}},"place":{"anyOf":[{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"},"path":{"type":"string"}}},{"type":"null"}]},"activityLabel":{"type":"string"},"lastActivityAt":{"anyOf":[{"type":"string","format":"date-time","description":"UTC day precision (privacy)"},{"type":"null"}]},"verified":{"type":"boolean"},"joined":{"type":"boolean","description":"false = imported network profile not yet activated on SwingersPlayLive"},"photoCounts":{"type":"object","properties":{"public":{"type":"integer"},"members":{"type":"integer"},"private":{"type":"integer"}}},"about":{"anyOf":[{"type":"string","description":"The profile text as public pages show it (publicly listed members; approved, nothing explicit, no contact details), or null"},{"type":"null"}]},"indexable":{"type":"boolean","description":"The profile page is offered to search engines"}}},"MemberPhoto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"url":{"type":"string","description":"Access-checked, same-origin path /api/v1/photos/{id}/file (send the session cookie or bearer token)"},"thumbUrl":{"type":"string","description":"/api/v1/photos/{id}/file?size=thumb"},"width":{"anyOf":[{"type":"integer"},{"type":"null"}]},"height":{"anyOf":[{"type":"integer"},{"type":"null"}]},"visibility":{"type":"string","enum":["public_sfw","members","private"]},"isPrimary":{"type":"boolean"}}},"MemberProfileFull":{"allOf":[{"$ref":"#/components/schemas/MemberProfileTeaser"},{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"uuid","description":"Member id (full profiles only; e.g. for reports)"},"limited":{"type":"boolean","description":"true = imported network profile not yet claimed and not publicly listed: only username, type, ages, city and activity are shown (no texts, preferences, join date or photos)"},"headline":{"anyOf":[{"type":"string"},{"type":"null"}]},"about":{"anyOf":[{"type":"string","description":"Only when approved by moderation"},{"type":"null"}]},"lookingFor":{"type":"array","items":{"type":"string"}},"interests":{"type":"array","items":{"type":"string"}},"experience":{"anyOf":[{"type":"string"},{"type":"null"}]},"memberSince":{"anyOf":[{"type":"string","format":"date-time","description":"null for limited profiles"},{"type":"null"}]},"memberSinceSource":{"anyOf":[{"type":"string","enum":["swingerplay","network"]},{"type":"null"}]},"isPremium":{"type":"boolean"},"photos":{"type":"array","items":{"$ref":"#/components/schemas/MemberPhoto"}},"lockedPhotos":{"type":"object","properties":{"members":{"type":"integer"},"private":{"type":"integer"},"membersReason":{"anyOf":[{"type":"string","enum":["premium_required","age_verification_required"]},{"type":"null"}]}}}}}]},"ProfileRelationship":{"type":"object","properties":{"isSelf":{"type":"boolean"},"conversationId":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"theyStarted":{"type":"boolean","description":"They wrote first → replying is free"},"iStarted":{"type":"boolean"},"isFavorite":{"type":"boolean"},"canStart":{"type":"boolean"},"startBlockedBy":{"anyOf":[{"type":"string","enum":["premium_required","unreachable","no_profile","verify_email","self","existing","suspended"]},{"type":"null"}]}}},"MemberProfileResponse":{"oneOf":[{"type":"object","required":["access","cta","profile"],"properties":{"access":{"const":"teaser"},"cta":{"type":"string","enum":["join","verify_email"]},"profile":{"$ref":"#/components/schemas/MemberProfileTeaser"}}},{"type":"object","required":["access","profile","relationship"],"properties":{"access":{"const":"full"},"profile":{"$ref":"#/components/schemas/MemberProfileFull"},"relationship":{"$ref":"#/components/schemas/ProfileRelationship"}}}]},"ConversationParty":{"allOf":[{"$ref":"#/components/schemas/MemberCard"},{"type":"object","properties":{"joined":{"type":"boolean"},"available":{"type":"boolean"}}}]},"ConversationSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"other":{"$ref":"#/components/schemas/ConversationParty"},"iStarted":{"type":"boolean"},"unread":{"type":"integer"},"lastMessageAt":{"type":"string","format":"date-time"},"lastMessage":{"anyOf":[{"type":"object","properties":{"snippet":{"type":"string"},"fromMe":{"type":"boolean"},"status":{"type":"string","enum":["delivered","pending_review","not_delivered"]},"createdAt":{"type":"string","format":"date-time"}}},{"type":"null"}]},"canSend":{"type":"boolean"},"sendBlockedBy":{"anyOf":[{"type":"string","enum":["premium_required","unavailable","suspended"],"description":"suspended = your own profile is suspended by moderation"},{"type":"null"}]}}},"Message":{"type":"object","properties":{"id":{"type":"integer"},"fromMe":{"type":"boolean"},"body":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"status":{"type":"string","enum":["delivered","pending_review","not_delivered"]},"readAt":{"anyOf":[{"type":"string","format":"date-time","description":"Own messages only, with read receipts (Premium)"},{"type":"null"}]}}},"ConversationThread":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"other":{"$ref":"#/components/schemas/ConversationParty"},"iStarted":{"type":"boolean"},"replyIsFree":{"type":"boolean"},"unread":{"type":"integer"},"lastMessageAt":{"type":"string","format":"date-time"},"canSend":{"type":"boolean"},"sendBlockedBy":{"anyOf":[{"type":"string","enum":["premium_required","unavailable","suspended"],"description":"suspended = your own profile is suspended by moderation"},{"type":"null"}]},"hasOlder":{"type":"boolean"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/Message"}}}},"SendMessageResult":{"type":"object","properties":{"conversationId":{"type":"string","format":"uuid"},"messageId":{"type":"integer"},"status":{"type":"string","enum":["delivered","pending_review"]},"createdConversation":{"type":"boolean"},"recipientJoined":{"type":"boolean","description":"false → the recipient is notified by e-mail and replies for free"}}},"TravelPlanInput":{"type":"object","required":["startDate","endDate"],"properties":{"destinationId":{"type":"integer","minimum":1,"maximum":2147483647},"placeId":{"type":"integer","minimum":1,"maximum":2147483647},"startDate":{"type":"string","format":"date"},"endDate":{"type":"string","format":"date"},"note":{"type":"string","maxLength":280}}},"TravelPlan":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"startDate":{"type":"string","format":"date"},"endDate":{"type":"string","format":"date"},"note":{"anyOf":[{"type":"string"},{"type":"null"}]},"target":{"type":"object","properties":{"type":{"type":"string","enum":["destination","place"]},"id":{"type":"integer"},"name":{"type":"string"},"path":{"type":"string"}}},"createdAt":{"type":"string","format":"date-time"}}},"TravellerCard":{"allOf":[{"$ref":"#/components/schemas/MemberCard"},{"type":"object","properties":{"startDate":{"type":"string","format":"date"},"endDate":{"type":"string","format":"date"},"note":{"anyOf":[{"type":"string"},{"type":"null"}]}}}]},"BillingError":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string","description":"Human-readable, safe to show"},"details":{"type":"object","properties":{"reason":{"type":"string","enum":["unknown_plan","account_inactive","email_unverified","payments_unavailable","consent_required","already_subscribed","already_lifetime","travel_included","sold_out","cancel_subscription_first","paid_until"]}}}}}}},"BillingPlan":{"type":"object","properties":{"code":{"type":"string","examples":["premium_12m"]},"name":{"type":"string"},"kind":{"type":"string","enum":["subscription","one_time"]},"priceCents":{"type":"integer"},"currency":{"type":"string","const":"USD"},"periodDays":{"anyOf":[{"type":"integer"},{"type":"null"}]},"perMonthCents":{"anyOf":[{"type":"integer"},{"type":"null"}]},"autoRenews":{"type":"boolean"},"renewalPriceCents":{"anyOf":[{"type":"integer"},{"type":"null"}]},"renewalTerms":{"type":"string","description":"Show next to the price and above the buy button"},"consentText":{"anyOf":[{"type":"string","description":"Label of the required auto-renewal checkbox (recurring plans)"},{"type":"null"}]},"grants":{"type":"array","items":{"type":"string","enum":["premium","travel","boost"]}},"badge":{"anyOf":[{"type":"string"},{"type":"null"}]},"highlight":{"type":"boolean"},"blurb":{"type":"string","description":"The website's blurb (it may carry the website's renewal and cancel wording)"},"appBlurb":{"type":"string","description":"The blurb the apps show next to a store's price: the plan's app blurb, else the website blurb without its \"Renews every …\" sentence (and so without \"cancel anytime in one click\")"},"limited":{"anyOf":[{"type":"object","properties":{"total":{"type":"integer"},"remaining":{"anyOf":[{"type":"integer"},{"type":"null"}]}}},{"type":"null"}]},"appleProductId":{"anyOf":[{"type":"string","description":"iOS app: App Store product id to buy this plan with In-App Purchase (null: not sold in the app)"},{"type":"null"}]},"googleProductId":{"anyOf":[{"type":"string","description":"Android app: Google Play product id to buy this plan with Play Billing (null: not sold in the app)"},{"type":"null"}]}}},"BillingPlanList":{"type":"object","properties":{"currency":{"type":"string"},"couplesShareOneMembership":{"type":"boolean"},"paymentsAvailable":{"type":"boolean","description":"false: nothing can be bought yet (checkout answers payments_unavailable)"},"appleIapAvailable":{"type":"boolean","description":"iOS app: In-App Purchase is open (POST /billing/apple/intent). Independent of paymentsAvailable."},"googlePlayBillingAvailable":{"type":"boolean","description":"Android app: Google Play Billing is open (POST /billing/google/intent). Independent of paymentsAvailable."},"plans":{"type":"array","items":{"$ref":"#/components/schemas/BillingPlan"}},"features":{"type":"array","items":{"type":"object","properties":{"feature":{"type":"string"},"label":{"type":"string"},"detail":{"anyOf":[{"type":"string"},{"type":"null"}]},"access":{"type":"string","enum":["anonymous","registered","premium","travel","boost"]},"accessLabel":{"type":"string"}}}}}},"BillingPaymentsNotice":{"type":"object","properties":{"requested":{"type":"boolean","description":"The member will get the one-time e-mail when payments open"},"emailNotifications":{"type":"boolean","description":"false: notification e-mails are off, so the e-mail will not be sent"},"paymentsAvailable":{"type":"boolean"}}},"BillingCheckoutRequest":{"type":"object","required":["planCode"],"properties":{"planCode":{"type":"string"},"acceptRenewalTerms":{"type":"boolean","default":false}}},"BillingAppleIntentRequest":{"type":"object","required":["planCode"],"properties":{"planCode":{"type":"string"},"acceptRenewalTerms":{"type":"boolean","default":false},"displayedPrice":{"type":"string","maxLength":100,"description":"StoreKit's `displayPrice` as the consent showed it, e.g. \"26,99 €\" (stored with the consent; without it the plan's USD consentText is stored)"}}},"BillingFunnelStep":{"description":"A step of the membership order flow the app saw. `page`: the membership screen was shown (`planCode`: the plan of a \"Choose\" link that led there). `plan`: a subscription's renewal consent was ticked. `cancelled`: the store's payment left the order unpaid: `sheet_closed` or `play_error` (Google Play), `app_store_closed` (the member closed the App Store payment sheet), `app_store_error` (a StoreKit or App Store error before payment) or `app_store_pending` (Ask to Buy or a deferred payment, no answer after the wait). Never `checkout`, `refused` or `paid`: the server records those itself.","oneOf":[{"type":"object","required":["step"],"properties":{"step":{"const":"page"},"planCode":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}]}}},{"type":"object","required":["step","planCode"],"properties":{"step":{"const":"plan"},"planCode":{"type":"string","maxLength":40}}},{"type":"object","required":["step","planCode","detail"],"properties":{"step":{"const":"cancelled"},"planCode":{"type":"string","maxLength":40},"detail":{"type":"string","enum":["sheet_closed","play_error","app_store_closed","app_store_error","app_store_pending"]}}}]},"BillingAppleIntent":{"type":"object","properties":{"appAccountToken":{"type":"string","format":"uuid","description":"StoreKit purchase option appAccountToken"},"productId":{"type":"string"},"planCode":{"type":"string"},"priceCents":{"type":"integer","description":"List price in USD cents (Apple charges its own price point in the member's currency)"}}},"BillingGoogleIntentRequest":{"type":"object","required":["planCode"],"properties":{"planCode":{"type":"string"},"acceptRenewalTerms":{"type":"boolean","default":false},"displayedPrice":{"type":"string","description":"Google Play's price as the consent showed it, e.g. \"26,99 €\" (stored with the consent)"}}},"BillingGoogleIntent":{"type":"object","properties":{"nonce":{"type":"string","description":"Post it back with the purchase token"},"productId":{"type":"string","description":"Google Play product id (the Payment Request `sku`)"},"planCode":{"type":"string"},"priceCents":{"type":"integer","description":"List price in USD cents (Google charges its own price in the member's currency; show getDetails() prices)"}}},"BillingGooglePurchases":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"string"},"planCode":{"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string","enum":["applied","duplicate","pending","rejected","unlinked","other_account","invalid"]},"code":{"type":"string"}}}},"access":{"$ref":"#/components/schemas/BillingAccess"}}},"BillingAppleTransactions":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"transactionId":{"anyOf":[{"type":"string"},{"type":"null"}]},"productId":{"anyOf":[{"type":"string"},{"type":"null"}]},"planCode":{"anyOf":[{"type":"string"},{"type":"null"}]},"status":{"type":"string","enum":["applied","duplicate","pending","rejected","unlinked","other_account","invalid"]},"code":{"type":"string"}}}},"access":{"$ref":"#/components/schemas/BillingAccess"}}},"BillingCheckoutResponse":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"provider":{"type":"string","enum":["ccbill","dev"]},"priceCents":{"type":"integer"}}},"BillingPurchaseBlockers":{"type":"object","description":"By plan code: why that plan's checkout would be refused right now. A plan left out can be bought.","additionalProperties":{"type":"object","required":["reason","message"],"properties":{"reason":{"type":"string","enum":["account_inactive","email_unverified","already_subscribed","already_lifetime","travel_included","sold_out","cancel_subscription_first","paid_until"]},"message":{"type":"string","description":"The refusal in the request's language, safe to show"}}}},"BillingAccess":{"type":"object","properties":{"premium":{"type":"boolean"},"travel":{"type":"boolean"},"boost":{"type":"boolean"},"premiumUntil":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"planCode":{"anyOf":[{"type":"string"},{"type":"null"}]}},"description":"premium=true with premiumUntil=null means lifetime"},"BillingMembership":{"type":"object","properties":{"access":{"$ref":"#/components/schemas/BillingAccess"},"subscription":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"planCode":{"type":"string"},"planName":{"type":"string"},"status":{"type":"string","enum":["pending","active","paused","past_due","canceled","expired"]},"priceCents":{"type":"integer","description":"Locked renewal price. For play_store it is only the plan's USD list price, not what Google charges: do not show it"},"currency":{"type":"string"},"autoRenew":{"type":"boolean","description":"The payment processor may still charge it. Can be true with status=expired (access ended, renewal never stopped): offer /billing/cancel."},"providerCancelPending":{"type":"boolean","description":"Auto-renewal is off on our side; the payment processor has not confirmed it yet"},"periodDays":{"anyOf":[{"type":"integer"},{"type":"null"}]},"currentPeriodEnd":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"accessUntil":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"canceledAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"pausedUntil":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"provider":{"type":"string","enum":["ccbill","dev","apple","google"]},"billedBy":{"type":"string","enum":["website","app_store","play_store"],"description":"app_store: bought in the iPhone app (Apple bills it); play_store: bought in the Android app (Google Play bills it). For both, show manageUrl for auto-renewal, never the cancel / pause / withdraw actions."},"manageUrl":{"anyOf":[{"type":"string","format":"uri","description":"The store's Subscriptions page (App Store and Google Play purchases only)"},{"type":"null"}]}}},{"type":"null"}]},"heldPrice":{"anyOf":[{"type":"object","properties":{"planCode":{"type":"string"},"planName":{"type":"string"},"priceCents":{"type":"integer"},"until":{"type":"string","format":"date-time"}}},{"type":"null"}]},"grants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["travel_pass","boost","lifetime","gift"]},"label":{"type":"string"},"startsAt":{"type":"string","format":"date-time"},"endsAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"source":{"type":"string","enum":["purchase","loyalty","admin"]}}}},"receipts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"date":{"type":"string","format":"date-time"},"type":{"type":"string"},"description":{"type":"string"},"amountCents":{"anyOf":[{"type":"integer","description":"Negative = refunded/charged back. null: no money moved, under review, or a Google Play order (Google charges its own price)"},{"type":"null"}]},"currency":{"type":"string"},"underReview":{"type":"boolean"}}}},"withdrawal":{"anyOf":[{"type":"object","properties":{"target":{"type":"string","enum":["subscription","grant"]},"id":{"type":"string"},"planName":{"type":"string"},"until":{"type":"string","format":"date-time"}}},{"type":"null"}]},"paymentsAvailable":{"type":"boolean"},"founder":{"anyOf":[{"type":"object","properties":{"total":{"type":"integer"},"sold":{"type":"integer"},"remaining":{"type":"integer"}}},{"type":"null"}]}}},"ModerationApiError":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}},"Photo":{"type":"object","required":["id","status","visibility","isPrimary","url","thumbUrl"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","approved","rejected","removed"]},"visibility":{"type":"string","enum":["public_sfw","members","private"]},"isPrimary":{"type":"boolean"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"rejectionReason":{"type":["string","null"],"description":"Statement of reasons shown to the owner"},"inReview":{"type":"boolean","description":"Held: the same image as a photo hidden while a report about it is reviewed; checked again once that report is decided."},"automated":{"type":"boolean","description":"Rejected or removed by an automated decision, no person involved: the automated photo check, an automatic removal after a report, or a match with an image removed for good"},"underReport":{"type":"boolean","description":"Hidden while a report is reviewed (or a child-safety case): it cannot be deleted or changed until the review ends"},"createdAt":{"type":"string","format":"date-time"},"url":{"type":["string","null"],"description":"Relative URL of the image (null when locked)"},"thumbUrl":{"type":["string","null"]},"locked":{"type":["string","null"],"enum":["login_required","age_verification_required","premium_required",null]}}},"PhotoUploadResult":{"type":"object","required":["file","ok"],"properties":{"file":{"type":"string"},"ok":{"type":"boolean"},"photoId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","approved","rejected"]},"reasons":{"type":"array","items":{"type":"string","enum":["csam_match","child_safety","minor_detected","age_uncertain_with_nudity","explicit_sexual_content","nudity_not_allowed","suggestive_not_allowed_public","bestiality","violence_gore","weapon","drugs","hate_symbol","self_harm","text_contact_info","text_unreadable","blocked_media","duplicate","removed_by_moderator","removed_after_report","hidden_pending_review","match_released"]}},"message":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}},"ReportInput":{"type":"object","required":["targetType","targetId","reason"],"properties":{"targetType":{"type":"string","enum":["member","photo","message","club","nudist_place","url"],"description":"Use `url` with any pasted link"},"targetId":{"type":"string","maxLength":500,"description":"uuid, username, numeric id, country/slug or URL"},"reason":{"type":"string","enum":["underage","non_consensual","nudity","illegal_content","prostitution","trafficking","harassment","hate","spam","fake_profile","impersonation","stolen_photos","copyright","other"]},"details":{"type":"string","maxLength":4000},"email":{"type":"string","format":"email","description":"Required when not signed in, except for reason `underage`"}}},"AppealInput":{"type":"object","required":["targetType","targetId","statement"],"properties":{"targetType":{"type":"string","enum":["member","profile_text","message"],"description":"member = profile suspension, profile_text = removed profile text (targetId = your member id)"},"targetId":{"type":"string"},"statement":{"type":"string","minLength":10,"maxLength":4000}}},"Appeal":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"targetType":{"type":"string"},"targetId":{"type":"string"},"status":{"type":"string","enum":["open","actioned","dismissed"],"description":"actioned = granted, dismissed = denied"},"createdAt":{"type":"string","format":"date-time"},"resolvedAt":{"type":["string","null"],"format":"date-time"}}},"VerificationState":{"type":"object","properties":{"ageVerified":{"type":"boolean"},"idVerified":{"type":"boolean"},"providerAvailable":{"type":"boolean"},"provider":{"type":"string"},"session":{"description":"Only with ?session=: that session's outcome, null when it is not one of yours","oneOf":[{"type":"null"},{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"level":{"type":"string","enum":["age","id"]},"status":{"type":"string","enum":["pending","approved","declined","underage"]}}}]}}},"PlaceSearchHit":{"type":"object","required":["id","name","path","href","country","members","ownMembers"],"properties":{"id":{"type":"integer"},"name":{"type":"string"},"path":{"type":"string","examples":["united-states/texas/austin"]},"href":{"type":"string","examples":["/swingers/united-states/texas/austin"]},"region":{"oneOf":[{"type":"null"},{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"}}}]},"country":{"type":"object","properties":{"iso2":{"type":"string"},"name":{"type":"string"}}},"members":{"type":["integer","null"],"description":"Listed members within the place radius (public or not); null when not published (high-risk jurisdiction)"},"ownMembers":{"type":["integer","null"],"description":"Members whose home is this place; null when not published (high-risk jurisdiction)"},"population":{"type":["integer","null"]}}},"PlaceCountry":{"type":"object","properties":{"iso2":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"continent":{"type":["string","null"]},"memberCount":{"type":["integer","null"],"description":"All listed members; null when not published (high-risk jurisdiction)"},"publicMemberCount":{"type":["integer","null"],"description":"Of memberCount: members shown by username to anonymous visitors; null when not published (high-risk jurisdiction)"},"clubCount":{"type":"integer"},"nudistCount":{"type":"integer"},"indexable":{"type":"boolean"},"restricted":{"type":"boolean","description":"High-risk jurisdiction: never indexable; member numbers and member lists are never published"},"restrictedReason":{"type":["string","null"],"enum":["high_risk_jurisdiction",null]},"dataUpdatedAt":{"type":["string","null"],"format":"date-time"},"webPath":{"type":"string"}}},"PlaceRegion":{"type":"object","properties":{"id":{"type":"integer"},"code":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"memberCount":{"type":["integer","null"],"description":"All listed members; null when not published (high-risk jurisdiction)"},"publicMemberCount":{"type":["integer","null"],"description":"Of memberCount: members shown by username to anonymous visitors; null when not published (high-risk jurisdiction)"},"clubCount":{"type":"integer"},"nudistCount":{"type":"integer"},"indexable":{"type":"boolean"},"restricted":{"type":"boolean","description":"High-risk jurisdiction: never indexable; member numbers and member lists are never published"},"restrictedReason":{"type":["string","null"],"enum":["high_risk_jurisdiction",null]},"dataUpdatedAt":{"type":["string","null"],"format":"date-time"},"webPath":{"type":"string"}}},"PlaceLinkItem":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"path":{"type":"string"},"webPath":{"type":"string"},"regionCode":{"type":["string","null"]},"population":{"type":["integer","null"]},"ownMembers":{"type":["integer","null"],"description":"Members whose home is this place; null when not published (high-risk jurisdiction)"},"radiusMembers":{"type":["integer","null"],"description":"Listed members within the place radius; null when not published (high-risk jurisdiction)"},"radiusPublicMembers":{"type":["integer","null"],"description":"Of radiusMembers: members shown by username to anonymous visitors; null when not published (high-risk jurisdiction)"},"indexable":{"type":"boolean"},"restricted":{"type":"boolean","description":"High-risk jurisdiction: never indexable; member numbers and member lists are never published"},"distanceKm":{"type":"number"},"distanceLabel":{"type":"string"}}},"PlaceVenue":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"kind":{"type":"string"},"locality":{"type":["string","null"]},"distanceKm":{"type":"number"},"distanceLabel":{"type":"string","examples":["~5 mi","in town"]},"featured":{"type":"boolean","description":"Paid placement (always labelled)"},"webPath":{"type":["string","null"],"description":"Detail page, when the venue has one"}}},"PlaceClub":{"allOf":[{"$ref":"#/components/schemas/PlaceVenue"},{"type":"object","required":["status","statusLabel"],"properties":{"status":{"type":"string","enum":["active","unverified"],"description":"As `status` in /clubs: `active` = a current source (OpenStreetMap, the venue or its website), counted in radiusClubs; `unverified` = an older SwingersPlay network listing that could not be verified since and may have closed (never counted)"},"statusLabel":{"type":"string","examples":["Current listing","Older listing, not yet verified (it may have closed)"]}}}]},"PlaceDetail":{"type":"object","properties":{"id":{"type":"integer"},"kind":{"type":"string","enum":["us_place","world_city","area"]},"name":{"type":"string"},"path":{"type":"string"},"webPath":{"type":"string"},"lat":{"type":"number"},"lng":{"type":"number"},"population":{"type":["integer","null"]},"timezone":{"type":["string","null"]},"radius":{"type":"object","properties":{"value":{"type":"integer"},"unit":{"type":"string","enum":["mi","km"]},"label":{"type":"string"}}},"counts":{"type":"object","properties":{"ownMembers":{"type":["integer","null"],"description":"Members whose home is this place; null when not published (high-risk jurisdiction)"},"radiusMembers":{"type":["integer","null"],"description":"Listed members within the radius; null when not published (high-risk jurisdiction)"},"ownPublicMembers":{"type":["integer","null"],"description":"Of ownMembers: shown by username to anonymous visitors; null when not published (high-risk jurisdiction)"},"radiusPublicMembers":{"type":["integer","null"],"description":"Of radiusMembers: shown by username to anonymous visitors; null when not published (high-risk jurisdiction)"},"radiusCouples":{"type":["integer","null"],"description":"Couples within the radius; null when not published (high-risk jurisdiction)"},"radiusWomen":{"type":["integer","null"],"description":"Single women within the radius; null when not published (high-risk jurisdiction)"},"radiusMen":{"type":["integer","null"],"description":"Single men within the radius; null when not published (high-risk jurisdiction)"},"ownClubs":{"type":"integer"},"radiusClubs":{"type":"integer"},"ownNudistPlaces":{"type":"integer"},"radiusNudistPlaces":{"type":"integer"}}},"indexable":{"type":"boolean"},"restricted":{"type":"boolean","description":"High-risk jurisdiction: never indexable; member numbers and member lists are never published"},"restrictedReason":{"type":["string","null"],"enum":["high_risk_jurisdiction",null]},"hubPlaceId":{"type":["integer","null"]},"dataUpdatedAt":{"type":["string","null"],"format":"date-time"},"membersEndpoint":{"type":"string"}}},"PlacesCountryResponse":{"type":"object","properties":{"type":{"const":"country"},"country":{"$ref":"#/components/schemas/PlaceCountry"},"regions":{"type":"array","items":{"$ref":"#/components/schemas/PlaceRegion"}},"places":{"type":"array","items":{"$ref":"#/components/schemas/PlaceLinkItem"}}}},"PlacesRegionResponse":{"type":"object","properties":{"type":{"const":"region"},"country":{"$ref":"#/components/schemas/PlaceCountry"},"region":{"$ref":"#/components/schemas/PlaceRegion"},"places":{"type":"array","items":{"$ref":"#/components/schemas/PlaceLinkItem"}}}},"PlacesPlaceResponse":{"type":"object","properties":{"type":{"const":"place"},"place":{"$ref":"#/components/schemas/PlaceDetail"},"region":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PlaceRegion"}]},"country":{"$ref":"#/components/schemas/PlaceCountry"},"hub":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PlaceLinkItem"}]},"nearby":{"type":"array","items":{"$ref":"#/components/schemas/PlaceLinkItem"}},"clubs":{"type":"array","items":{"$ref":"#/components/schemas/PlaceClub"},"description":"Up to 12 clubs with a current source (status active) within the place radius, featured first, then closest"},"olderListings":{"type":"array","items":{"$ref":"#/components/schemas/PlaceClub"},"description":"Older, unverified club listings (status unverified) within the radius, among the 12 nearest listed clubs: they may have closed, are never counted and are shown apart on the web page"},"nudistPlaces":{"type":"array","items":{"$ref":"#/components/schemas/PlaceVenue"}}}},"PlaceLocalLink":{"allOf":[{"$ref":"#/components/schemas/PlaceLinkItem"},{"type":"object","properties":{"label":{"type":"string","examples":["Austin, TX"],"description":"Name with the state code"},"distanceKm":{"type":"number","description":"In near.places: from the caller's community (absent for the community itself and in other lists)"},"distanceLabel":{"type":"string","description":"Deliberately coarse, e.g. ~15 mi"}}}]},"PlacesLocalResponse":{"type":"object","required":["near","state"],"properties":{"near":{"type":["object","null"],"required":["place","radius","counts","members","places"],"properties":{"place":{"$ref":"#/components/schemas/PlaceLocalLink","description":"The caller's community: the centre of the 50 miles"},"radius":{"type":"object","properties":{"value":{"type":"integer","examples":[50]},"unit":{"type":"string","enum":["mi"]},"km":{"type":"number"}}},"counts":{"type":"object","description":"Listed members within the radius","properties":{"members":{"type":"integer"},"publicMembers":{"type":"integer","description":"Of members: those shown by username without an account"},"couples":{"type":"integer"},"women":{"type":"integer"},"men":{"type":"integer"}}},"members":{"type":"array","items":{"$ref":"#/components/schemas/PlaceMemberCard"},"description":"Up to 6 public members within the radius: people who live in the community first, then by distance"},"places":{"type":"array","items":{"$ref":"#/components/schemas/PlaceLocalLink"},"description":"Up to 9 communities with members within the radius, the caller's own first, then by members"}}},"state":{"type":["object","null"],"required":["region","places","members"],"properties":{"region":{"type":"object","properties":{"id":{"type":"integer"},"code":{"type":"string","examples":["TX"]},"name":{"type":"string"},"slug":{"type":"string"},"memberCount":{"type":"integer"},"publicMemberCount":{"type":"integer"},"webPath":{"type":"string"}}},"places":{"type":"array","items":{"$ref":"#/components/schemas/PlaceLocalLink"},"description":"Up to 12 biggest scenes of the state, by members who live there"},"members":{"type":"array","items":{"$ref":"#/components/schemas/PlaceMemberCard"},"description":"Up to 6 recently active public members of the state, not already in near.members"}}}}},"PlaceMemberCard":{"type":"object","required":["username","kind","ages","activityLabel","profilePath"],"properties":{"username":{"type":"string"},"kind":{"type":"string","enum":["couple_mf","couple_ff","couple_mm","couple","woman","man","trans","nonbinary"]},"ages":{"type":"array","items":{"type":"integer"},"description":"Current ages (one per person; two for couples). Birth dates are never exposed."},"placeName":{"type":["string","null"]},"distanceLabel":{"type":["string","null"],"description":"Deliberately coarse, e.g. ~10 km"},"lastActivityAt":{"type":["string","null"],"format":"date-time","description":"UTC day precision (privacy): always midnight UTC"},"activityLabel":{"type":"string","examples":["Active this week","Active this month"],"description":"The same coarse label the web pages show"},"verified":{"type":"boolean"},"boosted":{"type":"boolean"},"isOwn":{"type":"boolean","description":"Lives in this place"},"about":{"type":["string","null"],"description":"Start of the profile text public pages show (approved, nothing explicit, no contact details), or null"},"profilePath":{"type":"string"}}},"VenuesError":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}},"OsmAttribution":{"type":"object","properties":{"attribution":{"type":"string","example":"© OpenStreetMap contributors"},"license":{"type":"string","example":"ODbL 1.0, © OpenStreetMap contributors"},"licenseUrl":{"type":"string","format":"uri"}}},"VenueListMeta":{"allOf":[{"$ref":"#/components/schemas/OsmAttribution"},{"type":"object","properties":{"total":{"type":"integer","description":"Country lists only."},"count":{"type":"integer","description":"`near` lists only."},"offset":{"type":"integer"},"limit":{"type":"integer"},"radiusKm":{"type":"number"},"near":{"type":"object","properties":{"lat":{"type":"number"},"lng":{"type":"number"}}},"country":{"$ref":"#/components/schemas/Country"}}}]},"Country":{"type":"object","properties":{"iso2":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"radiusUnit":{"type":"string","enum":["mi","km"]}}},"CountryCount":{"allOf":[{"$ref":"#/components/schemas/Country"},{"type":"object","properties":{"count":{"type":"integer"}}}]},"ClubCountryCount":{"allOf":[{"$ref":"#/components/schemas/CountryCount"},{"type":"object","properties":{"listed":{"type":"integer"}}}]},"Club":{"type":"object","properties":{"id":{"type":"integer"},"slug":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["club","party","resort","sauna","bar","other"]},"kindLabel":{"type":"string"},"country":{"type":"string","description":"ISO alpha-2"},"countrySlug":{"type":"string"},"city":{"type":["string","null"],"description":"Town the venue is in (null when only a distant community is known)."},"region":{"type":["string","null"]},"placePath":{"type":["string","null"],"description":"Path below /swingers/ of the community page (may be a nearby community, not the venue's own town)."},"lat":{"type":["number","null"]},"lng":{"type":["number","null"]},"geoPrecision":{"type":"string","enum":["exact","city"]},"verification":{"type":"string","enum":["owner","osm","listing","unverified"]},"verificationLabel":{"type":"string"},"status":{"type":"string","enum":["active","unverified","closed"]},"featured":{"type":"boolean","description":"Paid placement; always labelled as Featured."},"lastVerifiedAt":{"type":["string","null"],"format":"date-time"},"url":{"type":["string","null"],"format":"uri","description":"Web page, when the club has one."},"distanceKm":{"type":"number","description":"Only in `near` results."}}},"ClubDetail":{"allOf":[{"$ref":"#/components/schemas/Club"},{"type":"object","properties":{"address":{"type":["string","null"],"description":"Full address line to show: street, postcode and town, each once (as on the web page).","example":"Sommerkellerweg 20, 82256 Fürstenfeldbruck"},"street":{"type":["string","null"],"description":"The street and house number of `address` only.","example":"Sommerkellerweg 20"},"postcode":{"type":["string","null"]},"locality":{"type":["string","null"],"description":"Postal town of the address. `city` can instead name the community the club is in or next to.","example":"Fürstenfeldbruck"},"phone":{"type":["string","null"]},"website":{"type":["string","null"]},"openingHours":{"type":["string","null"],"description":"OpenStreetMap opening_hours syntax, as published."},"description":{"type":["string","null"]},"audience":{"type":"array","items":{"type":"string"}},"amenities":{"type":"array","items":{"type":"string"}},"sources":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"url":{"type":["string","null"]},"seenAt":{"type":["string","null"]}}}},"osm":{"type":["object","null"],"properties":{"type":{"type":"string","enum":["node","way","relation","n","w","r"]},"id":{"type":"integer"},"url":{"type":"string","format":"uri"},"attribution":{"type":"string","example":"© OpenStreetMap contributors"},"license":{"type":"string","example":"ODbL 1.0, © OpenStreetMap contributors"}}},"updatedAt":{"type":"string","format":"date-time"},"place":{"type":["object","null"],"properties":{"id":{"type":"integer"},"name":{"type":"string"},"path":{"type":"string"},"url":{"type":"string","format":"uri"}}},"membersNearby":{"type":["object","null"],"description":"Null when there is no centre to count from, or in a high-risk jurisdiction (`restricted`).","properties":{"count":{"type":"integer"},"radius":{"type":"string","example":"25 miles"}}},"restricted":{"type":"boolean","description":"High-risk jurisdiction (the venue, or the community its figures come from): member numbers and member lists are never published"},"restrictedReason":{"type":["string","null"],"enum":["high_risk_jurisdiction",null]},"nearby":{"type":"array","items":{"$ref":"#/components/schemas/Club"}}}}]},"NudistPlace":{"type":"object","properties":{"id":{"type":"integer"},"slug":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["beach","campsite","resort","sauna","spa","pool","park","trail","other"]},"kindLabel":{"type":"string"},"nudism":{"type":["string","null"],"description":"OSM nudism=* value (designated, yes, customary, permissive, obligatory…)."},"nudismLabel":{"type":["string","null"]},"country":{"type":"string"},"countrySlug":{"type":"string"},"city":{"type":["string","null"]},"region":{"type":["string","null"]},"placePath":{"type":["string","null"]},"lat":{"type":"number"},"lng":{"type":"number"},"status":{"type":"string"},"url":{"type":["string","null"],"format":"uri"},"osm":{"type":["object","null"],"properties":{"type":{"type":"string","enum":["node","way","relation","n","w","r"]},"id":{"type":"integer"},"url":{"type":"string","format":"uri"},"attribution":{"type":"string","example":"© OpenStreetMap contributors"},"license":{"type":"string","example":"ODbL 1.0, © OpenStreetMap contributors"}}},"distanceKm":{"type":"number"}}},"NudistPlaceDetail":{"allOf":[{"$ref":"#/components/schemas/NudistPlace"},{"type":"object","properties":{"website":{"type":["string","null"]},"description":{"type":["string","null"]},"facts":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"string"}}}},"tags":{"type":"object","additionalProperties":{"type":"string"},"description":"Raw OpenStreetMap tags (ODbL)."},"sourceUpdatedAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"place":{"type":["object","null"],"properties":{"id":{"type":"integer"},"name":{"type":"string"},"path":{"type":"string"},"url":{"type":"string","format":"uri"}}},"membersNearby":{"type":["object","null"],"description":"Null in a high-risk jurisdiction (`restricted`).","properties":{"count":{"type":"integer"},"radius":{"type":"string"}}},"restricted":{"type":"boolean","description":"High-risk jurisdiction (the venue, or the community its figures come from): member numbers and member lists are never published"},"restrictedReason":{"type":["string","null"],"enum":["high_risk_jurisdiction",null]},"nearby":{"type":"array","items":{"$ref":"#/components/schemas/NudistPlace"}}}}]},"Destination":{"type":"object","properties":{"id":{"type":"integer"},"slug":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["naturist_village","resort","island","city","region","cruise","beach"]},"country":{"type":["object","null"],"properties":{"iso2":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}}},"summary":{"type":"string"},"lat":{"type":["number","null"]},"lng":{"type":["number","null"]},"reviewedAt":{"type":["string","null"],"format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"url":{"type":"string","format":"uri"}}},"DestinationDetail":{"allOf":[{"$ref":"#/components/schemas/Destination"},{"type":"object","properties":{"bodyMd":{"type":"string","description":"Editorial Markdown (GFM)."},"facts":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"string"}}}},"faq":{"type":"array","items":{"type":"object","properties":{"q":{"type":"string"},"a":{"type":"string"}}}},"sources":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"url":{"type":"string"}}}},"bestMonths":{"type":"array","items":{"type":"integer","minimum":1,"maximum":12}},"radiusKm":{"type":"integer","description":"Search radius for the nearby club and naturist-place lists."},"place":{"type":["object","null"],"properties":{"id":{"type":"integer"},"name":{"type":"string"},"path":{"type":"string"},"url":{"type":"string","format":"uri"}}},"restricted":{"type":"boolean","description":"High-risk jurisdiction (the venue, or the community its figures come from): member numbers and member lists are never published"},"restrictedReason":{"type":["string","null"],"enum":["high_risk_jurisdiction",null]},"stats":{"type":"object","description":"Every figure is null in a high-risk jurisdiction (`restricted`).","properties":{"membersNearby":{"type":["integer","null"],"description":"Every listed member within `membersRadiusKm` of `membersAround` (the linked community's counter when there is one — the figure its /swingers page shows)."},"membersShownPublicly":{"type":["integer","null"],"description":"Of those, members shown by username on public pages."},"membersRadiusKm":{"type":["number","null"]},"membersAround":{"type":["object","null"],"properties":{"name":{"type":"string"},"path":{"type":["string","null"]}}},"travelersNext90Days":{"type":["integer","null"]}}},"membersEndpoint":{"type":["string","null"],"example":"/api/v1/places/by-id/34323/members","description":"The linked community's public member list (GET /api/v1/places/by-id/{id}/members; not for crawlers). Null without a linked community, or when `restricted`."},"clubs":{"type":"array","items":{"$ref":"#/components/schemas/Club"}},"nudist":{"type":"array","items":{"$ref":"#/components/schemas/NudistPlace"}}}}]}}},"paths":{"/auth/signup":{"post":{"tags":["auth"],"summary":"Create an account (18+). Requires the Terms and a separate explicit consent to sensitive data (GDPR Art. 9). E-mails a verification link — or a claim link if the address belongs to an imported network profile. The account's language (users.locale, for e-mails) is `locale`, else the request's Accept-Language; the sign-up is recorded as the iPhone app's with `X-SP-App: ios`.","parameters":[{"name":"X-SP-App","in":"header","required":false,"schema":{"type":"string","enum":["ios"]},"description":"`ios` from the iPhone app (sent with every request): a sign-up is recorded as an iPhone sign-up. Without it, as the website's."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthSignup"}}}},"responses":{"202":{"description":"Always the same answer (no account enumeration)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AuthCheckInbox"}}}}}},"422":{"description":"Validation failed — `details` is a zod issue list or `{ fieldErrors }`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"429":{"description":"Rate limited — `details.retryAfterSec`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/auth/login":{"post":{"tags":["auth"],"summary":"Log in with e-mail + password. kind=app returns a bearer token; kind=web sets httpOnly cookies. Only confirmed accounts: an unconfirmed one gets the same 401 as a wrong password (and its confirmation link is e-mailed again). 429 counts failed attempts per e-mail and client network, never successful ones; its message suggests a login link, which keeps working.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthLogin"}}}},"responses":{"200":{"description":"Session started (web: cookies set; app: bearer token in body)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AuthSession"}}}}}},"401":{"description":"Not signed in, or wrong e-mail/password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"403":{"description":"Forbidden (suspended account, cross-site request, e-mail not verified)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"422":{"description":"Validation failed — `details` is a zod issue list or `{ fieldErrors }`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"429":{"description":"Rate limited — `details.retryAfterSec`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/auth/logout":{"post":{"tags":["auth"],"summary":"Revoke the current session.","security":[{"bearer":[]}],"responses":{"200":{"description":"Logged out"},"403":{"description":"Forbidden (suspended account, cross-site request, e-mail not verified)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/auth/magic-link":{"post":{"tags":["auth"],"summary":"E-mail a one-time login link (30 min). Past the per-address limit (all sign-in links together: 2 per 30 min, 6 a day per client network) nothing more is sent, with the same answer (the last link still works).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthMagicLink"}}}},"responses":{"202":{"description":"Always the same answer (no account enumeration)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AuthCheckInbox"}}}}}},"422":{"description":"Validation failed — `details` is a zod issue list or `{ fieldErrors }`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"429":{"description":"Rate limited per client IP — `details.retryAfterSec`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/auth/magic-link/consume":{"post":{"tags":["auth"],"summary":"Exchange a login-link token for a session. Confirms the e-mail too; on an account's first confirmation every earlier session is revoked, and the password (set one with POST /me/password), date of birth, marketing opt-in and sensitive-data consent given at sign-up are cleared (POST /me/profile asks for the consent again).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthTokenKind"}}}},"responses":{"200":{"description":"Session started (web: cookies set; app: bearer token in body)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AuthSession"}}}}}},"400":{"description":"Bad request / invalid or expired link","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/auth/verify-email":{"post":{"tags":["auth"],"summary":"Confirm the e-mail address from the verification link and start a session (next step: onboarding). On the account's first confirmation `password` is required: it replaces the password typed at sign-up, the sign-up's date of birth, marketing opt-in and sensitive-data consent are cleared (onboarding asks for the birth month and year and for the consent), and every earlier session is revoked. Without it → 422 `fieldErrors.password` and the link stays usable. On an already confirmed account the link just logs in.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthVerifyEmail"}}}},"responses":{"200":{"description":"Session started (web: cookies set; app: bearer token in body)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AuthSession"}}}}}},"400":{"description":"Bad request / invalid or expired link","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"422":{"description":"Validation failed — `details` is a zod issue list or `{ fieldErrors }`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/auth/password/forgot":{"post":{"tags":["auth"],"summary":"E-mail a password-reset link (1 h). Past the per-address limit nothing more is sent, with the same answer (the last link still works).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthEmail"}}}},"responses":{"202":{"description":"Always the same answer (no account enumeration)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AuthCheckInbox"}}}}}},"422":{"description":"Validation failed — `details` is a zod issue list or `{ fieldErrors }`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"429":{"description":"Rate limited per client IP — `details.retryAfterSec`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/auth/password/reset":{"post":{"tags":["auth"],"summary":"Set a new password from a reset link. Revokes all existing sessions and starts a new one. It confirms the e-mail too; on an account's first confirmation the date of birth, marketing opt-in and sensitive-data consent given at sign-up are cleared.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthReset"}}}},"responses":{"200":{"description":"Session started (web: cookies set; app: bearer token in body)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AuthSession"}}}}}},"400":{"description":"Bad request / invalid or expired link","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"422":{"description":"Validation failed — `details` is a zod issue list or `{ fieldErrors }`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/auth/claim":{"post":{"tags":["auth"],"summary":"Claim an imported SwingersPlay-network profile from a claim link (free). Creates/reuses the verified account and starts a session. Requires the Terms and a separate explicit consent to sensitive data (GDPR Art. 9). Reusing a never-confirmed account revokes its earlier sessions and clears its password.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthClaim"}}}},"responses":{"200":{"description":"Session started (web: cookies set; app: bearer token in body)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AuthSession"}}}}}},"400":{"description":"Bad request / invalid or expired link","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"403":{"description":"Suspended account, or a profile suspended by moderation (not claimable until a person has reviewed it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"404":{"description":"Profile no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/auth/unsubscribe":{"post":{"tags":["auth"],"summary":"RFC 8058 one-click unsubscribe from notification e-mails (target of the List-Unsubscribe header).","parameters":[{"name":"t","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Unsubscribed"},"400":{"description":"Bad request / invalid or expired link","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/email/webhook":{"post":{"tags":["auth"],"summary":"E-mail provider events (server-to-server; not for apps): hard bounces and spam complaints → suppression list and opt-out.","description":"Resend: Svix-signed (`svix-id`, `svix-timestamp`, `svix-signature`; RESEND_WEBHOOK_SECRET). Postmark: basic-auth credentials in the webhook URL (POSTMARK_WEBHOOK_USER / POSTMARK_WEBHOOK_PASSWORD). Refused while the secret is not configured. Idempotent.","x-internal":true,"responses":{"200":{"description":"{ received, suppressed }"},"400":{"description":"Bad request / invalid or expired link","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"401":{"description":"Missing or invalid signature / credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"404":{"description":"Webhooks for this provider are not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/me":{"get":{"tags":["me"],"summary":"The current account, profile, entitlements and session.","security":[{"bearer":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Me"}}}}}},"401":{"description":"Not signed in, or wrong e-mail/password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}},"delete":{"tags":["me"],"summary":"Permanently delete the account (GDPR erasure). Refused (409) while a membership still auto-renews. While the account's own profile is part of an open review the request is honoured later (GDPR Art. 17(3)): the account is suspended and logged out now, nothing is deleted yet, and it is deleted automatically as soon as the review allows (`deleted: false`, `deletionPending: true`).","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeDelete"}}}},"responses":{"200":{"description":"{ deleted: boolean, deletionPending: boolean, pendingReview: number, message?: string } — deletionPending: the account's own profile is under an open review, so the account is suspended now and deleted automatically when the review allows (message explains it); pendingReview counts network profiles of the same mailbox that are under an open review: kept hidden, without e-mails, and deleted automatically when the review allows."},"401":{"description":"Not signed in, or wrong e-mail/password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"422":{"description":"Validation failed — `details` is a zod issue list or `{ fieldErrors }`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/me/profile":{"post":{"tags":["me"],"summary":"Onboarding: create the member profile (e-mail must be verified). While no consent to sensitive data is on record (GET /me → `user.hasSensitiveDataConsent: false`, always the case after a first confirmation through a verification, login or reset link), `sensitiveConsent: true` is required (422 `fieldErrors.sensitiveConsent` otherwise) and is recorded with the profile.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeProfileCreate"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/MeProfileRef"}}}}}},"401":{"description":"Not signed in, or wrong e-mail/password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"403":{"description":"Forbidden (suspended account, cross-site request, e-mail not verified)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"422":{"description":"Validation failed — `details` is a zod issue list or `{ fieldErrors }`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}},"patch":{"tags":["me"],"summary":"Update the profile (partial). Username can change once every 30 days.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeProfileUpdate"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/MeProfileRef"}}}}}},"401":{"description":"Not signed in, or wrong e-mail/password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"404":{"description":"No profile yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"422":{"description":"Validation failed — `details` is a zod issue list or `{ fieldErrors }`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/me/settings":{"patch":{"tags":["me"],"summary":"Notification, visibility, \"Browse privately\" and language (e-mails, push notifications) settings.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeSettings"}}}},"responses":{"200":{"description":"Saved"},"401":{"description":"Not signed in, or wrong e-mail/password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"402":{"description":"incognito=true needs Premium (nothing is saved)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"403":{"description":"incognito=true before the profile exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"422":{"description":"Validation failed — `details` is a zod issue list or `{ fieldErrors }`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"429":{"description":"Refused: the profile was made visible on public pages too often in the last hour (hiding is never limited). The notification and visibility fields are not saved — `details.retryAfterSec`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/me/password":{"post":{"tags":["me"],"summary":"Change (or set) the password. Other sessions are revoked.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MePassword"}}}},"responses":{"200":{"description":"Saved"},"401":{"description":"Not signed in, or wrong e-mail/password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"422":{"description":"Validation failed — `details` is a zod issue list or `{ fieldErrors }`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"429":{"description":"Rate limited — `details.retryAfterSec`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/me/email":{"post":{"tags":["me"],"summary":"Start an e-mail change: a confirmation link goes to the new address.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeEmail"}}}},"responses":{"202":{"description":"Always the same answer (no account enumeration)","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/AuthCheckInbox"}}}}}},"401":{"description":"Not signed in, or wrong e-mail/password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"422":{"description":"Validation failed — `details` is a zod issue list or `{ fieldErrors }`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"429":{"description":"Rate limited — `details.retryAfterSec`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/me/verification-email":{"post":{"tags":["me"],"summary":"Re-send the e-mail confirmation link.","security":[{"bearer":[]}],"responses":{"202":{"description":"Sent (or already verified)"},"401":{"description":"Not signed in, or wrong e-mail/password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"429":{"description":"Rate limited — `details.retryAfterSec`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/me/sessions":{"delete":{"tags":["me"],"summary":"Log out every other device.","security":[{"bearer":[]}],"responses":{"200":{"description":"{ revoked: number }"},"401":{"description":"Not signed in, or wrong e-mail/password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/me/export":{"get":{"tags":["me"],"summary":"Download your personal data as JSON (GDPR Art. 15/20): account, profile, photos (with owner-only download links), conversations, favorites, blocks, views, travel plans, photo grants, sessions, memberships, reports filed, appeals, reports and moderation decisions about you (never the reporter's identity), venue submissions. Nothing is truncated.","security":[{"bearer":[]}],"responses":{"200":{"description":"JSON attachment"},"401":{"description":"Not signed in, or wrong e-mail/password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/me/place-suggestions":{"get":{"tags":["me"],"summary":"Home-place candidates by name prefix (≤ 10) for onboarding / profile edit.","security":[{"bearer":[]}],"parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":60}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MePlaceOption"}}}}}}},"401":{"description":"Not signed in, or wrong e-mail/password","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}},"429":{"description":"Rate limited — `details.retryAfterSec`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthApiError"}}}}}}},"/members":{"get":{"operationId":"searchMembers","tags":["Members"],"summary":"Search members (free)","description":"Free for everyone. Anonymous callers see only publicly listed members; signed-in callers see all listed members except themselves and blocked members (imported network members who are not publicly listed only once the e-mail address is verified). Order: active Boost first, then distance (when a centre is given), then most recent activity. Default radius: 25 miles in the US/UK, 25 km elsewhere. meta.total is exact up to 2,500; above that it is 2500 with meta.totalIsLowerBound = true. Rate-limited per IP (anonymous) or per user, shared with the /search page. Anonymous callers never see members who live in a high-risk jurisdiction, wherever the centre is. Anonymous callers around a place in a high-risk jurisdiction (for lat/lng: the nearest place), or searching such a country, get no members and meta.total = null with meta.restricted = true, like the /search page; so do anonymous callers around coordinates with no known place within about 60 km (meta.restrictedReason = unknown_jurisdiction).","security":[{},{"bearer":[]}],"parameters":[{"name":"place","in":"query","description":"Place id (centre of the search).","schema":{"type":"integer","minimum":1,"maximum":2147483647}},{"name":"lat","in":"query","description":"Centre latitude (with lng).","schema":{"type":"number","minimum":-90,"maximum":90}},{"name":"lng","in":"query","description":"Centre longitude (with lat).","schema":{"type":"number","minimum":-180,"maximum":180}},{"name":"country","in":"query","description":"ISO 3166-1 alpha-2; only used without a centre.","schema":{"type":"string","pattern":"^[A-Za-z]{2}$"}},{"name":"radiusKm","in":"query","schema":{"type":"number","exclusiveMinimum":0,"maximum":250}},{"name":"kind","in":"query","description":"Comma-separated member kinds: couple_mf, couple_ff, couple_mm, couple, woman, man, trans, nonbinary.","schema":{"type":"string"}},{"name":"ageMin","in":"query","description":"At least one partner this age or older.","schema":{"type":"integer","minimum":18,"maximum":99}},{"name":"ageMax","in":"query","schema":{"type":"integer","minimum":18,"maximum":99}},{"name":"activeWithinDays","in":"query","schema":{"type":"integer","minimum":1,"maximum":3650}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"maximum":2400,"default":0}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":48,"default":24}}],"responses":{"200":{"description":"Matching members","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MemberSearchCard"}},"meta":{"$ref":"#/components/schemas/MemberSearchMeta"}}}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"validation_failed or content_rejected (content policy)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/members/{username}":{"get":{"operationId":"getMemberProfile","tags":["Members"],"summary":"Member profile (teaser anonymously, full after free sign-in with a confirmed e-mail)","description":"Anonymous callers get a teaser of publicly listed members; every other profile answers 404 exactly like an unknown username. Signed-in callers with a verified e-mail get the full profile (free) plus their relationship to the member; imported network profiles that are not publicly listed are returned with limited = true and basic facts only (and are 404 for unverified callers). Viewing records a profile view (once per viewer per day) unless the viewer browses privately (Premium). Photo URLs point at the access-checked GET /api/v1/photos/{id}/file. Teasers carry no member id (only full profiles do). Rate-limited per IP (anonymous) or per user, shared with the /u/{username} page.","security":[{},{"bearer":[]}],"parameters":[{"name":"username","in":"path","required":true,"schema":{"type":"string","maxLength":64}}],"responses":{"200":{"description":"Profile","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/MemberProfileResponse"}}}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/members/{username}/block":{"post":{"operationId":"blockMember","tags":["Members"],"summary":"Block a member (free)","description":"Hides both members from each other everywhere (search, profiles, messages) and removes favorites both ways.","security":[{"bearer":[]}],"parameters":[{"name":"username","in":"path","required":true,"schema":{"type":"string","maxLength":64}}],"responses":{"200":{"description":"Blocked","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"blocked":{"const":true}}}}}}}},"400":{"description":"bad_request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized — sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden — e-mail not verified, no profile yet, account not active, or (messaging) your profile is suspended by moderation (details.reason = 'suspended')","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"delete":{"operationId":"unblockMember","tags":["Members"],"summary":"Unblock a member","security":[{"bearer":[]}],"parameters":[{"name":"username","in":"path","required":true,"schema":{"type":"string","maxLength":64}}],"responses":{"200":{"description":"Unblocked","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"blocked":{"const":false}}}}}}}},"400":{"description":"bad_request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized — sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden — e-mail not verified, no profile yet, account not active, or (messaging) your profile is suspended by moderation (details.reason = 'suspended')","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/me/blocks":{"get":{"operationId":"listBlockedMembers","tags":["Members"],"summary":"Members I blocked (newest first)","description":"Username, kind and when, nothing else about them. Every block is listed whatever the other member's status today, so it can always be undone with DELETE /members/{username}/block.","security":[{"bearer":[]}],"parameters":[{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"Blocked members","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BlockedMember"}},"meta":{"type":"object","properties":{"total":{"type":"integer"},"offset":{"type":"integer"},"limit":{"type":"integer"}}}}}}}},"401":{"description":"unauthorized — sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden — e-mail not verified, no profile yet, account not active, or (messaging) your profile is suspended by moderation (details.reason = 'suspended')","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"validation_failed or content_rejected (content policy)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/conversations":{"get":{"operationId":"listConversations","tags":["Messages"],"summary":"Inbox","security":[{"bearer":[]}],"parameters":[{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}}],"responses":{"200":{"description":"Conversations, most recent first","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ConversationSummary"}},"meta":{"type":"object","properties":{"total":{"type":"integer"},"offset":{"type":"integer"},"limit":{"type":"integer"},"unreadTotal":{"type":"integer"}}}}}}}},"401":{"description":"unauthorized — sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden — e-mail not verified, no profile yet, account not active, or (messaging) your profile is suspended by moderation (details.reason = 'suspended')","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"operationId":"startConversation","tags":["Messages"],"summary":"Send a first message (Premium)","description":"Starting a conversation needs Premium; limits: 10 new conversations per hour and 40 per 24 hours. If the two members already have a conversation, the message is appended to it and the reply rules apply (free for the member who did not start it). Messages to members who have not yet activated their profile are delivered by e-mail notification (claim link, replying is free); if such a member cannot be reached by e-mail, the request fails with 409.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["to","body"],"properties":{"to":{"type":"string","description":"Recipient username"},"body":{"type":"string","minLength":1,"maxLength":2000}}}}}},"responses":{"200":{"description":"Appended to the existing conversation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SendMessageResult"}}}}}},"201":{"description":"New conversation created","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SendMessageResult"}}}}}},"400":{"description":"bad_request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized — sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"premium_required — this action needs Premium (or Travel access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden — e-mail not verified, no profile yet, account not active, or (messaging) your profile is suspended by moderation (details.reason = 'suspended')","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"validation_failed or content_rejected (content policy)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/conversations/{id}":{"get":{"operationId":"getConversation","tags":["Messages"],"summary":"One conversation with a page of messages (ascending)","description":"Default: the latest page. `beforeId` pages back in time; `afterId` returns only newer messages (polling).","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"beforeId","in":"query","schema":{"type":"integer"}},{"name":"afterId","in":"query","schema":{"type":"integer"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"Conversation","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ConversationThread"}}}}}},"401":{"description":"unauthorized — sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden — e-mail not verified, no profile yet, account not active, or (messaging) your profile is suspended by moderation (details.reason = 'suspended')","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/conversations/{id}/messages":{"post":{"operationId":"sendMessage","tags":["Messages"],"summary":"Send a message in a conversation","description":"Free for the member who did not start the conversation. The initiator needs Premium for every further message.","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["body"],"properties":{"body":{"type":"string","minLength":1,"maxLength":2000}}}}}},"responses":{"201":{"description":"Sent","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/SendMessageResult"}}}}}},"401":{"description":"unauthorized — sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"premium_required — this action needs Premium (or Travel access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden — e-mail not verified, no profile yet, account not active, or (messaging) your profile is suspended by moderation (details.reason = 'suspended')","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"validation_failed or content_rejected (content policy)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/conversations/{id}/read":{"post":{"operationId":"markConversationRead","tags":["Messages"],"summary":"Mark the conversation as read","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Read","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"unread":{"const":0}}}}}}}},"401":{"description":"unauthorized — sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden — e-mail not verified, no profile yet, account not active, or (messaging) your profile is suspended by moderation (details.reason = 'suspended')","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/favorites":{"get":{"operationId":"listFavorites","tags":["Members"],"summary":"My favorites (Premium)","security":[{"bearer":[]}],"parameters":[{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":48,"default":24}}],"responses":{"200":{"description":"Favorites, newest first","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MemberCard"}},"meta":{"type":"object","properties":{"total":{"type":"integer"},"offset":{"type":"integer"},"limit":{"type":"integer"}}}}}}}},"401":{"description":"unauthorized — sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"premium_required — this action needs Premium (or Travel access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden — e-mail not verified, no profile yet, account not active, or (messaging) your profile is suspended by moderation (details.reason = 'suspended')","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"operationId":"addFavorite","tags":["Members"],"summary":"Save a member (Premium)","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["username"],"properties":{"username":{"type":"string"}}}}}},"responses":{"201":{"description":"Saved","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"username":{"type":"string"},"favorite":{"const":true}}}}}}}},"400":{"description":"bad_request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"unauthorized — sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"premium_required — this action needs Premium (or Travel access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden — e-mail not verified, no profile yet, account not active, or (messaging) your profile is suspended by moderation (details.reason = 'suspended')","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"delete":{"operationId":"removeFavorite","tags":["Members"],"summary":"Remove a favorite (always allowed)","security":[{"bearer":[]}],"parameters":[{"name":"username","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Removed","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"username":{"type":"string"},"favorite":{"const":false}}}}}}}},"401":{"description":"unauthorized — sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden — e-mail not verified, no profile yet, account not active, or (messaging) your profile is suspended by moderation (details.reason = 'suspended')","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/travel":{"get":{"operationId":"listTravelPlans","tags":["Travel"],"summary":"My upcoming and ongoing trips","security":[{"bearer":[]}],"responses":{"200":{"description":"Trips, soonest first","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TravelPlan"}}}}}}},"401":{"description":"unauthorized — sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden — e-mail not verified, no profile yet, account not active, or (messaging) your profile is suspended by moderation (details.reason = 'suspended')","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"post":{"operationId":"createTravelPlan","tags":["Travel"],"summary":"Announce a trip (Travel access)","description":"Exactly one of destinationId or placeId. Start today or later, end on or after start, at most 365 days ahead; at most 10 upcoming trips.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TravelPlanInput"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/TravelPlan"}}}}}},"401":{"description":"unauthorized — sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"402":{"description":"premium_required — this action needs Premium (or Travel access)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden — e-mail not verified, no profile yet, account not active, or (messaging) your profile is suspended by moderation (details.reason = 'suspended')","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"validation_failed or content_rejected (content policy)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"delete":{"operationId":"deleteTravelPlan","tags":["Travel"],"summary":"Delete one of my trips","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"const":true}}}}}}}},"401":{"description":"unauthorized — sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"forbidden — e-mail not verified, no profile yet, account not active, or (messaging) your profile is suspended by moderation (details.reason = 'suspended')","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/travel/travellers":{"get":{"operationId":"listTravellers","tags":["Travel"],"summary":"Members travelling to a destination or place","description":"Signed-in callers get the list; anonymous callers get only meta.total (loginRequired: true), and for a place or destination in a high-risk jurisdiction (or a destination linked to a community there) not even that: meta.total = null and meta.restricted = true, like its page. An anonymous area count elsewhere leaves out trips into a high-risk jurisdiction inside the area. scope=exact (default): trips to exactly that destination or place, one row per trip. scope=area: trips to anywhere around it (the place's default radius or the destination's radius), one row per member, soonest first.","security":[{},{"bearer":[]}],"parameters":[{"name":"destinationId","in":"query","schema":{"type":"integer","minimum":1,"maximum":2147483647}},{"name":"placeId","in":"query","schema":{"type":"integer","minimum":1,"maximum":2147483647}},{"name":"scope","in":"query","schema":{"type":"string","enum":["exact","area"],"default":"exact"}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":48,"default":24}}],"responses":{"200":{"description":"Travellers, soonest trip first","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TravellerCard"}},"meta":{"type":"object","properties":{"total":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Null for anonymous callers in a high-risk jurisdiction (`restricted`)."},"offset":{"type":"integer"},"limit":{"type":"integer"},"loginRequired":{"type":"boolean"},"restricted":{"type":"boolean","description":"High-risk jurisdiction: member numbers and member lists are never published to anonymous callers"},"restrictedReason":{"anyOf":[{"type":"string","enum":["high_risk_jurisdiction"]},{"type":"null"}]}}}}}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"validation_failed or content_rejected (content policy)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/plans":{"get":{"tags":["billing"],"summary":"Public price list, free vs Premium features, Founding Member places left","description":"Cached for 5 minutes, publicly and by URL: the texts (plan names, badges, blurbs, renewal terms, consent texts, feature and access labels) are English unless `locale` names a language the site serves (Accept-Language is not used here; such a response says `Content-Language`). Every recurring plan carries its plain-words renewal terms (`renewalTerms`) and the consent text the website shows next to a checkbox (`consentText`); the apps show `appBlurb` with their store's own price and consent instead. `features` lists only features this deployment can deliver today; show nothing else as included. While `paymentsAvailable` is false nothing can be bought: show the plans with \"Payments open in a few days\" and offer POST /billing/payments-notice instead of a buy button.","parameters":[{"name":"locale","in":"query","required":false,"schema":{"type":"string","examples":["de","pt","zh-hant"]},"description":"Site language code of the texts (omit it for English). A language the site does not serve gives English."}],"responses":{"200":{"description":"Plans","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BillingPlanList"},"meta":{"type":"object"}}}}}}}}},"/billing/checkout":{"post":{"tags":["billing"],"summary":"Start a purchase; returns the hosted payment page URL","description":"Open `url` in the system browser. For recurring plans `acceptRenewalTerms` must be true and must come from an unticked-by-default checkbox showing the plan's `consentText`. Mobile apps: store rules on in-app purchases apply (see docs/MONETIZATION.md).","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingCheckoutRequest"}}}},"responses":{"201":{"description":"Checkout created","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BillingCheckoutResponse"},"meta":{"type":"object"}}}}}},"401":{"description":"Sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"403":{"description":"E-mail not verified or account inactive (`details.reason`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"404":{"description":"Unknown plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"409":{"description":"Purchase blocked, e.g. already subscribed, already paid up on an equal plan (paid_until), sold out, payments not open (`details.reason`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"422":{"description":"Auto-renewal consent missing or invalid body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}}}}},"/billing/subscription":{"get":{"tags":["billing"],"summary":"The signed-in member's access, current plan, grants and payment history (plan names, grant labels and receipt descriptions in the request's language)","description":"Also `blockers`: for each plan the iPhone app sells, the refusal POST /billing/apple/intent would answer right now, by plan code, with its text in the request's language (a plan left out can be bought). Show that text on the plan instead of the overlap notice, the consent checkbox and the buy button. Not included: `payments_unavailable` (read `appleIapAvailable` from GET /plans) and `consent_required`.","security":[{"bearer":[]}],"responses":{"200":{"description":"Membership","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/BillingMembership"},{"type":"object","required":["blockers"],"properties":{"blockers":{"$ref":"#/components/schemas/BillingPurchaseBlockers"}}}]},"meta":{"type":"object"}}}}}},"401":{"description":"Sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}}}}},"/billing/funnel":{"post":{"tags":["billing"],"summary":"Apps: record a step of the membership order flow, for the admins' daily funnel report","description":"Fire and forget: never retried, never awaited by the interface, never shown to the member. A bearer request is the iPhone app. The same step within 10 minutes is stored once, at most 40 steps per account a day and 60 posts an hour; over a limit the answer is still 200 with `recorded: false`. The iPhone app sends `page` once per view of its membership screen (only while In-App Purchase is open and a plan shows a StoreKit price), `plan` when a subscription's renewal consent is ticked (also only while In-App Purchase is open), and `cancelled` with an `app_store_*` detail when StoreKit leaves a purchase unpaid. While In-App Purchase is off on the server (`appleIapAvailable: false` in GET /plans), every bearer (iPhone) step answers `recorded: false` and is not stored.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingFunnelStep"}}}},"responses":{"200":{"description":"{ recorded: boolean }"},"401":{"description":"Sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"422":{"description":"Unknown step or detail","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}}}}},"/billing/payments-notice":{"post":{"tags":["billing"],"summary":"While payments are closed: e-mail me once when they open","description":"One-click purchase intent while `paymentsAvailable` is false. Idempotent (one request per member; `planCode` remembers the plan they looked at). The e-mail is sent once, after payments open, and honours the member's notification switch (`emailNotifications`).","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"planCode":{"type":"string"}}}}}},"responses":{"201":{"description":"Recorded","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BillingPaymentsNotice"},"meta":{"type":"object"}}}}}},"401":{"description":"Sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"403":{"description":"E-mail not verified (`details.reason` = email_unverified)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"409":{"description":"Payments are already open (`details.reason` = payments_open): use /billing/checkout","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}}}},"delete":{"tags":["billing"],"summary":"Withdraw the payments-open e-mail request","security":[{"bearer":[]}],"responses":{"200":{"description":"Withdrawn","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BillingPaymentsNotice"},"meta":{"type":"object"}}}}}},"401":{"description":"Sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}}}}},"/billing/cancel":{"post":{"tags":["billing"],"summary":"Turn off auto-renewal (mode=cancel) or pause (also holds the price for 6 months)","description":"Access continues until the end of the paid period. Turns off every member subscription that may still be charged. Returns the updated membership; `meta` has accessUntil, pausedUntil, providerConfirmed. When providerConfirmed is false, auto-renewal is off on our side but the payment processor has not confirmed it yet (retried daily; anything charged meanwhile is refunded): do NOT tell the member they will not be charged again, show that it is being confirmed.","security":[{"bearer":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","enum":["cancel","pause"],"default":"cancel"}}}}}},"responses":{"200":{"description":"Updated membership","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BillingMembership"},"meta":{"type":"object"}}}}}},"401":{"description":"Sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"404":{"description":"No auto-renewing membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}}}}},"/billing/withdraw":{"post":{"tags":["billing"],"summary":"14-day right of withdrawal from the latest purchase (EU/UK law, offered to everyone)","description":"Show a separate confirmation step first. Stops renewal, records the request, e-mails an acknowledgement with the date and time of the request; support refunds within 14 days. `meta` has planName, requestedAt, providerConfirmed (see /billing/cancel).","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["confirm"],"properties":{"confirm":{"const":true}}}}}},"responses":{"200":{"description":"Updated membership","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BillingMembership"},"meta":{"type":"object"}}}}}},"401":{"description":"Sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"404":{"description":"No purchase within the withdrawal period","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}}}}},"/billing/apple/intent":{"post":{"tags":["billing"],"summary":"iOS app: start an In-App Purchase (returns the appAccountToken for StoreKit)","description":"Runs the same guards as /billing/checkout and stores a checkout intent. Buy `productId` with StoreKit 2 passing `appAccountToken` (a UUID) as the purchase option; Apple signs it into the transaction, which links the purchase to this account. Then post the signed transaction to /billing/apple/transactions. For subscriptions `acceptRenewalTerms` must be true: show the auto-renewal consent with StoreKit's price (\"I understand this renews automatically at {price} every {period} until I turn auto-renewal off in my Apple ID settings.\", period `month`, `3 months` or `year`, in the app's language), not the plan's USD consentText, and send that price as shown in `displayedPrice`: the intent stores that English sentence with the request's language. The funnel steps `checkout` and `refused` are recorded here (the app never sends them to /billing/funnel).","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingAppleIntentRequest"}}}},"responses":{"201":{"description":"Intent stored","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BillingAppleIntent"},"meta":{"type":"object"}}}}}},"401":{"description":"Sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"403":{"description":"E-mail not verified or account inactive (`details.reason`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"404":{"description":"Unknown plan or not sold in the app","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"409":{"description":"Purchase blocked, e.g. already subscribed (also through the website), lifetime, sold out, In-App Purchase closed (`details.reason`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"422":{"description":"Auto-renewal consent missing or invalid body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}}}}},"/billing/apple/transactions":{"post":{"tags":["billing"],"summary":"iOS app: apply signed App Store transactions (after a purchase, restore, or at launch)","description":"Send StoreKit 2's `jwsRepresentation` of each transaction. Verified against Apple's root certificate; idempotent (the App Store Server Notification for the same transaction then does nothing). Finish the StoreKit transaction once its status is applied, duplicate, rejected, unlinked, other_account or invalid; keep it unfinished and retry later when pending.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["signedTransactions"],"properties":{"signedTransactions":{"type":"array","minItems":1,"maxItems":25,"items":{"type":"string"}}}}}}},"responses":{"200":{"description":"Per-transaction results and the member's access","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BillingAppleTransactions"},"meta":{"type":"object"}}}}}},"401":{"description":"Sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"422":{"description":"Invalid body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}}}}},"/billing/apple/notifications":{"post":{"tags":["billing"],"summary":"App Store Server Notifications V2 (server-to-server; not for apps)","description":"Apple's signed payload, verified against the pinned Apple root certificate. Idempotent.","x-internal":true,"responses":{"200":{"description":"Processed or ignored"},"403":{"description":"Not valid App Store data"}}}},"/billing/google/intent":{"post":{"tags":["billing"],"summary":"Android app: start a Google Play purchase (returns the nonce to post back with the purchase token)","description":"Runs the same guards as /billing/checkout and stores a checkout intent. Buy `productId` with the Payment Request API (method https://play.google.com/billing), then post the purchase token with this `nonce` to /billing/google/purchases. For subscriptions `acceptRenewalTerms` must be true: show the auto-renewal consent with Google Play's price (getDetails), not the plan's USD consentText, and send that price as shown in `displayedPrice` (the intent stores the consent the member ticked).","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingGoogleIntentRequest"}}}},"responses":{"201":{"description":"Intent stored","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BillingGoogleIntent"},"meta":{"type":"object"}}}}}},"401":{"description":"Sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"403":{"description":"E-mail not verified or account inactive (`details.reason`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"404":{"description":"Unknown plan or not sold in the app","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"409":{"description":"Purchase blocked, e.g. already subscribed (also through the website), lifetime, sold out, Google Play Billing closed (`details.reason`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"422":{"description":"Auto-renewal consent missing or invalid body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}}}}},"/billing/google/purchases":{"post":{"tags":["billing"],"summary":"Android app: apply Google Play purchases (after a purchase, and the ones listed at launch)","description":"Send each purchase token with its product id (and the intent's nonce when known). Each purchase is read back from the Google Play Developer API, applied once and acknowledged. Idempotent. Call PaymentResponse.complete('success') once the status is applied or duplicate; post pending ones again later.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["purchases"],"properties":{"purchases":{"type":"array","minItems":1,"maxItems":25,"items":{"type":"object","required":["productId","purchaseToken"],"properties":{"productId":{"type":"string"},"purchaseToken":{"type":"string"},"nonce":{"anyOf":[{"type":"string"},{"type":"null"}]}}}}}}}}},"responses":{"200":{"description":"Per-purchase results and the member's access","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/BillingGooglePurchases"},"meta":{"type":"object"}}}}}},"401":{"description":"Sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"422":{"description":"Invalid body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}},"429":{"description":"Too many posts from this account or network (`details.retryAfterSec`): post the purchases again later","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}}}}},"/billing/google/notifications":{"post":{"tags":["billing"],"summary":"Google Play Real-Time Developer Notifications (Pub/Sub push; not for apps)","description":"Authenticated by the `token` query parameter; every purchase is read back from the Play Developer API. Idempotent.","x-internal":true,"responses":{"200":{"description":"Processed or ignored"},"403":{"description":"Wrong token or not a Play notification"}}}},"/billing/ccbill/webhook":{"post":{"tags":["billing"],"summary":"CCBill webhooks (server-to-server; not for apps)","description":"Authenticated by source IP allowlist and optional `secret` query parameter. Idempotent.","x-internal":true,"parameters":[{"name":"eventType","in":"query","required":true,"schema":{"type":"string"}},{"name":"secret","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Processed"},"403":{"description":"Rejected source","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingError"}}}}}}},"/photos":{"get":{"tags":["photos"],"summary":"Own photos (all statuses), or ?member=<username> for another member's approved photos as the viewer may see them (locked ones have url=null and a `locked` reason).","security":[{"bearer":[]}],"parameters":[{"name":"member","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Photos","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Photo"}}}}}}},"401":{"description":"Not signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"404":{"description":"Not found (also returned when you may not see the photo at all)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}}}},"post":{"tags":["photos"],"summary":"Upload up to 6 photos (max 12 per member, 15 MB each). Metadata (EXIF/GPS) is stripped; nothing is visible to others before approval.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["files","attest"],"properties":{"files":{"type":"array","items":{"type":"string","format":"binary"}},"visibility":{"type":"string","enum":["public_sfw","members","private"],"default":"members"},"attest":{"type":"string","enum":["true"],"description":"Uploader confirms every person shown is an adult who consented to being shown."}}}}}},"responses":{"201":{"description":"One result per file","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PhotoUploadResult"}}}}}}},"401":{"description":"Not signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"403":{"description":"forbidden / age_verification_required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"422":{"description":"validation_failed or content_rejected (`details.reasons` lists machine codes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"503":{"description":"feature_unavailable — photo uploads are not open yet (photo storage or the automated photo check is not set up)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}}}}},"/photos/{id}":{"patch":{"tags":["photos"],"summary":"Change visibility or set as main photo (owner). Widening visibility is re-checked against the content policy.","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"visibility":{"type":"string","enum":["public_sfw","members","private"]},"isPrimary":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated photo","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/Photo"}}}}}},"401":{"description":"Not signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"403":{"description":"forbidden / age_verification_required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"404":{"description":"Not found (also returned when you may not see the photo at all)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"409":{"description":"Conflict (e.g. only approved photos can be the main photo)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"422":{"description":"validation_failed or content_rejected (`details.reasons` lists machine codes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}}}},"delete":{"tags":["photos"],"summary":"Delete one of your photos (files are removed from storage). Refused (403) while the photo is under review after a report.","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"deleted":{"const":true}}}}}}}},"401":{"description":"Not signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"403":{"description":"forbidden / age_verification_required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"404":{"description":"Not found (also returned when you may not see the photo at all)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}}}}},"/photos/{id}/file":{"get":{"tags":["photos"],"summary":"The image (WebP). Enforces visibility on every request; with S3 storage responds 302 to a 5-minute signed URL.","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"size","in":"query","required":false,"schema":{"type":"string","enum":["thumb"]}}],"responses":{"200":{"description":"image/webp","content":{"image/webp":{"schema":{"type":"string","format":"binary"}}}},"302":{"description":"Redirect to a short-lived signed URL"},"401":{"description":"Not signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"402":{"description":"premium_required — members-only photos need Premium","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"403":{"description":"forbidden / age_verification_required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"404":{"description":"Not found (also returned when you may not see the photo at all)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}}}}},"/reports":{"post":{"tags":["safety"],"summary":"Report content (EU DSA Art. 16 notice). Anonymous reports need `email`, except reports about minors (`underage`), which need no contact details (Art. 16(2)(c)). Critical reasons hide a member's profile, photo or message immediately pending review. A photo reported as `underage`, `non_consensual`, `stolen_photos` or `nudity` is removed at once instead, with no review and no dispute (`underage` reports are still checked by a person). Club and naturist-place listings wait for a moderator. The notifier is e-mailed the decision. A private message can only be reported by its recipient (signed in); anyone else gets 404.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportInput"}}}},"responses":{"201":{"description":"Received","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"received":{"const":true},"reviewWithinHours":{"type":"integer"}}}}}}}},"401":{"description":"Not signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"404":{"description":"Not found (also returned when you may not see the photo at all)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"422":{"description":"validation_failed or content_rejected (`details.reasons` lists machine codes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}}}}},"/reports/appeals":{"get":{"tags":["safety"],"summary":"Your appeals","security":[{"bearer":[]}],"responses":{"200":{"description":"Appeals","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Appeal"}}}}}}},"401":{"description":"Not signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}}}},"post":{"tags":["safety"],"summary":"Appeal a moderation decision about your profile, profile text or message. Reviewed by a person. Photo decisions cannot be appealed.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppealInput"}}}},"responses":{"200":{"description":"An open appeal already exists","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"duplicate":{"const":true}}}}}}}},"201":{"description":"Appeal created","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"duplicate":{"type":"boolean"}}}}}}}},"401":{"description":"Not signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"404":{"description":"Not found (also returned when you may not see the photo at all)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"409":{"description":"Conflict (e.g. only approved photos can be the main photo)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"422":{"description":"validation_failed or content_rejected (`details.reasons` lists machine codes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}}}}},"/age-verification":{"get":{"tags":["verification"],"summary":"Verification status; with ?session= also the outcome of one of your own sessions (pending until the provider decided)","security":[{"bearer":[]}],"parameters":[{"name":"session","in":"query","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Status","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/VerificationState"}}}}}},"401":{"description":"Not signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}}}},"post":{"tags":["verification"],"summary":"Start age (view members-only photos) or ID (optional identity check: ID document and selfie) verification. Open `redirectUrl` in a browser / web view.","description":"returnTo \"app\": when the member finishes, the provider sends them to /verification/app-return, which opens swingersplaylive://verification?session=<sessionId> (use it as the callback of an ASWebAuthenticationSession), then poll GET /age-verification?session=<sessionId>.","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["level"],"properties":{"level":{"type":"string","enum":["age","id"]},"returnTo":{"type":"string","enum":["web","app"],"default":"web"}}}}}},"responses":{"201":{"description":"Session","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"redirectUrl":{"type":"string","format":"uri"},"sessionId":{"type":"string"},"provider":{"type":"string"}}}}}}}},"401":{"description":"Not signed in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}},"403":{"description":"Verification not available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}}}}},"/age-verification/webhook":{"post":{"tags":["verification"],"summary":"Provider decision callback (signed; server-to-server only).","responses":{"200":{"description":"Processed"},"403":{"description":"Bad signature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationApiError"}}}}}}},"/places/search":{"get":{"operationId":"searchPlaces","tags":["Places"],"summary":"Place typeahead","description":"Case-insensitive name prefix search over US communities and world cities, ordered by members then population. A qualifier after a comma narrows by state code/name or country (\"Austin, TX\", \"Paris, France\").","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":80}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":20,"default":10}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PlaceSearchHit"}},"meta":{"type":"object","properties":{"q":{"type":"string"},"count":{"type":"integer"}}}}}}}},"422":{"description":"Invalid query","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/places/{country}":{"get":{"operationId":"getCountryPlaces","tags":["Places"],"summary":"Country: counts plus its states (US) or cities","description":"JSON twin of /swingers/{country}.","parameters":[{"name":"country","in":"path","required":true,"description":"Country slug, e.g. united-states, france","schema":{"type":"string","pattern":"^[a-z0-9-]{1,100}$"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"oneOf":[{"$ref":"#/components/schemas/PlacesCountryResponse"},{"$ref":"#/components/schemas/PlacesRegionResponse"},{"$ref":"#/components/schemas/PlacesPlaceResponse"}],"discriminator":{"propertyName":"type"}}}}}}},"404":{"description":"Unknown location","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/places/{country}/{area}":{"get":{"operationId":"getStateOrCity","tags":["Places"],"summary":"US state (with every place in it) or world city","description":"JSON twin of /swingers/{country}/{state|city}. Returns type=region for US states, type=place for cities.","parameters":[{"name":"country","in":"path","required":true,"description":"Country slug","schema":{"type":"string","pattern":"^[a-z0-9-]{1,100}$"}},{"name":"area","in":"path","required":true,"description":"State slug (US) or city slug","schema":{"type":"string","pattern":"^[a-z0-9-]{1,100}$"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"oneOf":[{"$ref":"#/components/schemas/PlacesCountryResponse"},{"$ref":"#/components/schemas/PlacesRegionResponse"},{"$ref":"#/components/schemas/PlacesPlaceResponse"}],"discriminator":{"propertyName":"type"}}}}}}},"404":{"description":"Unknown location","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/places/{country}/{state}/{place}":{"get":{"operationId":"getCommunity","tags":["Places"],"summary":"US community: counts, nearby communities, clubs (current and older listings apart) and nudist places","description":"JSON twin of /swingers/united-states/{state}/{place}.","parameters":[{"name":"country","in":"path","required":true,"description":"Country slug","schema":{"type":"string","pattern":"^[a-z0-9-]{1,100}$"}},{"name":"state","in":"path","required":true,"description":"State slug","schema":{"type":"string","pattern":"^[a-z0-9-]{1,100}$"}},{"name":"place","in":"path","required":true,"description":"Community slug","schema":{"type":"string","pattern":"^[a-z0-9-]{1,100}$"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"oneOf":[{"$ref":"#/components/schemas/PlacesCountryResponse"},{"$ref":"#/components/schemas/PlacesRegionResponse"},{"$ref":"#/components/schemas/PlacesPlaceResponse"}],"discriminator":{"propertyName":"type"}}}}}}},"404":{"description":"Unknown location","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/places/local":{"get":{"operationId":"getLocalArea","tags":["Places"],"summary":"The caller's area in the US: their community within 50 miles, then their state","description":"From the IP location of the request (the website's home page shows the same to a visitor from there): the community the caller is in (the place named like the IP's city near the IP location, else the nearest place) with the members and the communities with members within 50 miles of it, then their state with its biggest scenes and recently active members. Public member cards only, never photos. data is null outside the US (Puerto Rico counts as the state PR) or when the state or location of the IP is unknown; near or state is null when there is nothing to show for it. A signed-in member never gets the cards of members in a block relation with them (either direction) nor their own; the counts are aggregates and stay as they are. Never stored by a shared cache: private, max-age=300 for a visitor, no-store for a signed-in member (the answer then depends on their blocks). Place names, distances and activity labels are in `locale` when given, else in the request's language (Accept-Language); the response says which (Content-Language).","parameters":[{"name":"locale","in":"query","required":false,"schema":{"type":"string","examples":["de","pt","zh-hant"]},"description":"Site language code of the texts. Pass it (the app does), so a device cache keyed on the URL never serves another language; a language the site does not serve gives English. Absent: the request's language."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"oneOf":[{"$ref":"#/components/schemas/PlacesLocalResponse"},{"type":"null"}]},"meta":{"type":"object","properties":{"state":{"type":["string","null"],"description":"USPS code of the caller's state, or null"}}}}}}}}}}},"/places/by-id/{id}/members":{"get":{"operationId":"listPlaceMembers","tags":["Places"],"summary":"Public members near a place","description":"Members with a public listing within 25 mi (US, UK…) or 25 km of the place: people who live there first, then by distance, then most recent activity. Only public card fields (username, type, computed ages, home place, coarse distance, day-precision activity). Members without a public listing are counted in meta.radiusMembers but never returned here; they are visible to logged-in members with a confirmed e-mail address in the search. Full profiles require login. In a high-risk jurisdiction no one is returned and both counts are null (meta.restricted = true).","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer","minimum":1}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":5000,"default":0}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":48,"default":36}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PlaceMemberCard"}},"meta":{"type":"object","properties":{"placeId":{"type":"integer"},"radiusMembers":{"type":["integer","null"],"description":"All listed members in the radius (including those visible to logged-in members only); null when not published (high-risk jurisdiction)"},"radiusPublicMembers":{"type":["integer","null"],"description":"Of radiusMembers: those with a public listing, i.e. what this endpoint can return; null when not published"},"restricted":{"type":"boolean","description":"High-risk jurisdiction: member numbers and lists are never published"},"restrictedReason":{"type":["string","null"],"enum":["high_risk_jurisdiction",null]},"offset":{"type":"integer"},"limit":{"type":"integer"},"nextOffset":{"type":["integer","null"]}}}}}}}},"404":{"description":"Place not found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}}}}}}}},"/clubs":{"get":{"tags":["Venues"],"summary":"List swinger clubs by country or near a point","description":"Public, cacheable. Requires `country` or `near`. Only venues with status active/unverified are listed.","parameters":[{"name":"country","in":"query","schema":{"type":"string"},"description":"ISO 3166-1 alpha-2 code (e.g. `us`) or country slug (e.g. `united-states`)."},{"name":"near","in":"query","schema":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?,-?\\d+(\\.\\d+)?$"},"description":"`lat,lng` — results sorted by distance."},{"name":"radiusKm","in":"query","schema":{"type":"number","minimum":1,"maximum":250,"default":50},"description":"Only with `near`."},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Club"}},"meta":{"$ref":"#/components/schemas/VenueListMeta"}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VenuesError"}}}},"422":{"description":"Invalid query","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VenuesError"}}}}}}},"/clubs/countries":{"get":{"tags":["Venues"],"summary":"Countries with their number of clubs","description":"`count` = clubs with a current source (status active; the figure the web pages show), `listed` = `count` plus older unverified network listings. Countries with only older listings have `count` 0.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ClubCountryCount"}}}}}}}}}},"/clubs/{country}/{slug}":{"get":{"tags":["Venues"],"summary":"One club (only clubs with a public detail page)","parameters":[{"name":"country","in":"path","required":true,"schema":{"type":"string"},"description":"ISO code or country slug."},{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/ClubDetail"},"meta":{"$ref":"#/components/schemas/OsmAttribution"}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VenuesError"}}}},"422":{"description":"Invalid query","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VenuesError"}}}}}}},"/nudist":{"get":{"tags":["Venues"],"summary":"List naturist places by country or near a point","description":"OpenStreetMap-derived (ODbL 1.0, © OpenStreetMap contributors). Requires `country` or `near`.","parameters":[{"name":"country","in":"query","schema":{"type":"string"},"description":"ISO 3166-1 alpha-2 code (e.g. `us`) or country slug (e.g. `united-states`)."},{"name":"near","in":"query","schema":{"type":"string","pattern":"^-?\\d+(\\.\\d+)?,-?\\d+(\\.\\d+)?$"},"description":"`lat,lng` — results sorted by distance."},{"name":"radiusKm","in":"query","schema":{"type":"number","minimum":1,"maximum":250,"default":50},"description":"Only with `near`."},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/NudistPlace"}},"meta":{"$ref":"#/components/schemas/VenueListMeta"}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VenuesError"}}}},"422":{"description":"Invalid query","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VenuesError"}}}}}}},"/nudist/countries":{"get":{"tags":["Venues"],"summary":"Countries with their number of listed naturist places","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CountryCount"}}}}}}}}}},"/nudist/{country}/{slug}":{"get":{"tags":["Venues"],"summary":"One naturist place (only places with a public detail page)","parameters":[{"name":"country","in":"path","required":true,"schema":{"type":"string"},"description":"ISO code or country slug."},{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/NudistPlaceDetail"},"meta":{"$ref":"#/components/schemas/OsmAttribution"}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VenuesError"}}}},"422":{"description":"Invalid query","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VenuesError"}}}}}}},"/destinations":{"get":{"tags":["Venues"],"summary":"Editorial lifestyle destinations","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Destination"}}}}}}}}}},"/destinations/{slug}":{"get":{"tags":["Venues"],"summary":"One destination with its member figures, upcoming travellers and nearby venues (member list: membersEndpoint)","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"$ref":"#/components/schemas/DestinationDetail"},"meta":{"$ref":"#/components/schemas/OsmAttribution"}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VenuesError"}}}},"422":{"description":"Invalid query","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VenuesError"}}}}}}},"/me/push-device":{"put":{"tags":["me"],"summary":"Register this phone for push notifications (new messages)","description":"Call at every app launch while notifications are allowed. The registration belongs to the current session: logging out, \"log out other devices\" or a password change stops notifications to this phone. A phone signed in to another account before moves to this one. Notifications never contain the message text: the body is \"You have a new message\", or \"<username> sent you a message\" with `showSender`. `data` = { type: \"message\", conversationId }; `badge` = unread total. The notification text is in the language of this request (Accept-Language), stored with the phone, so phones in different languages on one account each get their own; without Accept-Language the phone follows the account's language (users.locale).","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token","platform"],"properties":{"token":{"type":"string","pattern":"^Expo(nent)?PushToken[[A-Za-z0-9_-]{8,200}]$","description":"Expo push token (Notifications.getExpoPushTokenAsync)"},"platform":{"type":"string","enum":["ios","android"]},"showSender":{"type":"boolean","default":false},"appVersion":{"type":"string","maxLength":40}}}}}},"responses":{"200":{"description":"Registered","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"registered":{"const":true},"showSender":{"type":"boolean"}}}}}}}},"401":{"description":"Sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Not an Expo push token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}},"delete":{"tags":["me"],"summary":"This phone stops getting push notifications (before logout, or notifications turned off in the app)","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["token"],"properties":{"token":{"type":"string","pattern":"^Expo(nent)?PushToken[[A-Za-z0-9_-]{8,200}]$","description":"Expo push token (Notifications.getExpoPushTokenAsync)"}}}}}},"responses":{"200":{"description":"Removed","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"object","properties":{"removed":{"type":"boolean"}}}}}}}},"401":{"description":"Sign in required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}}}