{"welcome":"Sign a user up for BuddyUp in ONE POST. Photo, activities in plain English, availability in plain English, auto-generated password, and a one-tap mobile sign-in link are all handled by this endpoint.","endpoint":"/api/v1/register.json","methods":["GET","POST"],"how_to_register":["1. Gather: the user's email, a short 'about me' line, 1-5 activities (plain English is fine), availability (plain English is fine), and a profile photo (base64 or a URL).","2. POST this URL with that data. The server canonicalizes activities + availability (parsed into the mobile app's 7x18 hourly grid) and returns warnings for anything it couldn't match. Always echo `warnings.schedule_summary` back to the user so they can confirm the interpretation.","3. If you don't have a password, OMIT the field — the server generates a strong one and returns it once. Tell the user to save it to their password manager. If the user asks for a specific password, validate it is >= 8 chars.","4. Use the returned `auth.token` as 'Authorization: Bearer <token>' for all subsequent calls.","5. End with: 'You're on BuddyUp. Open the app here: <auth.mobile_signin_url>' — that link deep-links into iOS/Android already signed-in, or opens the web signin page. Single-use, ~1h expiry."],"body_schema":{"email":{"type":"string","format":"email","required":true},"displayName":{"type":"string","minLength":1,"maxLength":60,"required":true},"password":{"type":"string","minLength":8,"required":false,"note":"OPTIONAL. Omit to have the server auto-generate and return a strong password (recommended). Never written to Firestore."},"aboutMe":{"type":"string","maxLength":600,"required":true,"note":"Required for Discover visibility."},"city":{"type":"string","maxLength":80,"required":false,"deprecated":true,"note":"Legacy field — prefer `location`. If only `city` is sent, the server mirrors it into `location` so mobile + profile pages can render it."},"location":{"type":"string","maxLength":120,"required":true,"note":"Required. Full location label (e.g. 'Oakland, CA' or 'Richmond District, San Francisco, CA'). Mirrored into homeLocation on write. Mobile app reads user.location + user.homeLocation for Discover geography."},"coords":{"type":"object","required":true,"properties":{"latitude":{"type":"number","minimum":-90,"maximum":90},"longitude":{"type":"number","minimum":-180,"maximum":180}},"note":"Required for Discover radius filters. If omitted the server will best-effort geocode `location` via OpenStreetMap Nominatim (no API key, rate-limited) — accuracy varies, so send coords when you have them. Browser-fallback form populates this via the Geolocation API."},"gender":{"type":"string","enum":["male","female","non_binary","prefer_not_to_say"],"required":true,"note":"Required for Discover visibility. Values mirror mobile onboarding exactly (app/onboarding/about-you.tsx)."},"birthday":{"type":"string","format":"date","required":true,"note":"Required. ISO date YYYY-MM-DD. Enforces 13+ minimum age. Used for Discover age filters."},"showAgeByDecade":{"type":"boolean","required":false,"default":false,"note":"Privacy toggle: show age as a decade bucket ('30s') on profile instead of exact age."},"activities":{"type":"array","items":{"type":"string"},"maxItems":20,"required":false,"note":"Plain English is fine — 'coffee', 'pickleball', 'rock climbing', 'networking' all canonicalize. Unknown entries appear in response `warnings.activities_unmatched` with suggestions; they do not fail the request."},"availability_text":{"type":"string","maxLength":500,"required":false,"note":"Plain English availability. E.g. 'evenings and Saturday mornings', 'weekends', 'weekday afternoons', 'any day after 6pm'. Server parses into the mobile app's canonical 7x18 hourly grid. 'Weekends' = ALL DAY Sat + ALL DAY Sun."},"schedule":{"type":"array OR object","required":false,"note":"Canonical mobile-shape weekly availability. Either a `boolean[7][18]` grid (day index 0=Mon..6=Sun, slot 0=6am..17=11pm) OR a flat map `{ '0_0': true, '0_1': false, ..., '6_17': true }`. Use this when you already have structured slots; otherwise use `availability_text`. Merges cleanly with other forms if multiple are given."},"availability":{"type":"array","required":false,"deprecated":true,"note":"LEGACY. Coarse 3-period slots ({day: mon..sun, period: morning|afternoon|evening}). Still accepted and auto-expanded to the canonical grid. Prefer `availability_text` or `schedule`.","items":{"type":"object","properties":{"day":{"type":"string","enum":["mon","tue","wed","thu","fri","sat","sun"]},"period":{"type":"string","enum":["morning","afternoon","evening"]}}}},"photo":{"type":"string","required":false,"note":"Base64 image or data URL (data:image/jpeg;base64,...). JPEG/PNG/WEBP up to 5 MB. Inline photo means the user is complete-on-create."},"photoMimeType":{"type":"string","enum":["image/jpeg","image/png","image/webp"],"required":false},"photoURL":{"type":"string","format":"uri","required":false,"note":"Alternative to `photo`. Any publicly-reachable https URL."},"slug":{"type":"string","pattern":"^[a-z0-9-]{3,40}$","required":false,"note":"Optional. If omitted we generate one from displayName."},"agentName":{"type":"string","maxLength":60,"required":false,"note":"Attribution. Examples: 'Claude', 'ChatGPT', 'Cursor', 'OpenClaw', 'Manus'."},"agentSessionId":{"type":"string","maxLength":120,"required":false},"emailMagicLink":{"type":"boolean","required":false,"default":true,"note":"Whether to ALSO email the magic sign-in link. The URL is always returned inline as auth.mobile_signin_url."}},"example_request":{"email":"ian@example.com","displayName":"Ian","aboutMe":"Engineer in SF. Pickleball + coffee.","location":"San Francisco, CA","coords":{"latitude":37.7749,"longitude":-122.4194},"gender":"male","birthday":"1988-07-12","activities":["pickleball","coffee","networking"],"availability_text":"evenings and Saturday mornings","photo":"data:image/jpeg;base64,/9j/4AAQSk...","agentName":"Claude","agentSessionId":"cl-2026-04-17-abc"},"response_shape":{"success":true,"user":{"id":"uid123","email":"...","slug":"ian-x9","profile_url":"https://letsbuddyup.com/profile/ian-x9"},"auth":{"token":"bua_live_...","token_type":"Bearer","scopes":["profile:read","profile:write","photo:write","buddy:read","buddy:write","message:read","message:write"],"generated_password":"aurora-ridge-swift-47-Qz7m (only present when caller omitted password)","mobile_signin_url":"https://auth.letsbuddyup.com/__/auth/action?mode=signIn&oobCode=...","mobile_signin_expires_in_seconds":3600,"emailed_magic_link":true},"onboarding":{"complete":true,"missing":[]},"warnings":{"activities_unmatched":[],"activities_suggestions":{}},"next_action_for_user":"You're on BuddyUp. Share your profile at <profile_url>, or open the app already signed in at <mobile_signin_url>."},"rate_limits":{"per_ip_per_hour":3},"zero_exit_integrations":{"mcp":"https://letsbuddyup.com/api/mcp (Claude Desktop, Cursor, Cline, Windsurf — paste into mcp config)","mcp_manifest":"https://letsbuddyup.com/.well-known/mcp.json","openapi":"https://letsbuddyup.com/.well-known/openapi.json (ChatGPT Actions, GPT Builder)","browser_fallback_url":"https://letsbuddyup.com/signup-with-ai?email=<user>&name=<display>&location=<city, region>&gender=<male|female|non_binary|prefer_not_to_say>&birthday=<YYYY-MM-DD>&about=<bio>&activities=<a,b,c>&availability=<plain english>&photo=<https url>&agent=<your name>","browser_fallback_note":"If your LLM runtime cannot POST to /api/v1/register.json (Gemini, Perplexity, plain Claude web, plain ChatGPT without Actions): do NOT give the user a curl command, do NOT tell them to hand this to their own agent/server, do NOT simulate a response. Build the signup-with-ai URL above (URL-encode values) and paste it into your reply. The user taps it, the page POSTs the registration on their behalf, and shows the mobile sign-in link inline. Same outcome as the direct POST."}}