{"openapi":"3.1.0","info":{"title":"Aurqa Language Learning Platform - Refactored","description":"A modern language learning platform with AI-powered conversation practice","version":"2.0.0"},"paths":{"/api/version/android":{"get":{"tags":["version"],"summary":"Get Android Version","description":"Version policy for the Android Capacitor app.\n\nDefaults track the latest internal-test build. Override via env when a\nnew release ships or a forced upgrade is needed.","operationId":"get_android_version_api_version_android_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/login":{"post":{"tags":["auth","authentication"],"summary":"Login","description":"Authenticate user login","operationId":"login_auth_login_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_login_auth_login_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/register":{"post":{"tags":["auth","authentication"],"summary":"Register","description":"Register a new user with optional invite code for extended trial","operationId":"register_auth_register_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegistrationRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/complete-profile/{user_id}":{"post":{"tags":["auth","authentication"],"summary":"Complete Profile","description":"Mark user profile as complete after they've filled in required information","operationId":"complete_profile_auth_complete_profile__user_id__post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/account-status/{user_id}":{"get":{"tags":["auth","authentication"],"summary":"Get Account Status","description":"Get account status and security information","operationId":"get_account_status_auth_account_status__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/logout":{"post":{"tags":["auth","authentication"],"summary":"Logout","description":"Logout user by clearing the auth cookie.\nCRITICAL: Ensures proper cookie deletion across all environments including GKE.\nUses aggressive multi-attempt clearing to handle OAuth and regular sessions.\n\nIMPORTANT: Cookie deletion parameters must EXACTLY match the parameters used when setting the cookie.\nSee: https://stackoverflow.com/questions/67094564/fastapi-deleting-cookies-after-logout-not-working","operationId":"logout_auth_logout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/refresh":{"post":{"tags":["auth","authentication"],"summary":"Refresh Token","description":"Refresh JWT token if current token is still valid","operationId":"refresh_token_auth_refresh_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/me":{"get":{"tags":["auth","authentication"],"summary":"Get Current User","description":"Get current user info from auth cookie or Authorization header","operationId":"get_current_user_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/ws-token":{"get":{"tags":["auth","authentication"],"summary":"Get Websocket Token","description":"Get a WebSocket token for authenticated users (since WebSocket can't access cookies across domains)","operationId":"get_websocket_token_auth_ws_token_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/resend-verification":{"post":{"tags":["auth","authentication"],"summary":"Resend Verification Email","description":"Resend verification email to user","operationId":"resend_verification_email_auth_resend_verification_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_resend_verification_email_auth_resend_verification_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/resend-verification-by-email":{"post":{"tags":["auth","authentication"],"summary":"Resend Verification By Email","description":"Resend verification email by username or email address. Returns generic message to prevent account enumeration.","operationId":"resend_verification_by_email_auth_resend_verification_by_email_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResendVerificationEmailRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/verify-email":{"get":{"tags":["auth","authentication"],"summary":"Verify Email","description":"Verify user email with token","operationId":"verify_email_auth_verify_email_get","parameters":[{"name":"token","in":"query","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/check-verification/{user_id}":{"get":{"tags":["auth","authentication"],"summary":"Check Verification Status","description":"Check if user's email is verified","operationId":"check_verification_status_auth_check_verification__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/forgot-password":{"post":{"tags":["auth","authentication"],"summary":"Forgot Password","description":"Request password reset. Returns generic message to prevent account enumeration.\nFollows OWASP best practices for password reset.","operationId":"forgot_password_auth_forgot_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForgotPasswordRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/reset-password":{"post":{"tags":["auth","authentication"],"summary":"Reset Password","description":"Reset password using token. Token is single-use and expires after 1 hour.","operationId":"reset_password_auth_reset_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPasswordRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/password-status":{"get":{"tags":["auth","authentication"],"summary":"Password Status","description":"Whether this account has a password its owner can change.\n\nThe settings row needs to say either \"Change password\" or \"Set a password\"\nBEFORE the user taps it, and the frontend cannot work that out on its own:\nan OAuth account's `hashed_password` is non-empty (it holds the literal\n`oauth:{provider}:{id}`), so the existing `has_password` field on\n/account-status reads true for exactly the accounts that have none.","operationId":"password_status_auth_password_status_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/change-password":{"post":{"tags":["auth","authentication"],"summary":"Change Password","description":"Change your own password, having proved you know the current one.\n\nReturns a REPLACEMENT token. That is not a convenience, it is required:\nsetting `password_changed_at` revokes every token issued before it, and the\ncaller's own token was issued before it. Without a fresh one the user is\nsigned out by their own successful password change — and on the very next\nrequest, so it reads as the change having failed.\n\nThe one-second backdate is for the same machinery. `iat` is whole seconds\n(jwt encodes an int), and the check in auth_service is `token_time <\npassword_changed_at`. Mint the replacement in the same wall-clock second as\nthe change and the truncated `iat` can land a fraction BEFORE\n`password_changed_at`, revoking the token we just issued. Backdating the\nstamp by a second puts every genuinely-old token on the revoked side and the\nnew one safely on the live side.","operationId":"change_password_auth_change_password_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePasswordRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/request-password-setup":{"post":{"tags":["auth","authentication"],"summary":"Request Password Setup","description":"Mail a set-a-password link to a signed-in OAuth user's own address.\n\nWhy this is not \"just let them set one\": there is no current password to\ncheck, so a bare session would be the only thing standing between a stolen\ntoken and permanent account takeover. Today that token dies in 24 hours;\nminting a password from it would make the takeover permanent. The mailbox is\nthe second proof, and it is the same proof the provider used to vouch for\nthem in the first place.\n\nSending it to `current_user.email` rather than an address in the body is the\npoint — the caller does not get to choose where the link goes.\n\n(When Sign in with Apple goes live this needs a second path: Apple's private\nrelay addresses, already detected as `is_private_email` in oauth_routes, are\nmailboxes many people never read. The convention there is a fresh OAuth\nbounce as the re-authentication instead of an email.)","operationId":"request_password_setup_auth_request_password_setup_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/activation/{token}":{"get":{"tags":["auth","authentication"],"summary":"Activation Info","description":"Who this link is for, so the page can say so.\n\nA student who is told \"set your password\" by a product they have never\nsigned up for needs to see their own school's name and their own address on\nthat screen, or the only sane reaction is to close the tab. The address is\nthe one the link was sent to, so it reveals nothing to the person holding\nit; an invalid token reveals nothing at all.","operationId":"activation_info_auth_activation__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/activate":{"post":{"tags":["auth","authentication"],"summary":"Activate Account","description":"Set the first password on a school-provisioned account and sign in.\n\nSigns in on success, rather than bouncing to the login form: the student has\njust proved control of the address and chosen a credential thirty seconds\nago, and a school's first-run flow that ends on a login screen is a school's\nfirst-run flow that ends in a support queue.","operationId":"activate_account_auth_activate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivateAccountRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/create-anonymous-user":{"post":{"tags":["auth","authentication"],"summary":"Create Anonymous User","description":"Create a temporary anonymous user for onboarding preview activities.\nThis user can be converted to a full account when they register.\nReturns a temporary user ID that can be used for preview features.","operationId":"create_anonymous_user_auth_create_anonymous_user_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/claim-guest-session":{"post":{"tags":["auth","authentication"],"summary":"Claim Guest Session","description":"Carry a guest session's call data onto the account that just logged in.\n\nNancy, 2026-08-26: \"when a student does a whole class as a guest and then\ncreates an account or joins after the class finishes, does their transcript\nfrom that class get carried over?\"\n\nSigning UP already did this — `_transfer_anonymous_call_data` runs on\nregistration and on the OAuth merge. LOGGING IN did not, and for a school\nthat is the common half: a student who already has a FluenTea account joins\nthe class by code without logging in, does the whole hour as a guest, and\nthen signs in. Their transcript, their presence in that room and therefore\ntheir attendance all stayed on the throwaway guest row.\n\nTwo guards, because this moves data between accounts:\n  * the source must actually BE anonymous — never another real account;\n  * and recent. A stale `anonymous_user_id` in a shared browser's storage\n    must not hand a stranger's guest session to whoever logs in next week.","operationId":"claim_guest_session_auth_claim_guest_session_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/update-anonymous-profile":{"post":{"tags":["auth","authentication"],"summary":"Update Anonymous Profile","description":"Update anonymous user's profile (name, languages) during onboarding preview.","operationId":"update_anonymous_profile_auth_update_anonymous_profile_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/merge-onboarding":{"post":{"tags":["auth","authentication"],"summary":"Merge Onboarding Data","description":"Merge onboarding data into a newly registered OAuth user's profile.\nCalled from OAuthCallback after Google/Microsoft signup to transfer\ntarget_languages, native_languages, practice_method, and anonymous user activity data.","operationId":"merge_onboarding_data_auth_merge_onboarding_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/oauth/providers":{"get":{"tags":["oauth","oauth"],"summary":"Oauth Providers","description":"Which social logins are actually usable in this environment.\n\nThe login/signup pages render one button per provider, and an unconfigured\nprovider's /login endpoint 503s — which would strand the user on a raw JSON\nerror page. The frontend filters its buttons on this so a provider whose\ncredentials aren't deployed yet simply isn't offered, and appears on its own\nonce the secrets land (no frontend release needed).","operationId":"oauth_providers_auth_oauth_providers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/oauth/google/login":{"get":{"tags":["oauth","oauth"],"summary":"Google Login","description":"Initiate Google OAuth login","operationId":"google_login_auth_oauth_google_login_get","parameters":[{"name":"platform","in":"query","required":false,"schema":{"type":"string","default":"web","title":"Platform"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/oauth/google/signup":{"get":{"tags":["oauth","oauth"],"summary":"Google Signup","description":"Initiate Google OAuth signup","operationId":"google_signup_auth_oauth_google_signup_get","parameters":[{"name":"source","in":"query","required":false,"schema":{"type":"string","default":"direct","title":"Source"}},{"name":"platform","in":"query","required":false,"schema":{"type":"string","default":"web","title":"Platform"}},{"name":"utm_source","in":"query","required":false,"schema":{"type":"string","title":"Utm Source"}},{"name":"utm_medium","in":"query","required":false,"schema":{"type":"string","title":"Utm Medium"}},{"name":"utm_campaign","in":"query","required":false,"schema":{"type":"string","title":"Utm Campaign"}},{"name":"utm_content","in":"query","required":false,"schema":{"type":"string","title":"Utm Content"}},{"name":"utm_term","in":"query","required":false,"schema":{"type":"string","title":"Utm Term"}},{"name":"gclid","in":"query","required":false,"schema":{"type":"string","title":"Gclid"}},{"name":"referrer","in":"query","required":false,"schema":{"type":"string","title":"Referrer"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/oauth/google/callback":{"get":{"tags":["oauth","oauth"],"summary":"Google Callback","description":"Handle Google OAuth callback","operationId":"google_callback_auth_oauth_google_callback_get","parameters":[{"name":"code","in":"query","required":true,"schema":{"type":"string","title":"Code"}},{"name":"state","in":"query","required":true,"schema":{"type":"string","title":"State"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/oauth/google/signup/callback":{"get":{"tags":["oauth","oauth"],"summary":"Google Signup Callback","description":"Handle Google OAuth signup callback","operationId":"google_signup_callback_auth_oauth_google_signup_callback_get","parameters":[{"name":"code","in":"query","required":true,"schema":{"type":"string","title":"Code"}},{"name":"state","in":"query","required":true,"schema":{"type":"string","title":"State"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/oauth/apple/login":{"get":{"tags":["oauth","oauth"],"summary":"Apple Login","description":"Initiate Sign In with Apple (login)","operationId":"apple_login_auth_oauth_apple_login_get","parameters":[{"name":"platform","in":"query","required":false,"schema":{"type":"string","default":"web","title":"Platform"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/oauth/apple/signup":{"get":{"tags":["oauth","oauth"],"summary":"Apple Signup","description":"Initiate Sign In with Apple (signup)","operationId":"apple_signup_auth_oauth_apple_signup_get","parameters":[{"name":"source","in":"query","required":false,"schema":{"type":"string","default":"direct","title":"Source"}},{"name":"platform","in":"query","required":false,"schema":{"type":"string","default":"web","title":"Platform"}},{"name":"utm_source","in":"query","required":false,"schema":{"type":"string","title":"Utm Source"}},{"name":"utm_medium","in":"query","required":false,"schema":{"type":"string","title":"Utm Medium"}},{"name":"utm_campaign","in":"query","required":false,"schema":{"type":"string","title":"Utm Campaign"}},{"name":"utm_content","in":"query","required":false,"schema":{"type":"string","title":"Utm Content"}},{"name":"utm_term","in":"query","required":false,"schema":{"type":"string","title":"Utm Term"}},{"name":"gclid","in":"query","required":false,"schema":{"type":"string","title":"Gclid"}},{"name":"referrer","in":"query","required":false,"schema":{"type":"string","title":"Referrer"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/oauth/apple/callback":{"post":{"tags":["oauth","oauth"],"summary":"Apple Callback","description":"Handle Sign In with Apple callback (login). POST — Apple uses form_post.","operationId":"apple_callback_auth_oauth_apple_callback_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_apple_callback_auth_oauth_apple_callback_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/oauth/apple/signup/callback":{"post":{"tags":["oauth","oauth"],"summary":"Apple Signup Callback","description":"Handle Sign In with Apple callback (signup). POST — Apple uses form_post.","operationId":"apple_signup_callback_auth_oauth_apple_signup_callback_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_apple_signup_callback_auth_oauth_apple_signup_callback_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/oauth/microsoft/login":{"get":{"tags":["oauth","oauth"],"summary":"Microsoft Login","description":"Initiate Microsoft OAuth login","operationId":"microsoft_login_auth_oauth_microsoft_login_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/oauth/microsoft/signup":{"get":{"tags":["oauth","oauth"],"summary":"Microsoft Signup","description":"Initiate Microsoft OAuth signup","operationId":"microsoft_signup_auth_oauth_microsoft_signup_get","parameters":[{"name":"source","in":"query","required":false,"schema":{"type":"string","default":"direct","title":"Source"}},{"name":"utm_source","in":"query","required":false,"schema":{"type":"string","title":"Utm Source"}},{"name":"utm_medium","in":"query","required":false,"schema":{"type":"string","title":"Utm Medium"}},{"name":"utm_campaign","in":"query","required":false,"schema":{"type":"string","title":"Utm Campaign"}},{"name":"utm_content","in":"query","required":false,"schema":{"type":"string","title":"Utm Content"}},{"name":"utm_term","in":"query","required":false,"schema":{"type":"string","title":"Utm Term"}},{"name":"gclid","in":"query","required":false,"schema":{"type":"string","title":"Gclid"}},{"name":"referrer","in":"query","required":false,"schema":{"type":"string","title":"Referrer"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/oauth/microsoft/callback":{"get":{"tags":["oauth","oauth"],"summary":"Microsoft Callback","description":"Handle Microsoft OAuth callback","operationId":"microsoft_callback_auth_oauth_microsoft_callback_get","parameters":[{"name":"code","in":"query","required":true,"schema":{"type":"string","title":"Code"}},{"name":"state","in":"query","required":true,"schema":{"type":"string","title":"State"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/oauth/microsoft/signup/callback":{"get":{"tags":["oauth","oauth"],"summary":"Microsoft Signup Callback","description":"Handle Microsoft OAuth signup callback","operationId":"microsoft_signup_callback_auth_oauth_microsoft_signup_callback_get","parameters":[{"name":"code","in":"query","required":true,"schema":{"type":"string","title":"Code"}},{"name":"state","in":"query","required":true,"schema":{"type":"string","title":"State"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/oauth/signup/state/{state}":{"get":{"tags":["oauth","oauth"],"summary":"Get Oauth Signup State","description":"Get OAuth signup state data for frontend","operationId":"get_oauth_signup_state_auth_oauth_signup_state__state__get","parameters":[{"name":"state","in":"path","required":true,"schema":{"type":"string","title":"State"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/oauth/signup/complete":{"post":{"tags":["oauth","oauth"],"summary":"Complete Oauth Signup","description":"Complete OAuth user signup with required validations","operationId":"complete_oauth_signup_auth_oauth_signup_complete_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthSignupComplete"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/ui-language":{"put":{"tags":["users","users"],"summary":"Set Ui Language","description":"Persist the app UI language so emails and notifications can follow it.\n\nprofile_data['ui_language'] is read by five i18n consumers (notification,\nemail, booking, school enrolment, scam-removal) but until 2026-08-25 was\nwritten by NOTHING, so every email fell back to English. The signup path\nnow writes it once; this keeps it current when the user switches language\nin the app (LanguageContext calls it fire-and-forget).\n\nA dedicated endpoint rather than a key on the big profile PUT on purpose:\nthat handler ignores unknown fields silently, which is exactly how the\n/privacy-settings email toggles became a no-op.","operationId":"set_ui_language_users_ui_language_put","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users":{"get":{"tags":["users","users"],"summary":"List Users","description":"Basic user list used by the chat frontend.\n\nReturns up to 100 active users, excluding the AI account.\nIncludes relationship_status if user is logged in.","operationId":"list_users_users_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDiscoveryResponse"},"title":"Response List Users Users Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/preview":{"get":{"tags":["users","users"],"summary":"Preview Language Partners","description":"Public preview endpoint for onboarding flow - no authentication required.\nShows potential language partners: native speakers and fellow learners of the target language.\nPrioritizes users with profile pictures and recent activity.\n\nDELIBERATELY STILL PUBLIC: its only caller is /onboarding/preview/partners,\na pre-signup route (listed in App.jsx `publicRoutes`) that anonymous visitors\nreach from the onboarding flow — requiring auth would break the funnel.\n\nBut it is now DE-IDENTIFIED. It used to publish, to anyone on the internet,\na language-filterable directory of real members: user id, username, full\nname, photo, bio, location and last-active time. Cards now carry only a\nfirst name, the language pair, and a rough last-active time — enough social\nproof for the preview, not a scrapeable member list. The `id` is a random\nper-response value (the page uses it only as a React key) so a card can't be\ncross-referenced against /users/profile/{id} or any other endpoint.","operationId":"preview_language_partners_users_preview_get","parameters":[{"name":"target_language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by users who speak this language natively","title":"Target Language"},"description":"Filter by users who speak this language natively"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":20,"description":"Number of users to return (max 20 for preview)","default":3,"title":"Limit"},"description":"Number of users to return (max 20 for preview)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDiscoveryResponse"},"title":"Response Preview Language Partners Users Preview Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/discover/paginated":{"get":{"tags":["users","users"],"summary":"Discover Users","description":"Discover users for language learning partnerships.","operationId":"discover_users_users_discover_paginated_get","parameters":[{"name":"language_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by native language","title":"Language Filter"},"description":"Filter by native language"},{"name":"learning_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by target language","title":"Learning Filter"},"description":"Filter by target language"},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Search by username, full name, or bio","title":"Search"},"description":"Search by username, full name, or bio"},{"name":"location_search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Search by location","title":"Location Search"},"description":"Search by location"},{"name":"gender_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by user gender (male, female, non_binary)","title":"Gender Filter"},"description":"Filter by user gender (male, female, non_binary)"},{"name":"sort_by","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Sort users by field (name, last_active, rating, reviews, recent)","title":"Sort By"},"description":"Sort users by field (name, last_active, rating, reviews, recent)"},{"name":"online_only","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filter to show only online users","title":"Online Only"},"description":"Filter to show only online users"},{"name":"school_only","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Only show learners from the viewer's school(s)","title":"School Only"},"description":"Only show learners from the viewer's school(s)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"description":"Number of users to return","default":20,"title":"Limit"},"description":"Number of users to return"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","description":"Number of users to skip","default":0,"title":"Offset"},"description":"Number of users to skip"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedUserDiscoveryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/discover":{"get":{"tags":["users","users"],"summary":"Discover Users","description":"Discover users for language learning partnerships.","operationId":"discover_users_users_discover_get","parameters":[{"name":"language_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by native language","title":"Language Filter"},"description":"Filter by native language"},{"name":"learning_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by target language","title":"Learning Filter"},"description":"Filter by target language"},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Search by username, full name, or bio","title":"Search"},"description":"Search by username, full name, or bio"},{"name":"location_search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Search by location","title":"Location Search"},"description":"Search by location"},{"name":"gender_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by user gender (male, female, non_binary)","title":"Gender Filter"},"description":"Filter by user gender (male, female, non_binary)"},{"name":"sort_by","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Sort users by field (name, last_active, rating, reviews, recent)","title":"Sort By"},"description":"Sort users by field (name, last_active, rating, reviews, recent)"},{"name":"online_only","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filter to show only online users","title":"Online Only"},"description":"Filter to show only online users"},{"name":"school_only","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Only show learners from the viewer's school(s)","title":"School Only"},"description":"Only show learners from the viewer's school(s)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"description":"Number of users to return","default":20,"title":"Limit"},"description":"Number of users to return"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","description":"Number of users to skip","default":0,"title":"Offset"},"description":"Number of users to skip"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDiscoveryResponse"},"title":"Response Discover Users Users Discover Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/profile":{"put":{"tags":["users","users"],"summary":"Update User Profile","description":"Update the current user's profile information.","operationId":"update_user_profile_users_profile_put","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfileEditUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/profile/{user_id}":{"get":{"tags":["users","users"],"summary":"Get User Profile","description":"Get detailed user profile with reviews and friend status.","operationId":"get_user_profile_users_profile__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/schedulable":{"get":{"tags":["users","users"],"summary":"Schedulable People","description":"People this user could plausibly put in a meeting: who do they KNOW?\n\nThe scheduler used to search /users/discover, which is the language-partner\nDISCOVERY index — filtered to public profiles and matching preferences. A\ncolleague who never set up a learner profile is not in it, so Nancy's\n2026-08-28 walkthrough hit: \"My natural instinct is to type in the person I\nwant to meet with. Unless I've friended them through FluenTea, they do not\nshow up. Confusing.\"\n\nDiscovering a language partner and scheduling with a colleague are\ndifferent questions and deserve different sources. This one is\nrelationship-based, which is what every calendar does — Google autocompletes\nfrom your contacts and your organisation's directory, not from a social\ngraph. Three sources, unioned:\n\n    * people at the same school (any role)\n    * people they have actually been in a call with\n    * accepted friends\n\nRanked by which of those they came from — a colleague you met yesterday is\na better guess than someone you shared one room with in March — then by\nname. There is no \"everyone else\" tier on purpose: a scheduler that offers\nstrangers is the discovery index again.\n\nThe school wall applies, because this returns people.","operationId":"schedulable_people_users_schedulable_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":25,"minimum":1,"default":10,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/friend-request":{"post":{"tags":["users","users"],"summary":"Send Friend Request","description":"Send a friend request to another user.","operationId":"send_friend_request_users_friend_request_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FriendRequestCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FriendRequestResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/friend-request/{request_id}":{"put":{"tags":["users","users"],"summary":"Respond To Friend Request","description":"Accept or reject a friend request.","operationId":"respond_to_friend_request_users_friend_request__request_id__put","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FriendRequestUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FriendRequestResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/test-friends/{user_id}":{"get":{"tags":["users","users"],"summary":"Test Friend Status","description":"Test endpoint to diagnose friend status issues.","operationId":"test_friend_status_users_test_friends__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/test-websocket/{user_id}":{"post":{"tags":["users","users"],"summary":"Test Websocket Notification","description":"Test WebSocket notification delivery to a specific user (admin-only debug).","operationId":"test_websocket_notification_users_test_websocket__user_id__post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/friends":{"get":{"tags":["users","users"],"summary":"Get Friends","description":"Get all friends of the current user.","operationId":"get_friends_users_friends_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDiscoveryResponse"},"title":"Response Get Friends Users Friends Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/friend-requests":{"get":{"tags":["users","users"],"summary":"Get Friend Requests","description":"Get incoming friend requests for the current user.\nOptimized with eager loading to avoid N+1 queries.","operationId":"get_friend_requests_users_friend_requests_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FriendRequestResponse"},"title":"Response Get Friend Requests Users Friend Requests Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/friend-requests/outgoing":{"get":{"tags":["users","users"],"summary":"Get Outgoing Friend Requests","description":"Get outgoing friend requests sent by the current user.\nOptimized with eager loading to avoid N+1 queries.","operationId":"get_outgoing_friend_requests_users_friend_requests_outgoing_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FriendRequestResponse"},"title":"Response Get Outgoing Friend Requests Users Friend Requests Outgoing Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/friend-request/{receiver_id}":{"delete":{"tags":["users","users"],"summary":"Cancel Friend Request","description":"Cancel a pending friend request sent to another user.","operationId":"cancel_friend_request_users_friend_request__receiver_id__delete","parameters":[{"name":"receiver_id","in":"path","required":true,"schema":{"type":"string","title":"Receiver Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/can-review/{user_id}":{"get":{"tags":["users","users"],"summary":"Check Can Review","description":"Check if the current user can review the specified user.","operationId":"check_can_review_users_can_review__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/review":{"post":{"tags":["users","users"],"summary":"Create User Review","description":"Create a review for another user.","operationId":"create_user_review_users_review_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserReviewCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserReviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/reviews/{user_id}":{"get":{"tags":["users","users"],"summary":"Get User Reviews","description":"Get reviews for a specific user. Only works for tutors with account_type='tutor' or 'pro_tutor'.","operationId":"get_user_reviews_users_reviews__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"default":10,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserReviewResponse"},"title":"Response Get User Reviews Users Reviews  User Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/report":{"post":{"tags":["users","users"],"summary":"Report User","description":"Report a user for inappropriate behavior.","operationId":"report_user_users_report_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserReportCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__models__schemas__UserReportResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/test-slack-notification":{"post":{"tags":["users","users"],"summary":"Test Slack Notification","description":"Test endpoint to verify Slack integration is working.","operationId":"test_slack_notification_users_test_slack_notification_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/last-login":{"put":{"tags":["users","users"],"summary":"Update Last Login","description":"Update the current user's last login timestamp.","operationId":"update_last_login_users_last_login_put","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/fix-timezone-data":{"post":{"tags":["users","users"],"summary":"Fix Timezone Data Endpoint","description":"Fix timezone data for all users - admin endpoint.","operationId":"fix_timezone_data_endpoint_users_fix_timezone_data_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/conversation-partners/{user_id}":{"get":{"tags":["users","users"],"summary":"Get Conversation Partners","description":"Get users that the current user has actually conversed with.\n\nThis endpoint returns only users that the current user has had actual conversations with,\neither through text chat or video/audio calls. This is used for learning analytics\nto filter conversation partners to only those with real interaction history.\n\nReturns:\n    List of UserDiscoveryResponse objects for users the current user has conversed with.\n    Excludes AI users and the current user themselves.","operationId":"get_conversation_partners_users_conversation_partners__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserDiscoveryResponse"},"title":"Response Get Conversation Partners Users Conversation Partners  User Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/username/{username}/id":{"get":{"tags":["users","users"],"summary":"Get User Id By Username","description":"Get user ID (UUID) by username.","operationId":"get_user_id_by_username_users_username__username__id_get","parameters":[{"name":"username","in":"path","required":true,"schema":{"type":"string","title":"Username"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/welcome-tour/complete":{"post":{"tags":["users","users"],"summary":"Mark Welcome Tour Complete","description":"Mark that the user has completed the welcome tour","operationId":"mark_welcome_tour_complete_users_welcome_tour_complete_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/{user_id}/block":{"post":{"tags":["users","users"],"summary":"Block User","description":"Block a user","operationId":"block_user_users__user_id__block_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["users","users"],"summary":"Unblock User","description":"Unblock a user","operationId":"unblock_user_users__user_id__block_delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/blocked":{"get":{"tags":["users","users"],"summary":"Get Blocked Users","description":"Get list of blocked users","operationId":"get_blocked_users_users_blocked_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/test-export":{"get":{"tags":["users","users"],"summary":"Test Export","description":"Test endpoint to verify routing works","operationId":"test_export_users_test_export_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/users/export-data":{"get":{"tags":["users","users"],"summary":"Export User Data","description":"Export the user's data (GDPR Article 20). The payload is built by\ngdpr_export_service — the inline version this replaced declared six\ncategories it never populated and exported OTHER people's messages\n(selected by conversation, not by sender). See the service docstring for\nthe scope decisions.","operationId":"export_user_data_users_export_data_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/delete-account":{"delete":{"tags":["users","users"],"summary":"Delete User Account","description":"Delete user account and all associated data (GDPR Article 17 - Right to erasure)","operationId":"delete_user_account_users_delete_account_delete","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/consents":{"get":{"tags":["users","users"],"summary":"Get User Consents","description":"Get all consent records for the current user","operationId":"get_user_consents_users_consents_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UserConsentResponse"},"title":"Response Get User Consents Users Consents Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["users","users"],"summary":"Update Consent","description":"Update or create a consent record","operationId":"update_consent_users_consents_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsentUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserConsentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/consents/{consent_type}":{"delete":{"tags":["users","users"],"summary":"Withdraw Consent","description":"Withdraw consent for a specific type","operationId":"withdraw_consent_users_consents__consent_type__delete","parameters":[{"name":"consent_type","in":"path","required":true,"schema":{"type":"string","title":"Consent Type"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/email-preferences":{"get":{"tags":["users","users"],"summary":"Get Email Preferences","description":"Get user's email preferences","operationId":"get_email_preferences_users_email_preferences_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailPreferencesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["users","users"],"summary":"Update Email Preferences","description":"Update user's email preferences","operationId":"update_email_preferences_users_email_preferences_put","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__models__schemas__EmailPreferencesUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/timezone":{"put":{"tags":["users","users"],"summary":"Update User Timezone","description":"Update user's timezone preference.\n\nFor tutors, this updates their tutor profile timezone.\nFor students, this could be stored in a user preferences table (future enhancement).","operationId":"update_user_timezone_users_timezone_put","parameters":[{"name":"timezone_str","in":"query","required":true,"schema":{"type":"string","title":"Timezone Str"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/{user_id}":{"get":{"tags":["users","users"],"summary":"Get User By Id","description":"Get a single user by ID.","operationId":"get_user_by_id_users__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDiscoveryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/flashcard-suggestions":{"get":{"tags":["users","users"],"summary":"Get My Flashcard Suggestions","description":"Get flashcard suggestions from tutors for the current student.","operationId":"get_my_flashcard_suggestions_users_flashcard_suggestions_get","parameters":[{"name":"status_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(pending|approved|rejected)$"},{"type":"null"}],"title":"Status Filter"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/flashcard-suggestions/{suggestion_id}/approve":{"put":{"tags":["users","users"],"summary":"Approve Flashcard Suggestion","description":"Approve a flashcard suggestion from a tutor and apply the changes.","operationId":"approve_flashcard_suggestion_users_flashcard_suggestions__suggestion_id__approve_put","parameters":[{"name":"suggestion_id","in":"path","required":true,"schema":{"type":"string","title":"Suggestion Id"}},{"name":"student_note","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Note"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/flashcard-suggestions/{suggestion_id}/reject":{"put":{"tags":["users","users"],"summary":"Reject Flashcard Suggestion","description":"Reject a flashcard suggestion from a tutor.","operationId":"reject_flashcard_suggestion_users_flashcard_suggestions__suggestion_id__reject_put","parameters":[{"name":"suggestion_id","in":"path","required":true,"schema":{"type":"string","title":"Suggestion Id"}},{"name":"student_note","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Note"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/me/teaching-philosophy":{"get":{"tags":["users","users"],"summary":"Get My Teaching Philosophy","operationId":"get_my_teaching_philosophy_users_me_teaching_philosophy_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["users","users"],"summary":"Set My Teaching Philosophy","operationId":"set_my_teaching_philosophy_users_me_teaching_philosophy_put","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/moderation/{user_id}/block":{"post":{"tags":["user-moderation","user-moderation"],"summary":"Block User","description":"Block a user","operationId":"block_user_users_moderation__user_id__block_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserBlockResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["user-moderation","user-moderation"],"summary":"Unblock User","description":"Unblock a user","operationId":"unblock_user_users_moderation__user_id__block_delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/moderation/blocked":{"get":{"tags":["user-moderation","user-moderation"],"summary":"Get Blocked Users","description":"Get list of blocked users","operationId":"get_blocked_users_users_moderation_blocked_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true},"title":"Response Get Blocked Users Users Moderation Blocked Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/moderation/report":{"post":{"tags":["user-moderation","user-moderation"],"summary":"Report User","description":"Report a user for inappropriate behavior","operationId":"report_user_users_moderation_report_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserReportRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__schemas__users__UserReportResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/users/moderation/{user_id}/reports":{"get":{"tags":["user-moderation","user-moderation"],"summary":"Get User Reports","description":"Get reports for a user (admin only)","operationId":"get_user_reports_users_moderation__user_id__reports_get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/app__schemas__users__UserReportResponse"},"title":"Response Get User Reports Users Moderation  User Id  Reports Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/notifications/register-device":{"post":{"tags":["notifications","notifications"],"summary":"Register Device","description":"Register or update an FCM device token for push notifications.","operationId":"register_device_notifications_register_device_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDeviceRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/notifications/":{"get":{"tags":["notifications","notifications"],"summary":"Get Notifications","description":"Get in-app notifications for the current user.","operationId":"get_notifications_notifications__get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"unread_only","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Unread Only"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/NotificationResponse"},"title":"Response Get Notifications Notifications  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/notifications/unread-count":{"get":{"tags":["notifications","notifications"],"summary":"Get Unread Count","description":"Get count of unread notifications for the current user.","operationId":"get_unread_count_notifications_unread_count_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/notifications/{notification_id}/read":{"patch":{"tags":["notifications","notifications"],"summary":"Mark Notification As Read","description":"Mark a notification as read.","operationId":"mark_notification_as_read_notifications__notification_id__read_patch","parameters":[{"name":"notification_id","in":"path","required":true,"schema":{"type":"string","title":"Notification Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/notifications/mark-all-read":{"post":{"tags":["notifications","notifications"],"summary":"Mark All Notifications As Read","description":"Mark all notifications as read for the current user.","operationId":"mark_all_notifications_as_read_notifications_mark_all_read_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/notifications/{notification_id}":{"delete":{"tags":["notifications","notifications"],"summary":"Delete Notification","description":"Delete a notification.","operationId":"delete_notification_notifications__notification_id__delete","parameters":[{"name":"notification_id","in":"path","required":true,"schema":{"type":"string","title":"Notification Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/notifications/read/clear-all":{"delete":{"tags":["notifications","notifications"],"summary":"Clear All Read Notifications","description":"Delete all read notifications for the current user.","operationId":"clear_all_read_notifications_notifications_read_clear_all_delete","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/notifications/subscribe":{"post":{"tags":["notifications","notifications"],"summary":"Subscribe To Push Notifications","description":"Subscribe a user to push notifications.\nStores the push subscription details for sending notifications later.","operationId":"subscribe_to_push_notifications_notifications_subscribe_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/notifications/unsubscribe":{"post":{"tags":["notifications","notifications"],"summary":"Unsubscribe From Push Notifications","description":"Unsubscribe a user from push notifications.\nDeactivates the push subscription.","operationId":"unsubscribe_from_push_notifications_notifications_unsubscribe_post","parameters":[{"name":"endpoint","in":"query","required":true,"schema":{"type":"string","title":"Endpoint"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/notifications/subscriptions":{"get":{"tags":["notifications","notifications"],"summary":"Get User Subscriptions","description":"Get all push subscriptions for the current user.","operationId":"get_user_subscriptions_notifications_subscriptions_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/notifications/email-preferences":{"get":{"tags":["notifications","notifications"],"summary":"Get Email Preferences","description":"Get current user's email notification preferences.","operationId":"get_email_preferences_notifications_email_preferences_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["notifications","notifications"],"summary":"Update Email Preferences","description":"Update current user's email notification preferences.","operationId":"update_email_preferences_notifications_email_preferences_patch","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__routes__notification_routes__EmailPreferencesUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/notifications/push-preferences":{"get":{"tags":["notifications","notifications"],"summary":"Get Push Preferences Route","description":"Current user's notification preferences (push + school email, defaults\nmerged in).","operationId":"get_push_preferences_route_notifications_push_preferences_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["notifications","notifications"],"summary":"Update Push Preferences","description":"Update current user's notification preferences (partial).","operationId":"update_push_preferences_notifications_push_preferences_patch","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushPreferencesUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/notifications/unsubscribe/{token}/reason":{"post":{"tags":["notifications","notifications"],"summary":"Record Unsubscribe Reason","description":"Optional one-tap 'why did you unsubscribe' from the confirmation page.\n\nToken-verified, no auth (the user just clicked an email link and is not\nlogged in). Reasons are allow-listed; anything else is dropped silently.","operationId":"record_unsubscribe_reason_notifications_unsubscribe__token__reason_post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/notifications/unsubscribe/{token}":{"get":{"tags":["notifications","notifications"],"summary":"Unsubscribe From Emails","description":"One-click unsubscribe from email notifications using a token.\nThis endpoint doesn't require authentication for user convenience.\nReturns an HTML page for better user experience.","operationId":"unsubscribe_from_emails_notifications_unsubscribe__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["notifications","notifications"],"summary":"Unsubscribe From Emails One Click","description":"RFC 8058 one-click unsubscribe target (List-Unsubscribe-Post=One-Click).\nGmail/Yahoo POST to the List-Unsubscribe URL directly; they expect a 2xx\nand no interactive page. Mirrors the GET handler's opt-out logic.","operationId":"unsubscribe_from_emails_one_click_notifications_unsubscribe__token__post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/conversations/{user_id}":{"get":{"tags":["chat","chat"],"summary":"Get Conversations","description":"Get conversations for a user with pagination support.\n\nReturns an envelope `{conversations, next_cursor, has_more}` when a cursor\nis supplied; for backwards compatibility, returns a bare list when only\n`offset` is used (existing clients pre-cursor support).","operationId":"get_conversations_chat_conversations__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"description":"Number of conversations to return","default":50,"title":"Limit"},"description":"Number of conversations to return"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","description":"Number of conversations to skip (legacy — prefer cursor)","default":0,"title":"Offset"},"description":"Number of conversations to skip (legacy — prefer cursor)"},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Opaque cursor returned as `next_cursor` from a previous page. Format: '<iso_timestamp>|<conversation_id>'. When supplied, the server returns conversations strictly before this point, ordered by last-activity desc. Beats offset for users with thousands of conversations — no row re-scan.","title":"Cursor"},"description":"Opaque cursor returned as `next_cursor` from a previous page. Format: '<iso_timestamp>|<conversation_id>'. When supplied, the server returns conversations strictly before this point, ordered by last-activity desc. Beats offset for users with thousands of conversations — no row re-scan."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/favorites":{"get":{"tags":["chat","chat"],"summary":"List Chat Favorites","description":"Return the current user's favorited users (id only — frontend already\nhas user profiles from /users/discover and the conversation list).","operationId":"list_chat_favorites_chat_favorites_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/favorites/chatlist":{"get":{"tags":["chat","chat"],"summary":"List Chat Favorites Chatlist","description":"Favorites tab, assembled server-side in a few batched queries instead of\na client-side linear scan over the whole conversation list.\n\nOld path: the client loaded a page of conversations + friends and filtered\nthem by a Set of favorite ids — so favorites outside the loaded page never\nshowed, and the full unified list was rebuilt/sorted on the client. This\nreturns exactly the favorited users, each enriched with their 1:1 last\nmessage + unread count, ordered by recent activity. Cost is O(#favorites)\nwith indexed joins (mirrors get_conversations' batched last-message/unread\npattern), not O(all conversations).","operationId":"list_chat_favorites_chatlist_chat_favorites_chatlist_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/favorites/{favorited_user_id}":{"post":{"tags":["chat","chat"],"summary":"Add Chat Favorite","description":"Favorite another user. Idempotent — no error if already favorited.","operationId":"add_chat_favorite_chat_favorites__favorited_user_id__post","parameters":[{"name":"favorited_user_id","in":"path","required":true,"schema":{"type":"string","title":"Favorited User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["chat","chat"],"summary":"Remove Chat Favorite","description":"Remove a favorite. Idempotent — returns favorited:false even if it wasn't favorited.","operationId":"remove_chat_favorite_chat_favorites__favorited_user_id__delete","parameters":[{"name":"favorited_user_id","in":"path","required":true,"schema":{"type":"string","title":"Favorited User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/channel-favorites":{"get":{"tags":["chat","chat"],"summary":"List Channel Favorites","operationId":"list_channel_favorites_chat_channel_favorites_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/channel-favorites/{channel_id}":{"post":{"tags":["chat","chat"],"summary":"Add Channel Favorite","description":"Favorite a channel. Idempotent.","operationId":"add_channel_favorite_chat_channel_favorites__channel_id__post","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["chat","chat"],"summary":"Remove Channel Favorite","description":"Unfavorite a channel. Idempotent.","operationId":"remove_channel_favorite_chat_channel_favorites__channel_id__delete","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/history/{user_id}/{other_user_id}":{"get":{"tags":["chat","chat"],"summary":"Get Chat History","description":"Get chat history between two users from the database.\n\nReturns an envelope `{messages, has_more, oldest_timestamp}` when\n`before_timestamp` is supplied; for backwards compatibility, returns a\nbare list when only `offset` is used.","operationId":"get_chat_history_chat_history__user_id___other_user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"other_user_id","in":"path","required":true,"schema":{"type":"string","title":"Other User Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"description":"Maximum number of messages to return","default":50,"title":"Limit"},"description":"Maximum number of messages to return"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","description":"Number of messages to skip (legacy — prefer before_timestamp)","default":0,"title":"Offset"},"description":"Number of messages to skip (legacy — prefer before_timestamp)"},{"name":"before_timestamp","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 timestamp. When supplied, return messages strictly OLDER than this timestamp. Used for 'load older messages' infinite scroll — caller passes the timestamp of the oldest message it already has. Avoids the LIMIT/OFFSET trap where the page boundary drifts as new messages arrive.","title":"Before Timestamp"},"description":"ISO 8601 timestamp. When supplied, return messages strictly OLDER than this timestamp. Used for 'load older messages' infinite scroll — caller passes the timestamp of the oldest message it already has. Avoids the LIMIT/OFFSET trap where the page boundary drifts as new messages arrive."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/messages-since/{user_id}/{other_user_id}":{"get":{"tags":["chat","chat"],"summary":"Get Messages Since","description":"Return messages between two users that arrived AFTER `since`.\n\nUsed by the frontend after a WebSocket reconnect to backfill messages\nthat may have arrived while the socket was offline. Without this, a\nuser who briefly loses connection sees no messages until they reload —\ninbox and open thread both appear frozen.\n\nThe caller passes the timestamp of the newest message it already has\nlocally. The server returns everything strictly newer, ordered ASC so\nthe client can append directly.","operationId":"get_messages_since_chat_messages_since__user_id___other_user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"other_user_id","in":"path","required":true,"schema":{"type":"string","title":"Other User Id"}},{"name":"since","in":"query","required":true,"schema":{"type":"string","description":"ISO 8601 timestamp; returns messages with timestamp > this","title":"Since"},"description":"ISO 8601 timestamp; returns messages with timestamp > this"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"description":"Safety cap on number of messages returned","default":200,"title":"Limit"},"description":"Safety cap on number of messages returned"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/send":{"post":{"tags":["chat","chat"],"summary":"Send Message","description":"Saves a message to the database and broadcasts it via WebSocket.","operationId":"send_message_chat_send_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/daily-question":{"get":{"tags":["chat","chat"],"summary":"Get Daily Question","description":"Get a conversation starter question in the user's target language.\nUsed for dashboard friend matching icebreakers.","operationId":"get_daily_question_chat_daily_question_get","parameters":[{"name":"target_language","in":"query","required":false,"schema":{"type":"string","description":"Target language for the question","title":"Target Language"},"description":"Target language for the question"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/translate":{"post":{"tags":["chat","chat"],"summary":"Translate Message","operationId":"translate_message_chat_translate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranslationRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/stream":{"post":{"tags":["chat","chat"],"summary":"Stream Chat Response","description":"Handles streaming chat responses from the AI with database persistence.","operationId":"stream_chat_response_chat_stream_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StreamChatRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/message/suggestions":{"post":{"tags":["chat","chat"],"summary":"Get Message Suggestions","description":"Generates conversational suggestions based on a message.","operationId":"get_message_suggestions_chat_message_suggestions_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageSuggestionsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/message/suggestions/sequential":{"post":{"tags":["chat","chat"],"summary":"Get Message Suggestions Sequential","description":"Generates conversational suggestions sequentially (one at a time) instead of all at once.","operationId":"get_message_suggestions_sequential_chat_message_suggestions_sequential_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageSuggestionsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/message/suggestions/bilingual":{"post":{"tags":["chat","chat"],"summary":"Get Bilingual Message Suggestions","description":"Get conversation suggestions in both target and native languages.","operationId":"get_bilingual_message_suggestions_chat_message_suggestions_bilingual_post","parameters":[{"name":"conversation-native-language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation-Native-Language"}},{"name":"conversation-target-language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation-Target-Language"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageSuggestionsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BilingualSuggestionsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/message/translate":{"post":{"tags":["chat","chat"],"summary":"Translate Message Endpoint","description":"Translates a message to the user's native language.","operationId":"translate_message_endpoint_chat_message_translate_post","parameters":[{"name":"conversation-native-language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation-Native-Language"}},{"name":"conversation-target-language","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation-Target-Language"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageTranslateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranslationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/messages/{message_id}/call-session":{"get":{"tags":["chat","chat"],"summary":"Get Message Call Session","description":"Find the call session associated with a message, especially for older messages without embedded metadata.","operationId":"get_message_call_session_chat_messages__message_id__call_session_get","parameters":[{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}},{"name":"requesting_user_id","in":"query","required":true,"schema":{"type":"string","description":"ID of the user requesting the call session","title":"Requesting User Id"},"description":"ID of the user requesting the call session"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/detect-language":{"post":{"tags":["chat","chat"],"summary":"Detect Language","description":"Detect the language of a given text.","operationId":"detect_language_chat_detect_language_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LanguageDetectionRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Detect Language Chat Detect Language Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/conversations-with-users/{user_id}":{"get":{"tags":["chat","chat"],"summary":"Get Conversations With Users","description":"Get all conversations for a user with full user details included.\nOptimized with eager loading to avoid N+1 queries.\n\n`unread_only=true` (used by NotificationBell) pre-filters to just the\nconversations with unread messages BEFORE the expensive per-conversation\nenrichment. For a heavy inbox (1000+ conversations) loading the whole list\njust to count notifications was ~2.5s; unread-only is typically a handful.","operationId":"get_conversations_with_users_chat_conversations_with_users__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"unread_only","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Unread Only"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/conversations/{conversation_id}/mark-read":{"put":{"tags":["chat","chat"],"summary":"Mark Conversation As Read","description":"Mark all messages in a conversation as read for the current user.","operationId":"mark_conversation_as_read_chat_conversations__conversation_id__mark_read_put","parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/mark-read/{other_user_id}":{"put":{"tags":["chat","chat"],"summary":"Mark Messages As Read By Users","description":"Mark all messages as read in conversations between current user and other user.\nThis endpoint finds the conversation(s) by user IDs rather than requiring a conversation ID.","operationId":"mark_messages_as_read_by_users_chat_mark_read__other_user_id__put","parameters":[{"name":"other_user_id","in":"path","required":true,"schema":{"type":"string","title":"Other User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/debug-unread/{username}":{"get":{"tags":["chat","chat"],"summary":"Debug Unread Count","description":"Debug endpoint to check unread message count (admin-only).\n\nPreviously unauthenticated — leaked any user's unread count by username.\nNow gated to admins only.","operationId":"debug_unread_count_chat_debug_unread__username__get","parameters":[{"name":"username","in":"path","required":true,"schema":{"type":"string","title":"Username"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/message":{"post":{"tags":["chat","chat"],"summary":"Chat Message Preview","description":"Chat endpoint for preview/onboarding users (anonymous users).\nWorks with anonymous user IDs created during onboarding.\nDoes NOT require authentication but does require a valid user_id.","operationId":"chat_message_preview_chat_message_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatPreviewRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/messages/{message_id}/reactions":{"post":{"tags":["chat","chat"],"summary":"Toggle Reaction","description":"Toggle an emoji reaction on a message.\n\n🔒 Had no conversation/channel-membership check at all, and trusted a\nclient-body `user_id` outright — any logged-in caller could react to (or\nsilently un-react) any message anywhere, forging another user's reaction\nby naming their id. Authorization is on the CALLER (the token), never on\nthe client-supplied user_id — same pattern as every other endpoint in\nthis file/session that accepts a body-supplied identity.","operationId":"toggle_reaction_chat_messages__message_id__reactions_post","parameters":[{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToggleReactionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/messages/{message_id}":{"put":{"tags":["chat","chat"],"summary":"Edit Message","description":"Edit a message within the 15-minute edit window.","operationId":"edit_message_chat_messages__message_id__put","parameters":[{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditMessageRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chat/wod-conversation-starters":{"post":{"tags":["chat","chat"],"summary":"Get Wod Conversation Starters","description":"Generate 3 bilingual conversation starters for WoD-matched users.\nUses Gemini to create contextual starters based on both users' WoD answers.","operationId":"get_wod_conversation_starters_chat_wod_conversation_starters_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/video/call/initiate":{"post":{"tags":["video","video"],"summary":"Initiate Call","description":"Initiate a video/audio call between users.\n\nChecks:\n- User authentication (must be logged in)\n- Call minutes quota (unless admin/premium)\n- Caller identity verification (can't initiate as someone else)\n- Relationship validation: Users must be friends OR have an accepted tutor-student relationship OR have a booking today\n- Admins can call anyone (bypass relationship check)","operationId":"initiate_call_video_call_initiate_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallInitiateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/video/call/respond":{"post":{"tags":["video","video"],"summary":"Respond To Call","description":"Accept or decline a call","operationId":"respond_to_call_video_call_respond_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallResponseRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/video/call/cancel":{"post":{"tags":["video","video"],"summary":"Cancel Call","description":"Cancel an outgoing call","operationId":"cancel_call_video_call_cancel_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallCancelRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/video/call/end/{call_id}":{"post":{"tags":["video","video"],"summary":"End Call","description":"End an active call\n\n🔒 Had auth but no caller/callee check, unlike initiate_call/respond_to_call\nin this same file — any logged-in user who knew a call_id could force-end\nsomeone else's call (and trigger its token-finalization side effects).","operationId":"end_call_video_call_end__call_id__post","parameters":[{"name":"call_id","in":"path","required":true,"schema":{"type":"string","title":"Call Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/video/register-instant-match":{"post":{"tags":["video","video"],"summary":"Register Instant Match Call","description":"Register an instant match call for proper end call notifications\n\n🔒 Had zero auth dependency — anyone could overwrite ANY call_id's active-\ncall registry entry (caller/callee identities, room name, and — since this\nmerges with `existing_data` — could also be used to probe/tamper with an\nin-flight call's token-economy fields). Now requires the caller to be one\nof the two parties they're naming.","operationId":"register_instant_match_call_video_register_instant_match_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstantMatchRegistrationRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/video/token":{"post":{"tags":["video","video"],"summary":"Get Video Token","description":"Generate JWT token for LiveKit video call access","operationId":"get_video_token_video_token_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/video/room/create":{"post":{"tags":["video","video"],"summary":"Create Instant Room","description":"Create an instant room — not tied to a booking or chatroom.\nReturns a room name, token, and shareable link.","operationId":"create_instant_room_video_room_create_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/video/room/{room_name}/join":{"post":{"tags":["video","video"],"summary":"Join Instant Room","description":"Join an existing room by name and get a LiveKit token. If the room belongs\nto a confirmed tutor booking, the booking's `language_for_practice` is also\nreturned so the call's transcription language is pre-set correctly.","operationId":"join_instant_room_video_room__room_name__join_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/video/call/{call_id}/status":{"get":{"tags":["video","video"],"summary":"Get Call Status","description":"Get current status of a call\n\n🔒 Had auth but no caller/callee check — any logged-in user who knew a\ncall_id could read another pair's call status (identities, room name,\nlanguage settings). Same fix as end_call.","operationId":"get_call_status_video_call__call_id__status_get","parameters":[{"name":"call_id","in":"path","required":true,"schema":{"type":"string","title":"Call Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/video/calls/active":{"get":{"tags":["video","video"],"summary":"Get Active Calls","description":"Get all active calls (admin/debugging). Reads the DB — the old module\ndict was a deprecated Redis-migration leftover that is near-always empty\n(and per-pod). Also gated: this endpoint previously required NO auth.","operationId":"get_active_calls_video_calls_active_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/video/tokens/balance":{"get":{"tags":["video","video"],"summary":"Get Token Balance","description":"Get current user's token balance and statistics","operationId":"get_token_balance_video_tokens_balance_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/video/tokens/calculate-cost/{duration_minutes}":{"get":{"tags":["video","video"],"summary":"Calculate Call Cost","description":"Calculate token cost for a call of given duration","operationId":"calculate_call_cost_video_tokens_calculate_cost__duration_minutes__get","parameters":[{"name":"duration_minutes","in":"path","required":true,"schema":{"type":"integer","title":"Duration Minutes"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/update-transcription-language":{"post":{"tags":["voice","voice"],"summary":"Update Transcription Language","description":"Update user's preferred transcription language","operationId":"update_transcription_language_voice_update_transcription_language_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptionLanguageRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/transcription-language/{user_id}":{"get":{"tags":["voice","voice"],"summary":"Get Transcription Language","description":"Get user's current transcription language preference","operationId":"get_transcription_language_voice_transcription_language__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/call/initiate":{"post":{"tags":["voice","voice"],"summary":"Initiate Voice Call","description":"Initiate a voice-only call","operationId":"initiate_voice_call_voice_call_initiate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceCallRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/transcribe-base64":{"post":{"tags":["voice","voice"],"summary":"Transcribe Audio","description":"Transcribe audio to text","operationId":"transcribe_audio_voice_transcribe_base64_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioProcessingRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/translate-speech":{"post":{"tags":["voice","voice"],"summary":"Translate Speech","description":"Transcribe and translate audio in one step","operationId":"translate_speech_voice_translate_speech_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioProcessingRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/call/{call_id}/transcription":{"get":{"tags":["voice","voice"],"summary":"Get Call Transcription","description":"Get transcription history for a voice call","operationId":"get_call_transcription_voice_call__call_id__transcription_get","parameters":[{"name":"call_id","in":"path","required":true,"schema":{"type":"string","title":"Call Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/settings/{user_id}":{"post":{"tags":["voice","voice"],"summary":"Update Voice Settings","description":"Update user's voice and audio settings","operationId":"update_voice_settings_voice_settings__user_id__post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Settings"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["voice","voice"],"summary":"Get Voice Settings","description":"Get user's voice and audio settings","operationId":"get_voice_settings_voice_settings__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/ai-voice/start-session":{"post":{"tags":["voice","voice"],"summary":"Start Ai Voice Session","description":"Start a new AI voice calling session","operationId":"start_ai_voice_session_voice_ai_voice_start_session_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_start_ai_voice_session_voice_ai_voice_start_session_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/ai-voice/end-session":{"post":{"tags":["voice","voice"],"summary":"End Ai Voice Session","description":"End an AI voice calling session","operationId":"end_ai_voice_session_voice_ai_voice_end_session_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_end_ai_voice_session_voice_ai_voice_end_session_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/ai-voice/process-message":{"post":{"tags":["voice","voice"],"summary":"Process Ai Voice Message","description":"Process a voice message through the AI pipeline","operationId":"process_ai_voice_message_voice_ai_voice_process_message_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_process_ai_voice_message_voice_ai_voice_process_message_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/ai-voice/get-response-audio/{session_id}/{filename}":{"get":{"tags":["voice","voice"],"summary":"Get Ai Response Audio","description":"Get AI response audio file","operationId":"get_ai_response_audio_voice_ai_voice_get_response_audio__session_id___filename__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"filename","in":"path","required":true,"schema":{"type":"string","title":"Filename"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/ai-voice/session/{session_id}/history":{"get":{"tags":["voice","voice"],"summary":"Get Ai Voice Session History","description":"Get conversation history for an AI voice session","operationId":"get_ai_voice_session_history_voice_ai_voice_session__session_id__history_get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/transcribe":{"post":{"tags":["voice","voice"],"summary":"Transcribe Audio File","description":"Transcribe audio file to text - for voice notes in chat","operationId":"transcribe_audio_file_voice_transcribe_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_transcribe_audio_file_voice_transcribe_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/upload":{"post":{"tags":["voice","voice"],"summary":"Upload Voice Note","description":"Upload and process voice note message","operationId":"upload_voice_note_voice_upload_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_voice_note_voice_upload_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/live-transcribe":{"post":{"tags":["voice","voice"],"summary":"Live Transcribe Audio","description":"Live transcription endpoint with option for enhanced processing.","operationId":"live_transcribe_audio_voice_live_transcribe_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_live_transcribe_audio_voice_live_transcribe_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/live-translate":{"get":{"tags":["voice","voice"],"summary":"Live Translate Text Get","description":"GET variant of live-translate for frontend query-param calls","operationId":"live_translate_text_get_voice_live_translate_get","parameters":[{"name":"text","in":"query","required":false,"schema":{"type":"string","default":"","title":"Text"}},{"name":"source_language","in":"query","required":false,"schema":{"type":"string","default":"","title":"Source Language"}},{"name":"target_language","in":"query","required":false,"schema":{"type":"string","default":"","title":"Target Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["voice","voice"],"summary":"Live Translate Text","description":"Translate text for live transcription with rate limiting and caching","operationId":"live_translate_text_voice_live_translate_post","requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_live_translate_text_voice_live_translate_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/voice/download/{voice_note_id}":{"get":{"tags":["voice","voice"],"summary":"Download Voice Note","description":"Download voice note file by ID (from GCS or local storage)","operationId":"download_voice_note_voice_download__voice_note_id__get","parameters":[{"name":"voice_note_id","in":"path","required":true,"schema":{"type":"string","title":"Voice Note Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/realtime/create-session":{"post":{"tags":["realtime-transcription","realtime-transcription"],"summary":"Create Realtime Session","description":"Create a new OpenAI Realtime transcription session","operationId":"create_realtime_session_realtime_create_session_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_create_realtime_session_realtime_create_session_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/realtime/process-audio":{"post":{"tags":["realtime-transcription","realtime-transcription"],"summary":"Process Realtime Audio","description":"Process audio frame from LiveKit","operationId":"process_realtime_audio_realtime_process_audio_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_process_realtime_audio_realtime_process_audio_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/realtime/flush-audio":{"post":{"tags":["realtime-transcription","realtime-transcription"],"summary":"Flush Audio","description":"Flush audio buffer and commit to Realtime API","operationId":"flush_audio_realtime_flush_audio_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_flush_audio_realtime_flush_audio_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/realtime/destroy-session":{"post":{"tags":["realtime-transcription","realtime-transcription"],"summary":"Destroy Realtime Session","description":"Destroy OpenAI Realtime session","operationId":"destroy_realtime_session_realtime_destroy_session_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_destroy_realtime_session_realtime_destroy_session_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/realtime/update-language":{"post":{"tags":["realtime-transcription","realtime-transcription"],"summary":"Update Realtime Language","description":"Update session language","operationId":"update_realtime_language_realtime_update_language_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_update_realtime_language_realtime_update_language_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/realtime/session-info/{session_id}":{"get":{"tags":["realtime-transcription","realtime-transcription"],"summary":"Get Session Info","description":"Get information about a Realtime session","operationId":"get_session_info_realtime_session_info__session_id__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/deepgram/create-session":{"post":{"tags":["deepgram-transcription","deepgram-transcription"],"summary":"Create Deepgram Session","description":"Create a new Deepgram transcription session","operationId":"create_deepgram_session_deepgram_create_session_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_create_deepgram_session_deepgram_create_session_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/deepgram/process-audio":{"post":{"tags":["deepgram-transcription","deepgram-transcription"],"summary":"Process Deepgram Audio","description":"Process audio frame from LiveKit","operationId":"process_deepgram_audio_deepgram_process_audio_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_process_deepgram_audio_deepgram_process_audio_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/deepgram/flush-audio":{"post":{"tags":["deepgram-transcription","deepgram-transcription"],"summary":"Flush Audio","description":"Flush audio buffer and finalize transcription","operationId":"flush_audio_deepgram_flush_audio_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_flush_audio_deepgram_flush_audio_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/deepgram/destroy-session":{"post":{"tags":["deepgram-transcription","deepgram-transcription"],"summary":"Destroy Deepgram Session","description":"Destroy Deepgram session","operationId":"destroy_deepgram_session_deepgram_destroy_session_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_destroy_deepgram_session_deepgram_destroy_session_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/deepgram/update-language":{"post":{"tags":["deepgram-transcription","deepgram-transcription"],"summary":"Update Deepgram Language","description":"Update session language","operationId":"update_deepgram_language_deepgram_update_language_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_update_deepgram_language_deepgram_update_language_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/deepgram/session-info/{session_id}":{"get":{"tags":["deepgram-transcription","deepgram-transcription"],"summary":"Get Session Info","description":"Get information about a Deepgram session","operationId":"get_session_info_deepgram_session_info__session_id__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ws/deepgram/active-streams":{"get":{"tags":["deepgram-websocket","deepgram-websocket"],"summary":"Get Active Streams","description":"Get information about active audio streams (from Redis) and the Deepgram\nconnection tracker (registered slots vs cap — includes any leaked slots, so\ntracker.active >> local_sessions means the slot leak is accumulating).","operationId":"get_active_streams_ws_deepgram_active_streams_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/ws/deepgram/admin/restart-room-transcription/{room_name}":{"post":{"tags":["deepgram-websocket","deepgram-websocket"],"summary":"Restart Room Transcription","description":"Admin: force every transcription client in a room to rebuild its pipeline.\n\nCloses the app↔backend stream WebSocket for each session in the room; each\nclient's health watchdog sees the closed socket and rebuilds (reconnect +\nfresh audio-processor tap). This is the targeted 'toggle' that did not exist\nduring the 2026-07-15 class, when the only lever was a full pod restart that\nstranded arriving students. Deepgram-side sessions are destroyed too so the\nrebuilt clients start clean rather than evicting themselves.","operationId":"restart_room_transcription_ws_deepgram_admin_restart_room_transcription__room_name__post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/health":{"get":{"tags":["learning","learning"],"summary":"Health Check","description":"Health check for learning analytics service","operationId":"health_check_learning_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/learning/flashcards/decks/{user_id}":{"get":{"tags":["learning","learning"],"summary":"Get User Flashcard Decks","description":"LEGACY (see block comment above) — per-pod in-memory decks.","operationId":"get_user_flashcard_decks_learning_flashcards_decks__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/flashcards/deck/{user_id}/{deck_id}":{"get":{"tags":["learning","learning"],"summary":"Get Flashcard Deck","description":"Get a specific flashcard deck by ID","operationId":"get_flashcard_deck_learning_flashcards_deck__user_id___deck_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"deck_id","in":"path","required":true,"schema":{"type":"string","title":"Deck Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["learning","learning"],"summary":"Delete Flashcard Deck","description":"Delete a flashcard deck","operationId":"delete_flashcard_deck_learning_flashcards_deck__user_id___deck_id__delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"deck_id","in":"path","required":true,"schema":{"type":"string","title":"Deck Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/flashcards/deck/{user_id}/{deck_id}/rename":{"put":{"tags":["learning","learning"],"summary":"Rename Flashcard Deck","description":"Rename a flashcard deck","operationId":"rename_flashcard_deck_learning_flashcards_deck__user_id___deck_id__rename_put","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"deck_id","in":"path","required":true,"schema":{"type":"string","title":"Deck Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_rename_flashcard_deck_learning_flashcards_deck__user_id___deck_id__rename_put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/flashcards/generate":{"post":{"tags":["learning","learning"],"summary":"Generate Flashcards","description":"Generate a new set of flashcards for a user based on their conversations.","operationId":"generate_flashcards_learning_flashcards_generate_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/flashcards/{user_id}":{"get":{"tags":["learning","learning"],"summary":"Get User Flashcards","description":"Get all flashcard sets for a user","operationId":"get_user_flashcards_learning_flashcards__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/feedback":{"post":{"tags":["learning","learning"],"summary":"Submit Learning Feedback","description":"Submit learning feedback for analytics","operationId":"submit_learning_feedback_learning_feedback_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/feedback/generate":{"post":{"tags":["learning","learning"],"summary":"Generate Learning Feedback","description":"Generate comprehensive learning feedback for a user.\n\nFor students: generates feedback for their own learning.\nFor Pro Tutors: can generate feedback for their students (filtered to interactions between tutor and student).","operationId":"generate_learning_feedback_learning_feedback_generate_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/task/{task_id}/status":{"get":{"tags":["learning","learning"],"summary":"Get Task Status","description":"Get the status of a background task","operationId":"get_task_status_learning_task__task_id__status_get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/tasks/user/{user_id}/completed":{"get":{"tags":["learning","learning"],"summary":"Get User Completed Tasks","description":"Get all completed tasks for a user","operationId":"get_user_completed_tasks_learning_tasks_user__user_id__completed_get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/vocabulary/{user_id}":{"get":{"tags":["learning","learning"],"summary":"Get Vocabulary Analytics","description":"(Partially Implemented) Get vocabulary analytics for a user","operationId":"get_vocabulary_analytics_learning_vocabulary__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"target_language","in":"query","required":false,"schema":{"type":"string","title":"Target Language"}},{"name":"time_period_days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Time Period Days"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/vocabulary/estimate":{"post":{"tags":["learning","learning"],"summary":"Estimate Vocabulary Size","description":"Estimate the user's vocabulary size based on their conversation history.","operationId":"estimate_vocabulary_size_learning_vocabulary_estimate_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/flashcards/review":{"post":{"tags":["learning","learning"],"summary":"Review Flashcard","description":"LEGACY (see block above /flashcards/decks) — writes into a throwaway\nFlashcardService() instance, so the review is discarded; the live path is\nPOST /learning/enhanced-flashcards/review (DB-backed).","operationId":"review_flashcard_learning_flashcards_review_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/solo-recordings/save":{"post":{"tags":["learning","learning"],"summary":"Save Solo Recording","description":"Save a solo recording with transcription and analysis","operationId":"save_solo_recording_learning_solo_recordings_save_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SoloRecordingSaveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/solo-recordings/{user_id}":{"get":{"tags":["learning","learning"],"summary":"Get Solo Recordings","description":"Get all solo recordings for a user","operationId":"get_solo_recordings_learning_solo_recordings__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/solo-recordings/{recording_id}/analyze":{"get":{"tags":["learning","learning"],"summary":"Analyze Solo Recording","description":"Analyze a specific solo recording","operationId":"analyze_solo_recording_learning_solo_recordings__recording_id__analyze_get","parameters":[{"name":"recording_id","in":"path","required":true,"schema":{"type":"string","title":"Recording Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/grammar/{user_id}":{"get":{"tags":["learning","learning"],"summary":"Get Grammar Analytics","description":"Get comprehensive grammar analysis for a user","operationId":"get_grammar_analytics_learning_grammar__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"target_language","in":"query","required":false,"schema":{"type":"string","title":"Target Language"}},{"name":"time_period_days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Time Period Days"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/fluency/{user_id}":{"get":{"tags":["learning","learning"],"summary":"Get Fluency Analytics","description":"Get comprehensive fluency analysis for a user","operationId":"get_fluency_analytics_learning_fluency__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"target_language","in":"query","required":false,"schema":{"type":"string","title":"Target Language"}},{"name":"time_period_days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Time Period Days"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/conversation-quality-analysis/{user_id}":{"post":{"tags":["learning","learning"],"summary":"Analyze Conversation Quality","description":"Analyze conversation quality and provide detailed metrics and feedback\n\nArgs:\n    user_id: ID of the user to analyze\n    target_language: Language being learned (optional, will use user's default)\n    partner_id: Specific conversation partner to analyze (optional)\n    time_period_hours: How many hours back to analyze (default 24)\n    \nReturns:\n    Comprehensive conversation quality metrics and feedback","operationId":"analyze_conversation_quality_learning_conversation_quality_analysis__user_id__post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_analyze_conversation_quality_learning_conversation_quality_analysis__user_id__post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/progress-tracking/store-snapshot/{user_id}":{"post":{"tags":["learning","learning"],"summary":"Store Progress Snapshot","description":"Store a progress snapshot after analyzing conversation quality\n\nArgs:\n    user_id: ID of the user\n    target_language: Language being learned (optional, will use user's default)\n    snapshot_type: Type of snapshot ('daily', 'weekly', 'monthly', 'on_demand')\n    analysis_period_hours: How many hours back to analyze (default 24)\n    \nReturns:\n    Progress snapshot information and trend analysis","operationId":"store_progress_snapshot_learning_progress_tracking_store_snapshot__user_id__post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_store_progress_snapshot_learning_progress_tracking_store_snapshot__user_id__post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/progress-tracking/history/{user_id}":{"get":{"tags":["learning","learning"],"summary":"Get Progress History","description":"Get progress history for a user\n\nArgs:\n    user_id: ID of the user\n    target_language: Language being learned (optional, will use user's default)\n    days_back: How many days of history to retrieve (default 30)\n    \nReturns:\n    Historical progress data and trends","operationId":"get_progress_history_learning_progress_tracking_history__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"target_language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Language"}},{"name":"days_back","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days Back"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/progress-tracking/trends/{user_id}":{"get":{"tags":["learning","learning"],"summary":"Get Learning Trends","description":"Get detailed learning trends analysis\n\nArgs:\n    user_id: ID of the user\n    target_language: Language being learned (optional, will use user's default)\n    days_back: How many days of trends to analyze (default 30)\n    \nReturns:\n    Detailed learning trends and analysis","operationId":"get_learning_trends_learning_progress_tracking_trends__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"target_language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Language"}},{"name":"days_back","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days Back"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/debug-vocabulary":{"post":{"tags":["learning","learning"],"summary":"Debug Vocabulary Analysis","description":"Debug endpoint to test vocabulary analysis with logging","operationId":"debug_vocabulary_analysis_learning_debug_vocabulary_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_debug_vocabulary_analysis_learning_debug_vocabulary_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/topics":{"get":{"tags":["lessons","lessons"],"summary":"Get Lesson Topics","description":"Get all available lesson topics with optional filters.\nReturns topics with passage count.","operationId":"get_lesson_topics_lessons_topics_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by category","title":"Category"},"description":"Filter by category"},{"name":"difficulty_level","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by CEFR level (A1-C2)","title":"Difficulty Level"},"description":"Filter by CEFR level (A1-C2)"},{"name":"target_language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by target language","title":"Target Language"},"description":"Filter by target language"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/LessonTopicResponse"},"title":"Response Get Lesson Topics Lessons Topics Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/by-grammar":{"get":{"tags":["lessons","lessons"],"summary":"Find Lesson By Grammar","description":"Find a lesson that teaches a given grammar concept.\n\nMatches the concept against LessonPassage.key_grammar[].point. The two naming\nsources are NOT normalized — lesson points are verbose/free-form (\"Simple\nPresent Tense for facts and routines\"), sometimes multilingual dicts, while\nthe progress breakdown uses a different, terser inventory (\"Present Simple\").\nSo we don't substring-match; we score TOKEN OVERLAP after normalizing both\n(lowercase, strip parentheticals, drop stopwords + over-generic grammar words\nlike \"tense\"/\"verb\"). Returns the passage id to open at /lessons/{id}.","operationId":"find_lesson_by_grammar_lessons_by_grammar_get","parameters":[{"name":"concept","in":"query","required":true,"schema":{"type":"string","description":"Grammar concept/pattern name to match","title":"Concept"},"description":"Grammar concept/pattern name to match"},{"name":"language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Target language to filter by","title":"Language"},"description":"Target language to filter by"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/passages":{"get":{"tags":["lessons","lessons"],"summary":"Get Lesson Passages","description":"Get all available lesson passages with optional filters.\nIncludes topic information and user's progress status if any.","operationId":"get_lesson_passages_lessons_passages_get","parameters":[{"name":"topic_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Filter by topic ID","title":"Topic Id"},"description":"Filter by topic ID"},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by category","title":"Category"},"description":"Filter by category"},{"name":"difficulty_level","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by CEFR level (A1-C2)","title":"Difficulty Level"},"description":"Filter by CEFR level (A1-C2)"},{"name":"target_language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by target language","title":"Target Language"},"description":"Filter by target language"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/LessonPassageResponse"},"title":"Response Get Lesson Passages Lessons Passages Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/passages/preview":{"get":{"tags":["lessons","lessons"],"summary":"Get Preview Passage","description":"Get a preview lesson passage without authentication.\nReturns one passage matching the specified language and difficulty level.","operationId":"get_preview_passage_lessons_passages_preview_get","parameters":[{"name":"target_language","in":"query","required":true,"schema":{"type":"string","description":"Target language for the lesson","title":"Target Language"},"description":"Target language for the lesson"},{"name":"difficulty_level","in":"query","required":false,"schema":{"type":"string","description":"CEFR level (A1-C2)","default":"A1","title":"Difficulty Level"},"description":"CEFR level (A1-C2)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/passages/{passage_id}":{"get":{"tags":["lessons","lessons"],"summary":"Get Lesson Passage","description":"Get a specific lesson passage by ID.\nIncludes topic information and user's progress status if any.","operationId":"get_lesson_passage_lessons_passages__passage_id__get","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LessonPassageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/passages/{passage_id}/sections":{"get":{"tags":["lessons","lessons"],"summary":"Get Lesson Passage Sections","description":"Get sections for a lesson passage.\nIf the passage needs sections but doesn't have them, they will be created automatically.","operationId":"get_lesson_passage_sections_lessons_passages__passage_id__sections_get","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/progress":{"post":{"tags":["lessons","lessons"],"summary":"Create Lesson Progress","description":"Create a new lesson progress record when a user starts a lesson.","operationId":"create_lesson_progress_lessons_progress_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LessonProgressCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LessonProgressResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["lessons","lessons"],"summary":"Get User Lesson Progress","description":"Get current user's lesson progress history with optional filters.","operationId":"get_user_lesson_progress_lessons_progress_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status","title":"Status"},"description":"Filter by status"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Number of records to return","default":50,"title":"Limit"},"description":"Number of records to return"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Number of records to skip","default":0,"title":"Offset"},"description":"Number of records to skip"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/LessonProgressResponse"},"title":"Response Get User Lesson Progress Lessons Progress Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/progress/{progress_id}":{"put":{"tags":["lessons","lessons"],"summary":"Update Lesson Progress","description":"Update an existing lesson progress record.\nUsed to track stage completions and timing.","operationId":"update_lesson_progress_lessons_progress__progress_id__put","parameters":[{"name":"progress_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Progress Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LessonProgressUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LessonProgressResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/match-partners":{"get":{"tags":["lessons","lessons"],"summary":"Find Lesson Partners","description":"Find potential practice partners who have completed the same lesson.\nMatches based on:\n- Same passage/topic\n- Similar CEFR level (±1 level)\n- Complementary languages (they speak user's target language, or vice versa)\n- Recently completed the lesson (within last 7 days)","operationId":"find_lesson_partners_lessons_match_partners_get","parameters":[{"name":"passage_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","description":"Passage ID to find partners for","title":"Passage Id"},"description":"Passage ID to find partners for"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"description":"Maximum number of partners to return","default":10,"title":"Limit"},"description":"Maximum number of partners to return"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PartnerMatchResponse"},"title":"Response Find Lesson Partners Lessons Match Partners Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/passages/reference-audio-preview/{passage_id}":{"get":{"tags":["lessons","lessons"],"summary":"Get Lesson Passage Reference Audio Preview","description":"Get pre-generated reference audio for a lesson passage (Chacha's voice) - PREVIEW MODE (no auth required).\nThis is used in the onboarding flow where users aren't logged in yet.","operationId":"get_lesson_passage_reference_audio_preview_lessons_passages_reference_audio_preview__passage_id__get","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","title":"Passage Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"head":{"tags":["lessons","lessons"],"summary":"Get Lesson Passage Reference Audio Preview","description":"Get pre-generated reference audio for a lesson passage (Chacha's voice) - PREVIEW MODE (no auth required).\nThis is used in the onboarding flow where users aren't logged in yet.","operationId":"get_lesson_passage_reference_audio_preview_lessons_passages_reference_audio_preview__passage_id__get","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","title":"Passage Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/passages/reference-audio/{passage_id}":{"get":{"tags":["lessons","lessons"],"summary":"Get Lesson Passage Reference Audio","description":"Get pre-generated reference audio for a lesson passage (Chacha's voice).\nThis audio is pre-generated using Google Cloud TTS Chirp 3 HD.","operationId":"get_lesson_passage_reference_audio_lessons_passages_reference_audio__passage_id__get","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"head":{"tags":["lessons","lessons"],"summary":"Get Lesson Passage Reference Audio","description":"Get pre-generated reference audio for a lesson passage (Chacha's voice).\nThis audio is pre-generated using Google Cloud TTS Chirp 3 HD.","operationId":"get_lesson_passage_reference_audio_lessons_passages_reference_audio__passage_id__get","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/passages/{passage_id}/recommended-partner":{"get":{"tags":["lessons","lessons"],"summary":"Get Recommended Practice Partner","description":"Get a recommended practice partner for a specific lesson passage.\nPrioritizes users who completed the same topic (cross-language matching),\nthen falls back to general language-based matching.","operationId":"get_recommended_practice_partner_lessons_passages__passage_id__recommended_partner_get","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/initiate-practice-chat":{"post":{"tags":["lessons","lessons"],"summary":"Initiate Practice Chat","description":"Initiate a practice chat between the current user and a partner after a lesson.\nCreates or finds an existing conversation and sends a ChaCha intro message.","operationId":"initiate_practice_chat_lessons_initiate_practice_chat_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/my-path":{"get":{"tags":["lessons","lessons"],"summary":"Get My Lesson Path","description":"Return the user's personalized lesson path with progressive unlock\nand content gating for free-tier users.","operationId":"get_my_lesson_path_lessons_my_path_get","parameters":[{"name":"language","in":"query","required":false,"schema":{"type":"string","description":"Target language override","title":"Language"},"description":"Target language override"},{"name":"show_all","in":"query","required":false,"schema":{"type":"boolean","description":"Show all levels, not just nearby ones","default":false,"title":"Show All"},"description":"Show all levels, not just nearby ones"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations":{"get":{"tags":["lessons","lessons"],"summary":"Get Situation Lessons","description":"Get available situation lessons with user completion status. Falls back to reading lessons if none exist yet.","operationId":"get_situation_lessons_lessons_situations_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by category (daily_life, travel)","title":"Category"},"description":"Filter by category (daily_life, travel)"},{"name":"difficulty_level","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by CEFR level (A1, B1, C1)","title":"Difficulty Level"},"description":"Filter by CEFR level (A1, B1, C1)"},{"name":"target_language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by target language","title":"Target Language"},"description":"Filter by target language"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/onboarding-pick":{"get":{"tags":["lessons","lessons"],"summary":"Get Onboarding Lesson Pick","description":"Resolve the first/easiest intro lesson for the Test 2 `lesson` variant\nof the `try_solo_activity_type_2026_05` A/B test.\n\nOrdered by `topic.display_order ASC` — the same curated ordering shown to\nusers on the /lessons listing page, so onboarding picks the same \"lesson 1\"\na returning user would see at the top of their list. Falls back to easier\nlevels (never harder) so we never overwhelm an A1 learner.\n\nNo auth required — anonymous onboarding visitors hit this before signing up.","operationId":"get_onboarding_lesson_pick_lessons_situations_onboarding_pick_get","parameters":[{"name":"target_language","in":"query","required":true,"schema":{"type":"string","title":"Target Language"}},{"name":"cefr_level","in":"query","required":false,"schema":{"type":"string","default":"A1","title":"Cefr Level"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/{passage_id}":{"get":{"tags":["lessons","lessons"],"summary":"Get Situation Lesson Detail","description":"Get full situation lesson detail including conversation starters and usage patterns.\n\nAccepts anonymous (or absent) auth: anonymous onboarding users land here via\nthe `try_solo_activity_type_2026_05` A/B test variant.\n\n`native_language` query param overrides the user's stored native_language; the\nonboarding flow passes it explicitly because saveData() is fire-and-forget so\nthe DB value can lag the user's actual selection.","operationId":"get_situation_lesson_detail_lessons_situations__passage_id__get","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"native_language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Native Language"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/{passage_id}/start-practice":{"post":{"tags":["lessons","lessons"],"summary":"Start Situation Practice","description":"Start ChaCha practice for a situation lesson. Creates progress record and returns conversation context.","operationId":"start_situation_practice_lessons_situations__passage_id__start_practice_post","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/{passage_id}/chat":{"post":{"tags":["lessons","lessons"],"summary":"Start Lesson Chat","description":"Start (or resume) a chat-based lesson session.\nReturns the initial message sequence: scene image, intro, vocab cards, grammar tip, action buttons.\nIf `resume_progress_id` is supplied and matches a non-completed record owned by the user,\nthat record is reused instead of creating a new one — used by the resume flow.\n\nRequires a valid user (anonymous tokens are fine, but expired/missing tokens\nreturn 401 so the frontend can prompt re-auth or fresh anonymous-user creation).","operationId":"start_lesson_chat_lessons_situations__passage_id__chat_post","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"resume_progress_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Resume Progress Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/{passage_id}/evaluate-attempt":{"post":{"tags":["lessons","lessons"],"summary":"Evaluate Vocab Attempt","description":"Evaluate a learner's pronunciation or text attempt for a single vocab word during drill phase.","operationId":"evaluate_vocab_attempt_lessons_situations__passage_id__evaluate_attempt_post","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_evaluate_vocab_attempt_lessons_situations__passage_id__evaluate_attempt_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/{passage_id}/generate-hints":{"post":{"tags":["lessons","lessons"],"summary":"Generate Hints","description":"Generate contextual response suggestions based on ChaCha's latest message and remaining vocab.","operationId":"generate_hints_lessons_situations__passage_id__generate_hints_post","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/{passage_id}/chat-message":{"post":{"tags":["lessons","lessons"],"summary":"Lesson Chat Message","description":"Send a message to ChaCha during lesson text practice. Returns ChaCha's response directly.\nDoes NOT save to the regular chat conversation — lesson messages stay in the lesson.","operationId":"lesson_chat_message_lessons_situations__passage_id__chat_message_post","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/{passage_id}/check-usage":{"post":{"tags":["lessons","lessons"],"summary":"Check Vocabulary Usage","description":"Real-time check: given a user message, detect which vocabulary/grammar was used.\nReturns updated usage state and newly matched items.","operationId":"check_vocabulary_usage_lessons_situations__passage_id__check_usage_post","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/{passage_id}/analyze":{"post":{"tags":["lessons","lessons"],"summary":"Analyze Situation Conversation","description":"Post-conversation Gemini analysis of vocabulary/grammar usage.\nCalled when the user ends their ChaCha practice session.","operationId":"analyze_situation_conversation_lessons_situations__passage_id__analyze_post","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/{passage_id}/comprehension":{"post":{"tags":["lessons","lessons"],"summary":"Submit Comprehension Answers","description":"Grade the dialogue comprehension section.\n\nmultiple_choice / true_false are checked deterministically against the\nstored key. fill_blank / short_answer go to ChaCha's LLM in one batched\ncall: mostly-right answers come back \"correct\" with the minor slip\nhighlighted in `correction`. Results are stored on the progress row.","operationId":"submit_comprehension_answers_lessons_situations__passage_id__comprehension_post","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComprehensionSubmission"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/{passage_id}/feedback":{"post":{"tags":["lessons","lessons"],"summary":"Submit Lesson Feedback","description":"Store student feedback on a scenario: a 1-5 rating from the completion\nslide and/or a free comment, optionally tagged with the stage it came from.","operationId":"submit_lesson_feedback_lessons_situations__passage_id__feedback_post","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LessonFeedbackIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/feedback/summary":{"get":{"tags":["lessons","lessons"],"summary":"Lesson Feedback Summary","description":"Admin readout of student scenario feedback: per-scenario aggregates\n(thumbs by stage, completion ratings) plus every comment, newest first.","operationId":"lesson_feedback_summary_lessons_feedback_summary_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/{passage_id}/progress/latest":{"get":{"tags":["lessons","lessons"],"summary":"Get Latest Lesson Progress","description":"Return the latest non-completed progress row for this user+passage so the\nfrontend can offer a 'Resume where you left off?' banner. Returns null if no\nresumable record exists. Anonymous / guest users (no user) get null progress\nrather than a 500 — they can play the lesson without resume tracking.","operationId":"get_latest_lesson_progress_lessons_situations__passage_id__progress_latest_get","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/{passage_id}/progress/{progress_id}":{"patch":{"tags":["lessons","lessons"],"summary":"Checkpoint Lesson Progress","description":"Checkpoint a lesson's current phase + sub-indexes so the user can resume.","operationId":"checkpoint_lesson_progress_lessons_situations__passage_id__progress__progress_id__patch","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"progress_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Progress Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LessonProgressCheckpoint"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/mini-lesson":{"get":{"tags":["lessons","lessons"],"summary":"Get Daily Mini Lesson","description":"Get a mini-lesson for today's daily session based on user's language and level.","operationId":"get_daily_mini_lesson_lessons_mini_lesson_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/{passage_id}/thread":{"get":{"tags":["lesson-threads","lesson-threads"],"summary":"Get Thread","operationId":"get_thread_lessons_situations__passage_id__thread_get","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/{passage_id}/thread/posts":{"post":{"tags":["lesson-threads","lesson-threads"],"summary":"Create Thread Post","operationId":"create_thread_post_lessons_situations__passage_id__thread_posts_post","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePostBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/threads/posts/{post_id}":{"patch":{"tags":["lesson-threads","lesson-threads"],"summary":"Update Thread Post","operationId":"update_thread_post_lessons_threads_posts__post_id__patch","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Post Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePostBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["lesson-threads","lesson-threads"],"summary":"Delete Thread Post","operationId":"delete_thread_post_lessons_threads_posts__post_id__delete","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Post Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/threads/posts/{post_id}/reactions":{"post":{"tags":["lesson-threads","lesson-threads"],"summary":"Add Post Reaction","operationId":"add_post_reaction_lessons_threads_posts__post_id__reactions_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Post Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/threads/posts/{post_id}/comments":{"post":{"tags":["lesson-threads","lesson-threads"],"summary":"Add Post Comment","operationId":"add_post_comment_lessons_threads_posts__post_id__comments_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Post Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/{passage_id}/thread/complete":{"post":{"tags":["lesson-threads","lesson-threads"],"summary":"Complete Lesson With Thread","operationId":"complete_lesson_with_thread_lessons_situations__passage_id__thread_complete_post","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/lessons/situations/{passage_id}/thread/skip":{"post":{"tags":["lesson-threads","lesson-threads"],"summary":"Skip Lesson Thread","operationId":"skip_lesson_thread_lessons_situations__passage_id__thread_skip_post","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/today":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Today Word","description":"Get word of the day with translations for user's target languages.\nIf date_str is provided (YYYY-MM-DD format), fetches word for that date.\nOtherwise, returns today's word.\nReturns the same concept across all languages, but only includes translations\nfor languages the user is learning.","operationId":"get_today_word_api_word_of_day_today_get","parameters":[{"name":"date_str","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date Str"}},{"name":"word_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Word Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/preview":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Preview Word","description":"Get today's word of the day for onboarding preview (no auth required).\nReturns word data for the specified language only.\nOptional 'level' param (beginner/intermediate/advanced) for level-specific words.","operationId":"get_preview_word_api_word_of_day_preview_get","parameters":[{"name":"language","in":"query","required":false,"schema":{"type":"string","default":"English","title":"Language"}},{"name":"level","in":"query","required":false,"schema":{"type":"string","title":"Level"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/access-check/{date_str}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Check Daily Session Access","description":"Check user's access permissions for a specific daily session date.\nReturns access status, attempt counts, and subscription info.\n\nUsed by frontend to:\n- Show/hide premium features\n- Display upgrade prompts at appropriate times\n- Track attempts for free users","operationId":"check_daily_session_access_api_word_of_day_access_check__date_str__get","parameters":[{"name":"date_str","in":"path","required":true,"schema":{"type":"string","title":"Date Str"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/pronounce-preview":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Analyze Pronunciation Preview","description":"Analyze pronunciation for preview/onboarding users (anonymous users).\nSaves attempts to database for anonymous users.\nNo authentication required but user_id should be provided.\n\n🔒 Deliberately unauthenticated (onboarding happens before an account\nexists), but the `user_id` used to attribute a saved attempt had no\nownership check at all: anyone — including an authenticated caller —\ncould pass a REAL registered user's UUID and write a forged pronunciation\nattempt (score, analysis) into their history/leaderboard placement. When\nthe CALLER does present a valid token, the attempt is attributed to that\ntoken's identity, not the client-supplied user_id — closing the \"logged\nin attacker targets a real victim account\" case while leaving the\ngenuinely anonymous onboarding flow (no token at all) unchanged.","operationId":"analyze_pronunciation_preview_api_word_of_day_pronounce_preview_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_analyze_pronunciation_preview_api_word_of_day_pronounce_preview_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/save-preview-attempt":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Save Preview Attempt","description":"Save a pronunciation attempt that was made during preview/onboarding.\nCalled after user signs up to persist their preview attempt.","operationId":"save_preview_attempt_api_word_of_day_save_preview_attempt_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Attempt Data"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/pronounce":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Analyze Pronunciation","description":"Analyze user's pronunciation of a word using Gemini multimodal AI.\nReturns a score (0-100) and detailed feedback.","operationId":"analyze_pronunciation_api_word_of_day_pronounce_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_analyze_pronunciation_api_word_of_day_pronounce_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/stats/{word_id}/{language}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Pronunciation Stats","description":"Get pronunciation statistics for a specific word and language.\nReturns user's best score and percentile ranking.","operationId":"get_pronunciation_stats_api_word_of_day_stats__word_id___language__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/leaderboard/{word_id}/{language}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Leaderboard","description":"Get top 3 users for a specific word and language.\nAlways includes the current user if they have an attempt.\nReturns user profile info and their best score.","operationId":"get_leaderboard_api_word_of_day_leaderboard__word_id___language__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/audio/{word_id}/{language}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Word Audio","description":"Get or generate TTS audio for a word in a specific language.\nUses Google Chirp 3 HD voices with caching to avoid regeneration.\nOptional 'level' param (beginner/intermediate/advanced) for multi-level words.","operationId":"get_word_audio_api_word_of_day_audio__word_id___language__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}},{"name":"level","in":"query","required":false,"schema":{"type":"string","title":"Level"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/quiz-audio/{question_id}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Quiz Audio","description":"Get or generate TTS audio for a quiz question.\nFor sentence-based listening questions, generates audio for the correct answer.\nFor counting questions, extracts sentences from explanation and concatenates them.\nUses Gemini TTS for natural-sounding spoken sentences.","operationId":"get_quiz_audio_api_word_of_day_quiz_audio__question_id__get","parameters":[{"name":"question_id","in":"path","required":true,"schema":{"type":"integer","title":"Question Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/quiz-audio/{question_id}/sentence/{sentence_index}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Quiz Sentence Audio","description":"Get or generate TTS audio for a specific sentence in a counting question.\nReturns just the sentence audio without numeric announcements.","operationId":"get_quiz_sentence_audio_api_word_of_day_quiz_audio__question_id__sentence__sentence_index__get","parameters":[{"name":"question_id","in":"path","required":true,"schema":{"type":"integer","title":"Question Id"}},{"name":"sentence_index","in":"path","required":true,"schema":{"type":"integer","title":"Sentence Index"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/quiz/{word_id}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Quiz Question","description":"Get quiz questions for a word - returns 2 questions: context + listening.","operationId":"get_quiz_question_api_word_of_day_quiz__word_id__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/quiz/{question_id}":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Submit Quiz Answer","description":"Submit quiz answer and get immediate feedback.","operationId":"submit_quiz_answer_api_word_of_day_quiz__question_id__post","parameters":[{"name":"question_id","in":"path","required":true,"schema":{"type":"integer","title":"Question Id"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_submit_quiz_answer_api_word_of_day_quiz__question_id__post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/recording-prompt/{word_id}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Recording Prompt","description":"Get recording prompt for a word.","operationId":"get_recording_prompt_api_word_of_day_recording_prompt__word_id__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/recording-response/{prompt_id}":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Submit Recording Response","description":"Submit audio recording OR text response for a prompt and trigger matching.","operationId":"submit_recording_response_api_word_of_day_recording_response__prompt_id__post","parameters":[{"name":"prompt_id","in":"path","required":true,"schema":{"type":"integer","title":"Prompt Id"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_submit_recording_response_api_word_of_day_recording_response__prompt_id__post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/matches/{word_id}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get User Matches","description":"Get ChaCha-generated matches for the user based on their recording.","operationId":"get_user_matches_api_word_of_day_matches__word_id__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/native-pronunciation/{word_id}":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Submit Native Pronunciation","description":"Submit native speaker pronunciation (auto-rated for quality).","operationId":"submit_native_pronunciation_api_word_of_day_native_pronunciation__word_id__post","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_submit_native_pronunciation_api_word_of_day_native_pronunciation__word_id__post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/native-pronunciation/{submission_id}":{"delete":{"tags":["word-of-day","word_of_day"],"summary":"Delete Native Pronunciation","description":"Delete a user's own native pronunciation submission.","operationId":"delete_native_pronunciation_api_word_of_day_native_pronunciation__submission_id__delete","parameters":[{"name":"submission_id","in":"path","required":true,"schema":{"type":"integer","title":"Submission Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/native-pronunciations/{word_id}/{language}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Native Pronunciations","description":"Browse top native speaker pronunciations.","operationId":"get_native_pronunciations_api_word_of_day_native_pronunciations__word_id___language__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}},{"name":"featured_only","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Featured Only"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/native-audio/{submission_id}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Native Audio","description":"Get audio file for a native pronunciation submission.","operationId":"get_native_audio_api_word_of_day_native_audio__submission_id__get","parameters":[{"name":"submission_id","in":"path","required":true,"schema":{"type":"integer","title":"Submission Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/native-pronunciation/{submission_id}/helpful":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Mark Pronunciation Helpful","description":"Mark a pronunciation as helpful (thumbs up). One vote per user per submission.","operationId":"mark_pronunciation_helpful_api_word_of_day_native_pronunciation__submission_id__helpful_post","parameters":[{"name":"submission_id","in":"path","required":true,"schema":{"type":"integer","title":"Submission Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/slang/{word_id}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Slang Question","description":"Get slang/culture question for a word.","operationId":"get_slang_question_api_word_of_day_slang__word_id__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/slang/{question_id}":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Submit Slang Answer","description":"Submit slang/culture quiz answer.","operationId":"submit_slang_answer_api_word_of_day_slang__question_id__post","parameters":[{"name":"question_id","in":"path","required":true,"schema":{"type":"integer","title":"Question Id"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_submit_slang_answer_api_word_of_day_slang__question_id__post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/slang/{word_id}/contributions":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Slang Contributions","description":"Get user-contributed slang terms for a word.","operationId":"get_slang_contributions_api_word_of_day_slang__word_id__contributions_get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Language"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","default":"recent","title":"Sort"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/slang/{word_id}/contribute":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Submit Slang Contribution","description":"Submit a user-contributed slang/cultural term.","operationId":"submit_slang_contribution_api_word_of_day_slang__word_id__contribute_post","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_submit_slang_contribution_api_word_of_day_slang__word_id__contribute_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/slang/contribution/{contribution_id}/like":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Like Slang Contribution","description":"Like a slang contribution.","operationId":"like_slang_contribution_api_word_of_day_slang_contribution__contribution_id__like_post","parameters":[{"name":"contribution_id","in":"path","required":true,"schema":{"type":"integer","title":"Contribution Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/caption-image/{word_id}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Caption Image","description":"Get image for caption contest.","operationId":"get_caption_image_api_word_of_day_caption_image__word_id__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/captions/{image_id}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Captions","description":"Get captions for an image (sorted by upvotes, newest, etc).","operationId":"get_captions_api_word_of_day_captions__image_id__get","parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"integer","title":"Image Id"}},{"name":"sort_by","in":"query","required":false,"schema":{"type":"string","default":"top","title":"Sort By"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/caption/{image_id}":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Submit Caption","description":"Submit a caption for an image.","operationId":"submit_caption_api_word_of_day_caption__image_id__post","parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"integer","title":"Image Id"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_submit_caption_api_word_of_day_caption__image_id__post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/caption/{caption_id}/vote":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Vote On Caption","description":"Upvote or downvote a caption. Users can only vote once per caption.","operationId":"vote_on_caption_api_word_of_day_caption__caption_id__vote_post","parameters":[{"name":"caption_id","in":"path","required":true,"schema":{"type":"integer","title":"Caption Id"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_vote_on_caption_api_word_of_day_caption__caption_id__vote_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/caption/{caption_id}/comments":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Caption Comments","description":"Get all comments for a caption (nested threading).","operationId":"get_caption_comments_api_word_of_day_caption__caption_id__comments_get","parameters":[{"name":"caption_id","in":"path","required":true,"schema":{"type":"integer","title":"Caption Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/caption/{caption_id}/comment":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Add Comment To Caption","description":"Add a comment to a caption (supports nested replies).","operationId":"add_comment_to_caption_api_word_of_day_caption__caption_id__comment_post","parameters":[{"name":"caption_id","in":"path","required":true,"schema":{"type":"integer","title":"Caption Id"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_add_comment_to_caption_api_word_of_day_caption__caption_id__comment_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/caption-comment/{comment_id}/vote":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Vote On Comment","description":"Upvote or downvote a comment.","operationId":"vote_on_comment_api_word_of_day_caption_comment__comment_id__vote_post","parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"integer","title":"Comment Id"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_vote_on_comment_api_word_of_day_caption_comment__comment_id__vote_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/caption/{caption_id}/flag":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Flag Caption","description":"Flag a caption for moderation.","operationId":"flag_caption_api_word_of_day_caption__caption_id__flag_post","parameters":[{"name":"caption_id","in":"path","required":true,"schema":{"type":"integer","title":"Caption Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/caption-comment/{comment_id}/flag":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Flag Comment","description":"Flag a comment for moderation.","operationId":"flag_comment_api_word_of_day_caption_comment__comment_id__flag_post","parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"integer","title":"Comment Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/caption-contest/{word_id}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Caption Contest","description":"Get caption contest data for a word (frontend-compatible route).","operationId":"get_caption_contest_api_word_of_day_caption_contest__word_id__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","default":"top","title":"Sort"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/caption-contest/{image_id}/caption":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Submit Caption Contest Caption","description":"Submit a caption (frontend-compatible route).","operationId":"submit_caption_contest_caption_api_word_of_day_caption_contest__image_id__caption_post","parameters":[{"name":"image_id","in":"path","required":true,"schema":{"type":"integer","title":"Image Id"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_submit_caption_contest_caption_api_word_of_day_caption_contest__image_id__caption_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/caption-contest/caption/{caption_id}/vote":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Vote On Caption Contest Caption","description":"Vote on a caption (frontend-compatible route).","operationId":"vote_on_caption_contest_caption_api_word_of_day_caption_contest_caption__caption_id__vote_post","parameters":[{"name":"caption_id","in":"path","required":true,"schema":{"type":"integer","title":"Caption Id"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_vote_on_caption_contest_caption_api_word_of_day_caption_contest_caption__caption_id__vote_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/caption-contest/caption/{caption_id}/comments":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Caption Contest Comments","description":"Get comments for a caption (frontend-compatible route).","operationId":"get_caption_contest_comments_api_word_of_day_caption_contest_caption__caption_id__comments_get","parameters":[{"name":"caption_id","in":"path","required":true,"schema":{"type":"integer","title":"Caption Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/caption-contest/caption/{caption_id}/comment":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Add Caption Contest Comment","description":"Add a comment to a caption (frontend-compatible route).","operationId":"add_caption_contest_comment_api_word_of_day_caption_contest_caption__caption_id__comment_post","parameters":[{"name":"caption_id","in":"path","required":true,"schema":{"type":"integer","title":"Caption Id"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_add_caption_contest_comment_api_word_of_day_caption_contest_caption__caption_id__comment_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/caption-contest/comment/{comment_id}/vote":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Vote On Caption Contest Comment","description":"Vote on a comment (frontend-compatible route).","operationId":"vote_on_caption_contest_comment_api_word_of_day_caption_contest_comment__comment_id__vote_post","parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"integer","title":"Comment Id"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_vote_on_caption_contest_comment_api_word_of_day_caption_contest_comment__comment_id__vote_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/improve-text":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Improve Text","description":"Use AI to improve user-written text in their target language.\nReturns an improved version with better grammar, vocabulary, and naturalness.","operationId":"improve_text_api_word_of_day_improve_text_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_improve_text_api_word_of_day_improve_text_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/recording-matches-enhanced/{word_id}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Enhanced Recording Matches","description":"Get matches for the user. Uses stored WordUserMatch records (created by\nbackground matching task) as the primary source. These are the same records\nthat trigger match notifications, so results are always consistent.","operationId":"get_enhanced_recording_matches_api_word_of_day_recording_matches_enhanced__word_id__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"learning_language","in":"query","required":true,"schema":{"type":"string","title":"Learning Language"}},{"name":"native_language","in":"query","required":true,"schema":{"type":"string","title":"Native Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/initiate-matched-chat":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Initiate Matched Chat","description":"Create a new conversation between matched users with ChaCha's introduction.\nChaCha explains what each person answered and suggests discussion topics.","operationId":"initiate_matched_chat_api_word_of_day_initiate_matched_chat_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_initiate_matched_chat_api_word_of_day_initiate_matched_chat_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/session-progress":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Word Of Day Session Progress","description":"Get the user's Word of the Day session progress for a specific date.\nIf date_str is provided (YYYY-MM-DD format), fetches progress for that date.\nOtherwise, returns today's progress.\nReturns which activities have been completed.","operationId":"get_word_of_day_session_progress_api_word_of_day_session_progress_get","parameters":[{"name":"date_str","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date Str"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/daily-session/today-status":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Daily Session Today Status","description":"Check whether the current user has completed today's daily session.\nReturns { completed: bool, date: \"YYYY-MM-DD\" }.","operationId":"get_daily_session_today_status_api_word_of_day_daily_session_today_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/word-of-day/session-history":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Word Of Day Session History","description":"Get the user's Word of the Day session progress for the past N days.\nReturns completed activities for each day, grouped by date.","operationId":"get_word_of_day_session_history_api_word_of_day_session_history_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/complete-activity":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Complete Word Of Day Activity","description":"Record that the user completed a specific Word of the Day activity.\nActivities: pronunciation, slang, matchmaking","operationId":"complete_word_of_day_activity_api_word_of_day_complete_activity_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_complete_word_of_day_activity_api_word_of_day_complete_activity_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/complete-session":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Complete Word Of Day Session","description":"Record that the user completed a Word of the Day session.\nThis should only count toward the streak (and the daily-practice reward) if\nall of the current daily-session activities are completed:\n1. Pronunciation\n2. Phrase of the Day\n3. Quick Read / Sound Catch (input exercise)\n4. Share & Match","operationId":"complete_word_of_day_session_api_word_of_day_complete_session_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_complete_word_of_day_session_api_word_of_day_complete_session_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/admin/future-activities":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Future Daily Activities","description":"Get future daily activities for admin preview.\nReturns word of the day data with activity counts for the next N days.\nOnly accessible to admin users.","operationId":"get_future_daily_activities_api_word_of_day_admin_future_activities_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/admin/word-details/{word_id}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Word Activity Details","description":"Get detailed activity information for a specific word.\nReturns all quiz questions, slang questions, recording prompts, and caption images.\nOnly accessible to admin users.","operationId":"get_word_activity_details_api_word_of_day_admin_word_details__word_id__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/admin/activity-analytics":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Word Of Day Activity Analytics","description":"Get Word of the Day activity participation and skip analytics.\nShows completion rates and skip rates for each activity type.\nOnly accessible to admin users.","operationId":"get_word_of_day_activity_analytics_api_word_of_day_admin_activity_analytics_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"word_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Word Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/cultural-question-audio/{word_id}/{language}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Cultural Question Audio","description":"Get or generate TTS audio for the cultural question in a specific language.\nUses Google Chirp 3 HD voices with caching to avoid regeneration.","operationId":"get_cultural_question_audio_api_word_of_day_cultural_question_audio__word_id___language__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/recording-prompt-audio/{word_id}/{language}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Recording Prompt Audio","description":"Get or generate TTS audio for the recording prompt in a specific language.\nUses Google Chirp 3 HD voices with caching to avoid regeneration.","operationId":"get_recording_prompt_audio_api_word_of_day_recording_prompt_audio__word_id___language__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/warmup-audio/{word_id}":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Warmup Audio Cache","description":"Pre-generate all audio clips for a word across all languages.\nCall this when a new Word of the Day is set to avoid first-user latency.\nGenerates: word pronunciation, cultural question audio, recording prompt audio.","operationId":"warmup_audio_cache_api_word_of_day_warmup_audio__word_id__post","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/cultural-response":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Analyze Cultural Response","description":"Analyze user's cultural response audio and provide encouraging feedback.\nReturns a badge based on overall score.","operationId":"analyze_cultural_response_api_word_of_day_cultural_response_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_analyze_cultural_response_api_word_of_day_cultural_response_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/cultural-answer":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Submit Cultural Answer","description":"Submit a user's answer to a cultural question.\nAnalyzes relevance and sentiment, provides Chacha feedback.\nOnly posts to community wall if appropriate.","operationId":"submit_cultural_answer_api_word_of_day_cultural_answer_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CulturalAnswerRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/cultural-answers/{word_id}/{language}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Cultural Answers","description":"Get all community answers for a cultural question.\nReturns answers with user names and regions for display in cloud/list view.\nUser's own contribution appears first with is_current_user flag.","operationId":"get_cultural_answers_api_word_of_day_cultural_answers__word_id___language__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/cultural-answer/{contribution_id}":{"put":{"tags":["word-of-day","word_of_day"],"summary":"Update Cultural Answer","description":"Update a user's own cultural answer.","operationId":"update_cultural_answer_api_word_of_day_cultural_answer__contribution_id__put","parameters":[{"name":"contribution_id","in":"path","required":true,"schema":{"type":"integer","title":"Contribution Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CulturalAnswerRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["word-of-day","word_of_day"],"summary":"Delete Cultural Answer","description":"Delete a user's own cultural answer.","operationId":"delete_cultural_answer_api_word_of_day_cultural_answer__contribution_id__delete","parameters":[{"name":"contribution_id","in":"path","required":true,"schema":{"type":"integer","title":"Contribution Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/polls/{word_id}/{language}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Polls","description":"Get poll questions and community results for a word.\nResults are only revealed after the user has voted on each poll.\nIf native_language is provided, also includes native language options for display.\nIf target_language is provided, includes target language question for bilingual display.","operationId":"get_polls_api_word_of_day_polls__word_id___language__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}},{"name":"native_language","in":"query","required":false,"schema":{"type":"string","title":"Native Language"}},{"name":"target_language","in":"query","required":false,"schema":{"type":"string","title":"Target Language"}},{"name":"min_display_votes","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Min Display Votes"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/polls/{word_id}/{language}/custom-answers":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Poll Custom Answers","description":"Fetch the most recent custom (user-submitted) poll answers with author\ninfo, JOINing `users` for full_name/username/profile_picture.\n\nSplit out of get_polls so this users-JOIN (no index on the join path,\nORDER BY created_at DESC) doesn't block first paint of the poll + results.\nThe frontend calls this asynchronously after the poll renders.","operationId":"get_poll_custom_answers_api_word_of_day_polls__word_id___language__custom_answers_get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}},{"name":"poll_index","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Poll Index"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/polls/vote":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Vote Poll","description":"Submit a vote on a poll. Returns updated community results.\nNo paywall - polls are free to encourage community engagement.","operationId":"vote_poll_api_word_of_day_polls_vote_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PollVoteRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/sentence-frames/{word_id}/{language}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Sentence Frames","description":"Get sentence frame starters for the open-ended question.\nReturns target language frames, and optionally native language frames for bilingual display.","operationId":"get_sentence_frames_api_word_of_day_sentence_frames__word_id___language__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}},{"name":"native_language","in":"query","required":false,"schema":{"type":"string","title":"Native Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/associations/submit":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Submit Word Associations","description":"Submit 3-5 word associations. No paywall.","operationId":"submit_word_associations_api_word_of_day_associations_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WordAssociationRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/associations/{word_id}/{language}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Word Association Cloud","description":"Get community word association cloud data. Returns word frequencies.","operationId":"get_word_association_cloud_api_word_of_day_associations__word_id___language__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/ranking/{word_id}/{language}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Ranking Data","description":"Get ranking scenarios and community average.","operationId":"get_ranking_data_api_word_of_day_ranking__word_id___language__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/ranking/submit":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Submit Ranking","description":"Submit ranking. No paywall.","operationId":"submit_ranking_api_word_of_day_ranking_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RankingSubmitRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/thinking-routine/{word_id}/{language}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Thinking Routine","description":"Get thinking routine prompts and user's previous responses.","operationId":"get_thinking_routine_api_word_of_day_thinking_routine__word_id___language__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/thinking-routine/submit":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Submit Thinking Routine","description":"Submit thinking routine responses. No paywall.","operationId":"submit_thinking_routine_api_word_of_day_thinking_routine_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThinkingRoutineRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/madlib/{word_id}/{language}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Madlib","description":"Get madlib template and current fills.","operationId":"get_madlib_api_word_of_day_madlib__word_id___language__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/madlib/fill":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Fill Madlib Blank","description":"Fill a madlib blank. First come first served per blank.","operationId":"fill_madlib_blank_api_word_of_day_madlib_fill_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MadlibFillRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/community-poll/{word_id}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Community Poll","description":"Get community poll data for Slot 1: 3 level options in user's native language.\nReturns vote status and results if already voted.","operationId":"get_community_poll_api_word_of_day_community_poll__word_id__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/community-poll/vote":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Vote Community Poll","description":"Vote on community poll (Slot 1). No paywall — free for all users.","operationId":"vote_community_poll_api_word_of_day_community_poll_vote_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityPollVoteRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/input-exercise/{word_id}/{language}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Input Exercise","description":"Get input exercise content for user's CEFR level and the day's exercise type.\nOptional `level` query param overrides the user's profile level (for session-level selection).","operationId":"get_input_exercise_api_word_of_day_input_exercise__word_id___language__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}},{"name":"level","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Level"}},{"name":"native","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Native"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/input-exercise/submit-comprehension":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Submit Comprehension","description":"Score comprehension answers. Free: 1 attempt, Premium: unlimited.","operationId":"submit_comprehension_api_word_of_day_input_exercise_submit_comprehension_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComprehensionSubmitRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/input-exercise/submit-reading-audio":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Submit Reading Audio","description":"Submit reading audio for passage-level pronunciation scoring.\nReuses GeminiSpeechService.","operationId":"submit_reading_audio_api_word_of_day_input_exercise_submit_reading_audio_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_submit_reading_audio_api_word_of_day_input_exercise_submit_reading_audio_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/input-exercise/submit-reaction-taps":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Submit Reaction Taps","description":"Score reaction taps against expected timestamps. Returns accuracy + leaderboard rank.","operationId":"submit_reaction_taps_api_word_of_day_input_exercise_submit_reaction_taps_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionTapSubmitRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/input-exercise/leaderboard/{word_id}/{language}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Input Exercise Leaderboard","description":"Get top 10 by average speed per word for a word's listening exercise.\nRanked by best_reaction_ms column (repurposed as average_ms, lowest wins), mixed across all languages.","operationId":"get_input_exercise_leaderboard_api_word_of_day_input_exercise_leaderboard__word_id___language__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/sparkle-assist":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Sparkle Assist","description":"AI-powered text improvement using Gemini with ChaCha personality.\nPremium-only or 1 free use per day.","operationId":"sparkle_assist_api_word_of_day_sparkle_assist_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SparkleAssistRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/phrase-exercise/audio/{content_id}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Phrase Exercise Audio","description":"Get or generate TTS audio for a phrase exercise using Gemini TTS.\nReturns cached MP3 if available (pre-generated or GCS), otherwise generates on-the-fly.\nNo auth required — audio is public like word audio.","operationId":"get_phrase_exercise_audio_api_word_of_day_phrase_exercise_audio__content_id__get","parameters":[{"name":"content_id","in":"path","required":true,"schema":{"type":"integer","title":"Content Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/phrase-exercise/{word_id}/{language}":{"get":{"tags":["word-of-day","word_of_day"],"summary":"Get Phrase Exercise","description":"Get phrase exercise content for user's CEFR level.\nOptional ?native= overrides native language, ?level= overrides CEFR level.","operationId":"get_phrase_exercise_api_word_of_day_phrase_exercise__word_id___language__get","parameters":[{"name":"word_id","in":"path","required":true,"schema":{"type":"integer","title":"Word Id"}},{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}},{"name":"native","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Native"}},{"name":"level","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Level"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/word-of-day/phrase-exercise/submit":{"post":{"tags":["word-of-day","word_of_day"],"summary":"Submit Phrase Exercise","description":"Score phrase exercise answers.","operationId":"submit_phrase_exercise_api_word_of_day_phrase_exercise_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhraseSubmitRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/badges/me":{"get":{"tags":["badges","badges"],"summary":"Get My Badges","description":"Return all earned badges and progress toward next tiers\nfor the currently authenticated user. Also triggers a check\nfor any newly earned badges.","operationId":"get_my_badges_api_badges_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/badges/user/{user_id}":{"get":{"tags":["badges","badges"],"summary":"Get User Badges Public","description":"Return earned badges for any user (for profile view).\nDoes not include progress or trigger new badge checks.","operationId":"get_user_badges_public_api_badges_user__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/badges/definitions":{"get":{"tags":["badges","badges"],"summary":"Get Badge Definitions","description":"Return all badge definitions with descriptions, tier thresholds,\nand tier colors. No authentication required.","operationId":"get_badge_definitions_api_badges_definitions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/analytics/track/page-view":{"post":{"tags":["analytics"],"summary":"Track Page View","description":"Track a page view event.\nCreates or updates visitor session and records page view.\n\nSync (def, not async def) so FastAPI runs it in its threadpool instead of\non the event loop. The body is entirely sync DB work (no awaits) — keeping\nit on the loop would block WebSockets / call traffic during the ~9\nslow-request/6h spikes. Client only checks response.ok and doesn't\nconsume the body, so the threadpool wait is invisible to UX.","operationId":"track_page_view_analytics_track_page_view_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PageViewRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/track/conversion":{"post":{"tags":["analytics"],"summary":"Track Conversion","description":"Track a conversion event (signup, onboarding step, etc.)","operationId":"track_conversion_analytics_track_conversion_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversionEventRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/summary":{"get":{"tags":["analytics"],"summary":"Get Analytics Summary","description":"Get analytics summary for the specified time period.\nOnly accessible to authenticated users (for now, make admin-only later).","operationId":"get_analytics_summary_analytics_summary_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyticsSummaryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/track/activity":{"post":{"tags":["analytics"],"summary":"Track User Activity","description":"Track user activity in their current session.\nAutomatically creates or updates the current session.\n\nSync (def, not async def) so FastAPI runs it in its threadpool — the body\nis pure sync DB (no awaits) and the client doesn't consume the response\nbody, just response.ok. Keeps the event loop free for call/WS traffic.","operationId":"track_user_activity_analytics_track_activity_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivityTrackingRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/end-session":{"post":{"tags":["analytics"],"summary":"End User Session","description":"Explicitly end the current user session.\nCalled when user logs out or closes the app.","operationId":"end_user_session_analytics_end_session_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/retention":{"get":{"tags":["analytics"],"summary":"Get Retention Metrics","description":"Get user retention metrics showing repeat users.\nShows how many users have logged in 2+ or 3+ days within the specified period.","operationId":"get_retention_metrics_analytics_retention_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetentionMetricsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/activation-retention":{"get":{"tags":["analytics"],"summary":"Get Activation Retention","description":"Get activation retention metrics at Day 1, Day 7, Day 30 across three tracks:\n1. \"Meaningful Action\" — messages, calls, lessons, ChaCha (NOT just logins)\n2. \"Activated Users\" — verified + profile complete + meaningful action\n3. \"ChaCha Activity\" — daily session / pronunciation / word of day etc.\nFor each cohort date, shows what % of users who signed up that day returned.","operationId":"get_activation_retention_analytics_activation_retention_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/activation-funnel":{"get":{"tags":["analytics"],"summary":"Get Activation Funnel","description":"Post-signup activation funnel: Signup → Email Verified → Profile Completed →\nFirst Meaningful Action (message, call, lesson, or ChaCha activity).\nShows drop-off at each step.","operationId":"get_activation_funnel_analytics_activation_funnel_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/user-composition":{"get":{"tags":["analytics"],"summary":"Get User Composition","description":"User demographic/composition analytics:\n- Signup method breakdown (OAuth provider vs email)\n- Top language pair combinations (native → target)\n- Proficiency level distribution\n- Account type breakdown (student / tutor)","operationId":"get_user_composition_analytics_user_composition_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":90,"title":"Days"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/user-geography":{"get":{"tags":["analytics"],"summary":"Get User Geography","description":"Get user geography based on registration IPs.\nUses ip-api.com batch endpoint for geolocation.\nResults are cached per unique IP set to avoid redundant lookups.","operationId":"get_user_geography_analytics_user_geography_get","parameters":[{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/feature-adoption":{"get":{"tags":["analytics"],"summary":"Get Feature Adoption","description":"Feature adoption rates and engagement tiers.\nShows what % of users have tried each feature and how engaged they are.","operationId":"get_feature_adoption_analytics_feature_adoption_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":90,"title":"Days"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/most-active-users":{"get":{"tags":["analytics"],"summary":"Get Most Active Users","description":"Get the most active users in the past N days with a breakdown of what they did.\nSeparates students and tutors.\n\nRanking prioritizes RETURNING users: active_days is the dominant scoring\nterm, so a one-day burst can't outrank a genuine multi-day returner.\nCalls are split into \"ChaCha Calls\" (the ChaCha bot is a participant) and \"Human Calls\",\nand ChaCha \"ghost\" sessions (failed Gemini Live connects — empty transcript,\nfabricated ~300s duration) are excluded so per-user call counts/minutes\nreflect real usage.","operationId":"get_most_active_users_analytics_most_active_users_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/profile-step-funnel":{"get":{"tags":["analytics"],"summary":"Get Profile Step Funnel","description":"Profile completion step funnel: shows how many unique users viewed/completed each step.\n\n\"Viewed\" comes from pages_visited in user_activity_sessions (captures all flows),\nmerged with profile_step_viewed_* activity keys (from the chat flow).\n\"Completed\" comes from profile_step_completed_* activity keys, plus page-visit\nheuristic: if a user visited step N+1, they completed step N.\n\nAll counts are unique users, not raw event totals.","operationId":"get_profile_step_funnel_analytics_profile_step_funnel_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/daily-session-participation":{"get":{"tags":["analytics"],"summary":"Get Daily Session Participation","description":"Daily session activity participation and skip rates.\nShows how many users did each activity vs entered the session.\n\nUses daily_session_attempts table (reliable backend tracking) as primary\ndata source, supplemented by user_activity_sessions for frontend-only\nevents like instant talk choices.","operationId":"get_daily_session_participation_analytics_daily_session_participation_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/analytics/engagement-summary":{"get":{"tags":["analytics"],"summary":"Get Engagement Summary","description":"Per-category engagement rollup powering the dashboard summary cards.\n\nReturns one row per category in ``EVENT_CATEGORY_MAP`` plus a top-events\nbreakdown and a 7/14/30-day sparkline (daily counts) for each. Counts are\nsourced from ``user_activity_sessions.activities_performed`` (the same\nJSONB dict that retention/activation endpoints already use), so any event\na feature emits via ``trackFeatureUse(...)`` shows up automatically.\n\nThe categories are coarse on purpose — fine-grained per-event detail is\non the Activity Monitoring page. The point of this endpoint is the\n\"is this whole feature area being used\" headline number.","operationId":"get_engagement_summary_analytics_engagement_summary_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/enhanced-flashcards/generation-methods":{"get":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Get Generation Methods","description":"Get available flashcard generation methods","operationId":"get_generation_methods_learning_enhanced_flashcards_generation_methods_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/learning/enhanced-flashcards/health":{"get":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Enhanced Flashcard Health","description":"Health check for enhanced flashcard service","operationId":"enhanced_flashcard_health_learning_enhanced_flashcards_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/learning/enhanced-flashcards/generate":{"post":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Generate Enhanced Flashcards","description":"Generate enhanced flashcards with multiple generation methods and user options","operationId":"generate_enhanced_flashcards_learning_enhanced_flashcards_generate_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/enhanced-flashcards/add-to-study-deck":{"post":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Add To Study Deck","description":"Add a single word/phrase to the user's study deck with LLM-generated flashcard","operationId":"add_to_study_deck_learning_enhanced_flashcards_add_to_study_deck_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/enhanced-flashcards/generate-theme-flashcards":{"post":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Generate Theme Flashcards","description":"Generate new vocabulary flashcards for a specific theme and optionally add to study deck","operationId":"generate_theme_flashcards_learning_enhanced_flashcards_generate_theme_flashcards_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/enhanced-flashcards/task/{task_id}/status":{"get":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Get Enhanced Task Status","description":"Get the status of an enhanced flashcard generation task","operationId":"get_enhanced_task_status_learning_enhanced_flashcards_task__task_id__status_get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/enhanced-flashcards/decks/{user_id}":{"get":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Get Enhanced Flashcard Decks","description":"Get all enhanced flashcard decks for a user","operationId":"get_enhanced_flashcard_decks_learning_enhanced_flashcards_decks__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/enhanced-flashcards/decks/{user_id}/due-counts":{"get":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Get Deck Due Counts","description":"Return {deck_id: due_count} where a card is due if its latest review's\nnext_review_at <= now, OR if it has never been reviewed (new card).","operationId":"get_deck_due_counts_learning_enhanced_flashcards_decks__user_id__due_counts_get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/enhanced-flashcards/deck/{user_id}/{deck_id}":{"get":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Get Enhanced Flashcard Deck","description":"Get a specific enhanced flashcard deck by ID","operationId":"get_enhanced_flashcard_deck_learning_enhanced_flashcards_deck__user_id___deck_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"deck_id","in":"path","required":true,"schema":{"type":"string","title":"Deck Id"}},{"name":"smart_order","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Smart Order"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Delete Enhanced Flashcard Deck","description":"Delete an enhanced flashcard deck","operationId":"delete_enhanced_flashcard_deck_learning_enhanced_flashcards_deck__user_id___deck_id__delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"deck_id","in":"path","required":true,"schema":{"type":"string","title":"Deck Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/enhanced-flashcards/review":{"post":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Review Enhanced Flashcard","description":"Record a user's review with SM2-lite scheduling.\n\nAccepts rating: int 0..4 (0=Don't know, 1=Again, 2=Hard, 3=Good, 4=Easy).\nBackward-compat: if only `correct` is sent (no rating), maps True->3 / False->1.","operationId":"review_enhanced_flashcard_learning_enhanced_flashcards_review_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/enhanced-flashcards/levels":{"get":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Get Available Cefr Levels","description":"Get available CEFR levels and their descriptions, or HSK levels for Mandarin","operationId":"get_available_cefr_levels_learning_enhanced_flashcards_levels_get","parameters":[{"name":"language","in":"query","required":false,"schema":{"type":"string","default":"","title":"Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/enhanced-flashcards/card-types":{"get":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Get Available Card Types","description":"Get available card types and their subtypes","operationId":"get_available_card_types_learning_enhanced_flashcards_card_types_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/learning/enhanced-flashcards/analytics/{user_id}":{"get":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Get Enhanced Flashcard Analytics","description":"Get analytics for a user's enhanced flashcard performance.\n\nComputed from flashcards + flashcard_reviews in the DB. The previous\nimplementation read a per-process memory dict populated only by the pod\nthat ran generation/review — on a multi-pod cluster it returned zeros\nwhenever the GET landed on any other pod.","operationId":"get_enhanced_flashcard_analytics_learning_enhanced_flashcards_analytics__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/enhanced-flashcards/deck/{user_id}/{deck_id}/rename":{"put":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Rename Enhanced Flashcard Deck","description":"Rename an enhanced flashcard deck","operationId":"rename_enhanced_flashcard_deck_learning_enhanced_flashcards_deck__user_id___deck_id__rename_put","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"deck_id","in":"path","required":true,"schema":{"type":"string","title":"Deck Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_rename_enhanced_flashcard_deck_learning_enhanced_flashcards_deck__user_id___deck_id__rename_put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/enhanced-flashcards/generate-audio":{"post":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Generate Flashcard Audio","description":"Generate audio for flashcard listening exercises","operationId":"generate_flashcard_audio_learning_enhanced_flashcards_generate_audio_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_generate_flashcard_audio_learning_enhanced_flashcards_generate_audio_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/enhanced-flashcards/card/{user_id}/{card_id}":{"delete":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Delete Flashcard","description":"Delete a specific flashcard from the database","operationId":"delete_flashcard_learning_enhanced_flashcards_card__user_id___card_id__delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"card_id","in":"path","required":true,"schema":{"type":"string","title":"Card Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Update Flashcard","description":"Update a specific flashcard's content","operationId":"update_flashcard_learning_enhanced_flashcards_card__user_id___card_id__put","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"card_id","in":"path","required":true,"schema":{"type":"string","title":"Card Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/enhanced-flashcards/card/manual":{"post":{"tags":["enhanced-flashcards","enhanced-flashcards"],"summary":"Add Manual Flashcard","description":"Add a manually created flashcard to a user's deck","operationId":"add_manual_flashcard_learning_enhanced_flashcards_card_manual_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ai-voice/start-session":{"post":{"tags":["ai-voice"],"summary":"Start Ai Voice Session","description":"Start a new AI voice calling session","operationId":"start_ai_voice_session_ai_voice_start_session_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_start_ai_voice_session_ai_voice_start_session_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ai-voice/end-session":{"post":{"tags":["ai-voice"],"summary":"End Ai Voice Session","description":"End an AI voice calling session","operationId":"end_ai_voice_session_ai_voice_end_session_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_end_ai_voice_session_ai_voice_end_session_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ai-voice/process-message":{"post":{"tags":["ai-voice"],"summary":"Process Ai Voice Message","description":"Process a voice message through the AI pipeline (non-blocking)","operationId":"process_ai_voice_message_ai_voice_process_message_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_process_ai_voice_message_ai_voice_process_message_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ai-voice/process-message/status/{message_id}":{"get":{"tags":["ai-voice"],"summary":"Get Ai Voice Message Status","description":"Poll for AI voice message processing status","operationId":"get_ai_voice_message_status_ai_voice_process_message_status__message_id__get","parameters":[{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ai-voice/process-message/status-preview/{message_id}":{"get":{"tags":["ai-voice"],"summary":"Get Ai Voice Message Status Preview","description":"Poll for AI voice message processing status (preview/anonymous users)","operationId":"get_ai_voice_message_status_preview_ai_voice_process_message_status_preview__message_id__get","parameters":[{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}},{"name":"user_id","in":"query","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ai-voice/get-response-audio/{session_id}/{filename}":{"get":{"tags":["ai-voice"],"summary":"Get Ai Response Audio","description":"Get AI response audio file","operationId":"get_ai_response_audio_ai_voice_get_response_audio__session_id___filename__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"filename","in":"path","required":true,"schema":{"type":"string","title":"Filename"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ai-voice/session/{session_id}/history":{"get":{"tags":["ai-voice"],"summary":"Get Ai Voice Session History","description":"Get conversation history for an AI voice session","operationId":"get_ai_voice_session_history_ai_voice_session__session_id__history_get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ai-voice/test-audio":{"get":{"tags":["ai-voice"],"summary":"Test Audio Generation","description":"Test endpoint to verify audio generation and serving works","operationId":"test_audio_generation_ai_voice_test_audio_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/ai-voice/synthesize":{"post":{"tags":["ai-voice"],"summary":"Synthesize Ai Voice","description":"Synthesize text to speech for AI voice responses","operationId":"synthesize_ai_voice_ai_voice_synthesize_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_synthesize_ai_voice_ai_voice_synthesize_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ai-voice/sessions/{session_id}/language":{"put":{"tags":["ai-voice"],"summary":"Update Session Language","description":"Update the language for an existing AI voice session","operationId":"update_session_language_ai_voice_sessions__session_id__language_put","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_update_session_language_ai_voice_sessions__session_id__language_put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ai-voice/add-ai-message":{"post":{"tags":["ai-voice"],"summary":"Add Ai Message To History","description":"Add an AI message to the conversation history (used for pre-generated welcome messages)","operationId":"add_ai_message_to_history_ai_voice_add_ai_message_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_add_ai_message_to_history_ai_voice_add_ai_message_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ai-voice/welcome-audio/{language}/{filename}":{"get":{"tags":["ai-voice"],"summary":"Get Welcome Audio","description":"Serve welcome audio files for AI voice chat from GCS or local storage\n\nArgs:\n    language: Language code (english, spanish, french, german, mandarin)\n    filename: Audio filename (chacha_welcome.wav, chacha_waiting.wav, chacha_tutor_welcome.wav, chacha_tutor_waiting.wav)","operationId":"get_welcome_audio_ai_voice_welcome_audio__language___filename__get","parameters":[{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}},{"name":"filename","in":"path","required":true,"schema":{"type":"string","title":"Filename"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ai-voice/start-session-preview":{"post":{"tags":["ai-voice"],"summary":"Start Ai Voice Session Preview","description":"Start a new AI voice session for anonymous/preview users","operationId":"start_ai_voice_session_preview_ai_voice_start_session_preview_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_start_ai_voice_session_preview_ai_voice_start_session_preview_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ai-voice/process-message-preview":{"post":{"tags":["ai-voice"],"summary":"Process Ai Voice Message Preview","description":"Process a voice message for anonymous/preview users (non-blocking)","operationId":"process_ai_voice_message_preview_ai_voice_process_message_preview_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_process_ai_voice_message_preview_ai_voice_process_message_preview_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ai-voice/synthesize-preview":{"post":{"tags":["ai-voice"],"summary":"Synthesize Ai Voice Preview","description":"Synthesize AI voice response for preview users","operationId":"synthesize_ai_voice_preview_ai_voice_synthesize_preview_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_synthesize_ai_voice_preview_ai_voice_synthesize_preview_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ai-voice/end-session-preview":{"post":{"tags":["ai-voice"],"summary":"End Ai Voice Session Preview","description":"End AI voice session for preview users","operationId":"end_ai_voice_session_preview_ai_voice_end_session_preview_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_end_ai_voice_session_preview_ai_voice_end_session_preview_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/assessment/start":{"post":{"tags":["assessment"],"summary":"Start Assessment","description":"Create the assessment row + Redis state. Returns the FIRST item.\n\nBoth text and voice modalities use this same flow — the difference is that\nvoice mode also opens an /ai-voice/ws/live WebSocket later for narration.\nEach item is served via this REST API; the WS only does TTS+STT.","operationId":"start_assessment_assessment_start_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartAssessmentBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/assessment/answer":{"post":{"tags":["assessment"],"summary":"Post Answer","description":"Submit one item answer. Returns whether it was correct + the next item.\n\nFor discrete items (mcq_grammar / cloze / vocab_match / listen_pick), pass\n`answer_index` (0-3). For produce_short, pass `response_text`.","operationId":"post_answer_assessment_answer_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnswerBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/assessment/greeting":{"post":{"tags":["assessment"],"summary":"Post Greeting","description":"Record the learner's spoken reply to ChaCha's 'How are you?' opener.\n\nVoice mode only. The transcript is stored as a production with kind=\n'greeting' so it feeds the productive-CEFR grader (alongside chat_turn\nand produce_short responses) at score time. Tiny replies like 'good' do\nbias the grader downward, but that's accurate signal — a learner whose\nonly voluntary production is 'good' really is performing at A1.","operationId":"post_greeting_assessment_greeting_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GreetingBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/assessment/message":{"post":{"tags":["assessment"],"summary":"Deprecated Message","description":"Legacy v1 endpoint — replaced by /assessment/answer in v2.","operationId":"deprecated_message_assessment_message_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/assessment/finalize/{assessment_id}":{"post":{"tags":["assessment"],"summary":"Finalize","description":"Score the placement state, select lessons, return proposal.\n\nDoes NOT write language_proficiency — that's done in /confirm-level.","operationId":"finalize_assessment_finalize__assessment_id__post","parameters":[{"name":"assessment_id","in":"path","required":true,"schema":{"type":"string","title":"Assessment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinalizeBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/assessment/confirm-level/{assessment_id}":{"post":{"tags":["assessment"],"summary":"Confirm Level Endpoint","description":"Write inferred (or overridden) CEFR into users.language_proficiency iff accepted.","operationId":"confirm_level_endpoint_assessment_confirm_level__assessment_id__post","parameters":[{"name":"assessment_id","in":"path","required":true,"schema":{"type":"string","title":"Assessment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmLevelBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/assessment/{assessment_id}":{"get":{"tags":["assessment"],"summary":"Get Assessment","operationId":"get_assessment_assessment__assessment_id__get","parameters":[{"name":"assessment_id","in":"path","required":true,"schema":{"type":"string","title":"Assessment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/assessment/active-set/current":{"get":{"tags":["assessment"],"summary":"Get Active Set","description":"Return the user's active AssignedLessonSet (optionally filtered by language).","operationId":"get_active_set_assessment_active_set_current_get","parameters":[{"name":"target_language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Language"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/assessment/skip-to-beginner":{"post":{"tags":["assessment"],"summary":"Skip To Beginner","description":"Skip-the-test path. Used by both PHASE 0 short-circuits:\n- `complete_beginner=True` → stamp profile_data.complete_beginner[lang]\n  and force CEFR=A1 (override_cefr ignored for safety).\n- `complete_beginner=False`, `override_cefr=None` → honor user's\n  existing self-report (the \"skip\" path); CEFR stays at whatever's in\n  language_proficiency, lessons selected at that level.","operationId":"skip_to_beginner_assessment_skip_to_beginner_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SkipBeginnerBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/assessment/abandon/{assessment_id}":{"post":{"tags":["assessment"],"summary":"Abandon","operationId":"abandon_assessment_abandon__assessment_id__post","parameters":[{"name":"assessment_id","in":"path","required":true,"schema":{"type":"string","title":"Assessment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/messages/{message_id}/session":{"get":{"tags":["call","calls"],"summary":"Get Call Session By Message","description":"Find the call session associated with a message","operationId":"get_call_session_by_message_call_messages__message_id__session_get","parameters":[{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/sessions":{"post":{"tags":["call","calls"],"summary":"Create Call Session","description":"Create a new call session","operationId":"create_call_session_call_sessions_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCallSessionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallSessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["call","calls"],"summary":"Get User Call Sessions","description":"Get call sessions for a user with participant information","operationId":"get_user_call_sessions_call_sessions_get","parameters":[{"name":"user_id","in":"query","required":false,"schema":{"type":"string","title":"User Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":10,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/sessions/{session_id}/room":{"put":{"tags":["call","calls"],"summary":"Update Session Room","description":"Update the room name for a call session when the actual call starts","operationId":"update_session_room_call_sessions__session_id__room_put","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"room_name","in":"query","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"call_id","in":"query","required":false,"schema":{"type":"string","title":"Call Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/sessions/{session_id}/transcription-language":{"put":{"tags":["call","calls"],"summary":"Update Session Transcription Language","description":"Update the transcription language for a call session","operationId":"update_session_transcription_language_call_sessions__session_id__transcription_language_put","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"language","in":"query","required":true,"schema":{"type":"string","title":"Language"}},{"name":"user_id","in":"query","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/sessions/{call_session_id}/participants":{"post":{"tags":["call","calls"],"summary":"Add Call Participant","description":"Add a participant to a call session.\n\n🔒 A user may only enroll THEMSELVES. Any client-supplied user_id is ignored;\nthe enrolled participant is always derived from the authenticated token.","operationId":"add_call_participant_call_sessions__call_session_id__participants_post","parameters":[{"name":"call_session_id","in":"path","required":true,"schema":{"type":"string","title":"Call Session Id"}},{"name":"user_id","in":"query","required":false,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/sessions/{call_session_id}/end":{"put":{"tags":["call","calls"],"summary":"End Call Session","description":"End a call session and calculate duration","operationId":"end_call_session_call_sessions__call_session_id__end_put","parameters":[{"name":"call_session_id","in":"path","required":true,"schema":{"type":"string","title":"Call Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/sessions/{call_session_id}/call-limit":{"get":{"tags":["call","calls"],"summary":"Get Call Effective Limit","description":"Effective per-call duration limit for a peer call = the MOST GENEROUS\nparticipant's tier limit.\n\nIf any participant is premium / pro-tutor / org-premium (their\ncall_minutes_per_call is the 999999 \"unlimited\" sentinel), the whole call is\nuncapped and the client shows no countdown. The 45-min cap (warning + the\nclient-side auto-end) only applies when EVERY participant is free/lite. This\nkeeps the on-screen warning consistent with what actually happens — a free\nuser paired with a premium partner no longer sees a \"call will end\" warning\nfor a call that won't end.\n\n🔒 Had no auth dependency — anyone could pull the tier composition of any\ncall by guessing/enumerating a UUID. Requires the caller to be an\nauthenticated participant of the session.","operationId":"get_call_effective_limit_call_sessions__call_session_id__call_limit_get","parameters":[{"name":"call_session_id","in":"path","required":true,"schema":{"type":"string","title":"Call Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/sessions/{call_session_id}/summary":{"get":{"tags":["call","calls"],"summary":"Get Call Summary","description":"Get the quick qualitative summary for a call session.","operationId":"get_call_summary_call_sessions__call_session_id__summary_get","parameters":[{"name":"call_session_id","in":"path","required":true,"schema":{"type":"string","title":"Call Session Id"}},{"name":"requesting_user_id","in":"query","required":false,"schema":{"type":"string","title":"Requesting User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/sessions/{call_session_id}/rating":{"post":{"tags":["call","calls"],"summary":"Submit Call Rating","description":"Store a post-call rating (stars and/or comment) for this call.\n\nThis is the durable store for AI (ChaCha) call ratings — they never touch\nuser_reviews. Human calls also rate the partner via /users/review; this\nendpoint just adds the per-call technical rating + comment. Upserts per\n(call_session, user) so a re-submit updates rather than duplicates.\n\nSync def on purpose: single-worker gamma — an async handler doing sync DB\nwork would block the event loop.","operationId":"submit_call_rating_call_sessions__call_session_id__rating_post","parameters":[{"name":"call_session_id","in":"path","required":true,"schema":{"type":"string","title":"Call Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallRatingRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/sessions/{call_session_id}/detailed-report-access":{"post":{"tags":["call","calls"],"summary":"Record Detailed Report Access","description":"Check and record access to a detailed call report. Free users get 3 lifetime.","operationId":"record_detailed_report_access_call_sessions__call_session_id__detailed_report_access_post","parameters":[{"name":"call_session_id","in":"path","required":true,"schema":{"type":"string","title":"Call Session Id"}},{"name":"requesting_user_id","in":"query","required":false,"schema":{"type":"string","title":"Requesting User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/sessions/{call_session_id}/transcript-exists":{"get":{"tags":["call","calls"],"summary":"Check Transcript Exists","description":"Quick check if transcript exists - same method as analytics","operationId":"check_transcript_exists_call_sessions__call_session_id__transcript_exists_get","parameters":[{"name":"call_session_id","in":"path","required":true,"schema":{"type":"string","title":"Call Session Id"}},{"name":"requesting_user_id","in":"query","required":false,"schema":{"type":"string","title":"Requesting User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/sessions/{call_session_id}/transcript":{"get":{"tags":["call","calls"],"summary":"Get Call Transcript","description":"Get call transcript with privacy settings respected","operationId":"get_call_transcript_call_sessions__call_session_id__transcript_get","parameters":[{"name":"call_session_id","in":"path","required":true,"schema":{"type":"string","title":"Call Session Id"}},{"name":"requesting_user_id","in":"query","required":false,"schema":{"type":"string","title":"Requesting User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallTranscriptResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["call","calls"],"summary":"Update Call Transcript","description":"Update call transcript data for a specific user.\n\n🔒 Authorization is on the WRITER (the authenticated token), not on the\nclient-supplied user_id. NOTE: the client legitimately writes REMOTE\ntranscript entries attributed to the *other* participant (see\ntranscriptService.saveTranscriptEntry / isRemote), so we deliberately do\nNOT force speaker_id == caller — instead we require the caller to be a\nparticipant of the session, which closes the IDOR (an outsider writing\ninto a call they aren't in) while preserving remote-speaker attribution.","operationId":"update_call_transcript_call_sessions__call_session_id__transcript_put","parameters":[{"name":"call_session_id","in":"path","required":true,"schema":{"type":"string","title":"Call Session Id"}},{"name":"user_id","in":"query","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptEntryRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/sessions/{call_session_id}/transcription-state":{"put":{"tags":["call","calls"],"summary":"Update Transcription State","description":"Track when a user enables/disables transcription during a call.\n\nThis enables per-message privacy filtering based on when transcription was actually enabled.\n\nArgs:\n    call_session_id: Call session ID (UUID or instant match session ID)\n    user_id: User ID who is changing their transcription state\n    enabled: Whether transcription is being enabled (True) or disabled (False)\n    reason: Reason for change - \"user_toggled\", \"quota_exceeded\", \"initial_setting\"\n\n🔒 Had zero auth dependency. This drives per-message privacy filtering\n(whether a transcript line is retained), so an outsider who knew a\ncall_session_id could forge \"transcription enabled\" history for a call\nthey were never in. Now requires the caller to be an authenticated\nparticipant of the session, and the recorded user_id is always the\ncaller's own id — a client-supplied user_id can no longer forge another\nparticipant's transcription-state history.","operationId":"update_transcription_state_call_sessions__call_session_id__transcription_state_put","parameters":[{"name":"call_session_id","in":"path","required":true,"schema":{"type":"string","title":"Call Session Id"}},{"name":"user_id","in":"query","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"enabled","in":"query","required":true,"schema":{"type":"boolean","title":"Enabled"}},{"name":"reason","in":"query","required":false,"schema":{"type":"string","default":"user_toggled","title":"Reason"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/cleanup-orphaned":{"post":{"tags":["call","calls"],"summary":"Manual Cleanup Orphaned Sessions","description":"Manually trigger cleanup of orphaned call sessions.\n\n🔒 Had no auth dependency at all. Not called by the frontend (cleanup\nalready runs opportunistically inside create_call_session) — this is a\nmanual maintenance trigger, so it's gated the same way its sibling\n/admin/tokens/cleanup-orphaned is, rather than opened to any logged-in user.","operationId":"manual_cleanup_orphaned_sessions_call_cleanup_orphaned_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/sessions/{call_session_id}/heartbeat":{"post":{"tags":["call","calls"],"summary":"Heartbeat Call Session","description":"Update heartbeat for an active call session to prevent it from being cleaned up\n\n🔒 Had no auth dependency — anyone who knew/guessed a call_session_id could\nkeep an arbitrary call \"alive\" indefinitely, defeating orphan cleanup.\nRequires the caller to be an authenticated participant of the session.","operationId":"heartbeat_call_session_call_sessions__call_session_id__heartbeat_post","parameters":[{"name":"call_session_id","in":"path","required":true,"schema":{"type":"string","title":"Call Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/admin/tokens/cleanup-orphaned":{"post":{"tags":["call","calls"],"summary":"Cleanup Orphaned Token Reservations","description":"Admin endpoint to clean up orphaned token reservations.\n\nOrphaned reservations occur when:\n- A call is initiated and tokens are reserved\n- The call never completes or crashes\n- No finalization happens\n\nThis endpoint refunds those stuck tokens back to users.\n\nArgs:\n    age_threshold_hours: Minimum age (in hours) for a reservation to be considered orphaned","operationId":"cleanup_orphaned_token_reservations_call_admin_tokens_cleanup_orphaned_post","parameters":[{"name":"age_threshold_hours","in":"query","required":false,"schema":{"type":"integer","maximum":48,"minimum":1,"default":2,"title":"Age Threshold Hours"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/call/admin/tokens/health":{"get":{"tags":["call","calls"],"summary":"Token Economy Health Check","description":"Admin endpoint to check token economy health.\n\nReturns:\n    - Total tokens in circulation\n    - Pending reservations\n    - Recent failed finalizations\n    - Orphaned reservation count","operationId":"token_economy_health_check_call_admin_tokens_health_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/instant-match/join":{"post":{"tags":["instant-match","instant-match"],"summary":"Join Matchmaking Queue","description":"Join the instant matchmaking queue.","operationId":"join_matchmaking_queue_instant_match_join_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstantMatchJoinRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/instant-match/leave":{"post":{"tags":["instant-match","instant-match"],"summary":"Leave Matchmaking Queue","description":"Leave the matchmaking queue.","operationId":"leave_matchmaking_queue_instant_match_leave_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstantMatchLeaveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/instant-match/status":{"get":{"tags":["instant-match","instant-match"],"summary":"Get Matchmaking Status","description":"Check matchmaking status for a user.","operationId":"get_matchmaking_status_instant_match_status_get","parameters":[{"name":"userId","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Userid"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/instant-match/end-match/{session_id}":{"post":{"tags":["instant-match","instant-match"],"summary":"End Match Session","description":"End an active match session.","operationId":"end_match_session_instant_match_end_match__session_id__post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/instant-match/stats":{"get":{"tags":["instant-match","instant-match"],"summary":"Get Matchmaking Stats","description":"Get current matchmaking queue statistics (admin/debug).","operationId":"get_matchmaking_stats_instant_match_stats_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/instant-match/cleanup":{"post":{"tags":["instant-match","instant-match"],"summary":"Cleanup Stale Users","description":"Clean up stale users from the queue (admin/debug).","operationId":"cleanup_stale_users_instant_match_cleanup_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/instant-match/background-mode":{"post":{"tags":["instant-match","instant-match"],"summary":"Set Background Mode","description":"Enable or disable background matching mode.","operationId":"set_background_mode_instant_match_background_mode_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstantMatchBackgroundRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/instant-match/accept/{match_id}":{"post":{"tags":["instant-match","instant-match"],"summary":"Accept Match","description":"Accept a pending match offer.","operationId":"accept_match_instant_match_accept__match_id__post","parameters":[{"name":"match_id","in":"path","required":true,"schema":{"type":"string","title":"Match Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/instant-match/reject/{match_id}":{"post":{"tags":["instant-match","instant-match"],"summary":"Reject Match","description":"Reject a pending match offer.","operationId":"reject_match_instant_match_reject__match_id__post","parameters":[{"name":"match_id","in":"path","required":true,"schema":{"type":"string","title":"Match Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/instant-match/sessions/{match_session_id}/convert":{"post":{"tags":["instant-match-calls","instant-match-calls"],"summary":"Convert Instant Match To Call Session","description":"Convert an instant match session to a regular call session for transcript storage.","operationId":"convert_instant_match_to_call_session_instant_match_sessions__match_session_id__convert_post","parameters":[{"name":"match_session_id","in":"path","required":true,"schema":{"type":"string","title":"Match Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/instant-match/sessions/{match_session_id}/transcript":{"put":{"tags":["instant-match-calls","instant-match-calls"],"summary":"Save Instant Match Transcript","description":"Save transcript entry for an instant match session.\n\n🔒 Had no participant check at all — anyone who knew a match_session_id\ncould inject fabricated transcript lines (attributed to any `user_id` they\nchose) into another pair's call, or bootstrap-convert one they had no part\nin. Authorization is on the WRITER (the token), never on the client-\nsupplied `user_id` — same as call_routes.update_call_transcript, which the\nclient also legitimately uses to write REMOTE entries attributed to the\n*other* participant.","operationId":"save_instant_match_transcript_instant_match_sessions__match_session_id__transcript_put","parameters":[{"name":"match_session_id","in":"path","required":true,"schema":{"type":"string","title":"Match Session Id"}},{"name":"user_id","in":"query","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptEntryRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["instant-match-calls","instant-match-calls"],"summary":"Get Instant Match Transcript","description":"Get transcript for an instant match session.\n\n🔒 This used to forward to call_routes.get_call_transcript with three bare\npositional args (call_session_id, requesting_user_id, db) against a\n4-parameter signature (call_session_id, requesting_user_id, current_user,\ndb) — `db` silently landed in the `current_user` slot, so\nget_call_transcript's own authorization check (current_user.id) ran\nagainst a DB Session object instead of the caller, not a real caller\nidentity. Fixed by depending on the actual User object here and passing\nevery argument by keyword.","operationId":"get_instant_match_transcript_instant_match_sessions__match_session_id__transcript_get","parameters":[{"name":"match_session_id","in":"path","required":true,"schema":{"type":"string","title":"Match Session Id"}},{"name":"requesting_user_id","in":"query","required":true,"schema":{"type":"string","title":"Requesting User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/instant-match/sessions/{match_session_id}/end":{"post":{"tags":["instant-match-calls","instant-match-calls"],"summary":"End Instant Match Call Session","description":"End an instant match call session and update the corresponding call session.\n\n🔒 Had no participant check — anyone who knew a match_session_id (a\nURL-shaped string, not a secret) could force-end another pair's call,\nincluding its billing-relevant duration/status side effects.\n\nAlso had no `except HTTPException: raise` before its catch-all — unlike\nevery other endpoint in this file — so the 403 this fix raises would have\nbeen swallowed and reported back as a generic 500.","operationId":"end_instant_match_call_session_instant_match_sessions__match_session_id__end_post","parameters":[{"name":"match_session_id","in":"path","required":true,"schema":{"type":"string","title":"Match Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bot/start-session":{"post":{"tags":["bot","bot"],"summary":"Start Bot Session","description":"Start a bot session for instant matchmaking queue.","operationId":"start_bot_session_bot_start_session_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BotSessionStartRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bot/end-session":{"post":{"tags":["bot","bot"],"summary":"End Bot Session","description":"End a bot session.","operationId":"end_bot_session_bot_end_session_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BotSessionEndRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bot/session/{session_id}/status":{"get":{"tags":["bot","bot"],"summary":"Get Bot Session Status","description":"Get the status of a bot session.","operationId":"get_bot_session_status_bot_session__session_id__status_get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bot/session/{session_id}/message":{"post":{"tags":["bot","bot"],"summary":"Send Bot Message","description":"Send a message to the bot session.","operationId":"send_bot_message_bot_session__session_id__message_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BotMessageRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bot/session/{session_id}/conversation":{"get":{"tags":["bot","bot"],"summary":"Get Bot Conversation","description":"Get the conversation history for a bot session.","operationId":"get_bot_conversation_bot_session__session_id__conversation_get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bot/grammar-check":{"post":{"tags":["bot","bot"],"summary":"Grammar Check","description":"Check grammar and provide corrections using AI.","operationId":"grammar_check_bot_grammar_check_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrammarCheckRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bot/grammar-check-preview":{"post":{"tags":["bot","bot"],"summary":"Grammar Check Preview","description":"Check grammar for preview users (no authentication required).","operationId":"grammar_check_preview_bot_grammar_check_preview_post","parameters":[{"name":"user_id","in":"query","required":false,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrammarCheckRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bot/translate-and-explain":{"post":{"tags":["bot","bot"],"summary":"Translate And Explain","description":"Translate text and provide learning advice using ChaCha.","operationId":"translate_and_explain_bot_translate_and_explain_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranslateAndExplainRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/system/gpu-status":{"get":{"tags":["system","system"],"summary":"Get Gpu Status","description":"Get current GPU status and usage information.","operationId":"get_gpu_status_api_system_gpu_status_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GPUInfo"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/system/model-status":{"get":{"tags":["system","system"],"summary":"Get Model Status","description":"Get status of loaded AI models.","operationId":"get_model_status_api_system_model_status_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/system/full-status":{"get":{"tags":["system","system"],"summary":"Get Full System Status","description":"Get comprehensive system status including GPU, models, CPU, and memory.","operationId":"get_full_system_status_api_system_full_status_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SystemInfo"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/system/verify-gpu":{"post":{"tags":["system","system"],"summary":"Verify Gpu Functionality","description":"Run GPU verification tests.","operationId":"verify_gpu_functionality_api_system_verify_gpu_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/verify":{"get":{"tags":["admin","admin"],"summary":"Verify Admin Access","description":"Verify admin access","operationId":"verify_admin_access_admin_verify_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/stats":{"get":{"tags":["admin","admin"],"summary":"Get Admin Stats","description":"Get comprehensive admin statistics.\n\nSync (def) so FastAPI runs it in its threadpool — a cold-cache fill on\nthis handler used to block the event loop for ~3.5s and queue every\nother in-flight request behind it (observed post-hotfix-76 2026-05-30).\nNo awaits in the body, so the conversion is mechanical.","operationId":"get_admin_stats_admin_stats_get","parameters":[{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users/online":{"get":{"tags":["admin","admin"],"summary":"Get Online Users","description":"Get list of currently online users","operationId":"get_online_users_admin_users_online_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/conversations/active":{"get":{"tags":["admin","admin"],"summary":"Get Active Conversations","description":"Get list of active conversations","operationId":"get_active_conversations_admin_conversations_active_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/calls/active":{"get":{"tags":["admin","admin"],"summary":"Get Active Calls","description":"Get list of active calls","operationId":"get_active_calls_admin_calls_active_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/capacity":{"get":{"tags":["admin","admin"],"summary":"Get Capacity Status","description":"Get real-time capacity status for all rate-limited services. Sync (def)\nso cold-cache fills run in the threadpool, not on the event loop.","operationId":"get_capacity_status_admin_capacity_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/deepgram/health":{"get":{"tags":["admin","admin"],"summary":"Get Deepgram Health","description":"Get Deepgram connection health status\n\nReturns:\n- Overall health status (ok/degraded/critical)\n- Connection success rate\n- Active sessions\n- Recent failures\n- Summary metrics","operationId":"get_deepgram_health_admin_deepgram_health_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/talk-now/waiting-pool":{"get":{"tags":["admin","admin"],"summary":"Get Talk Now Waiting Pool","description":"Get users currently waiting in the Talk Now pool. Sync (def) so\ncold-cache fills run in the threadpool, not on the event loop.","operationId":"get_talk_now_waiting_pool_admin_talk_now_waiting_pool_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/calls/details":{"get":{"tags":["admin","admin"],"summary":"Get Calls Details","description":"Get detailed information about all active calls","operationId":"get_calls_details_admin_calls_details_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/calls/{call_id}/end":{"post":{"tags":["admin","admin"],"summary":"End Call","description":"End an active call (admin override)","operationId":"end_call_admin_calls__call_id__end_post","parameters":[{"name":"call_id","in":"path","required":true,"schema":{"type":"string","title":"Call Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/business-metrics":{"get":{"tags":["admin","admin"],"summary":"Get Business Metrics","description":"Get business metrics for the specified number of days.\n\nReturns revenue, costs, subscriptions, and user engagement metrics.","operationId":"get_business_metrics_admin_business_metrics_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/business-metrics/export":{"get":{"tags":["admin","admin"],"summary":"Export Business Metrics","description":"Export business metrics as CSV file for download.","operationId":"export_business_metrics_admin_business_metrics_export_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/fraud-alerts":{"get":{"tags":["admin","admin"],"summary":"Get Fraud Alerts","description":"Get recent fraud alerts for admin monitoring. Sync (def) so\ncold-cache fills run in the threadpool, not on the event loop.\n\nDeliberately NOT segmented. Every fraud_alerts row on gamma carries a NULL\nuser_id (517 of 517, checked 2026-09-02) — the alerts are raised against\nsignals, not attributed to an account. Filtering them by segment would show\na school an empty list and imply there is no fraud, which is worse than\nshowing the unfiltered list. Attribute the alerts first, then segment them.","operationId":"get_fraud_alerts_admin_fraud_alerts_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"severity","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Severity"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/tutor-applications":{"get":{"tags":["admin","admin"],"summary":"Get Pending Tutor Applications","description":"Get tutor applications for admin review","operationId":"get_pending_tutor_applications_admin_tutor_applications_get","parameters":[{"name":"status_filter","in":"query","required":false,"schema":{"type":"string","default":"pending","title":"Status Filter"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/tutor-applications/{profile_id}/approve":{"post":{"tags":["admin","admin"],"summary":"Approve Tutor Application","description":"Approve a tutor application","operationId":"approve_tutor_application_admin_tutor_applications__profile_id__approve_post","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/tutor-applications/{profile_id}/deny":{"post":{"tags":["admin","admin"],"summary":"Deny Tutor Application","description":"Deny a tutor application with a reason","operationId":"deny_tutor_application_admin_tutor_applications__profile_id__deny_post","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}},{"name":"reason","in":"query","required":true,"schema":{"type":"string","title":"Reason"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/tutor-reports":{"get":{"tags":["admin","admin"],"summary":"Get Tutor Reports","description":"Get all tutor reports with optional status filter (admin only)","operationId":"get_tutor_reports_admin_tutor_reports_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status: pending, reviewed, dismissed, action_taken","title":"Status"},"description":"Filter by status: pending, reviewed, dismissed, action_taken"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/tutor-reports/{report_id}":{"get":{"tags":["admin","admin"],"summary":"Get Tutor Report","description":"Get a single tutor report by ID (admin only)","operationId":"get_tutor_report_admin_tutor_reports__report_id__get","parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"string","title":"Report Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["admin","admin"],"summary":"Update Tutor Report","description":"Update tutor report status and resolution (admin only)","operationId":"update_tutor_report_admin_tutor_reports__report_id__patch","parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"string","title":"Report Id"}},{"name":"status_update","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status Update"}},{"name":"resolution_notes","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resolution Notes"}},{"name":"action_taken","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action Taken"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/user-reports":{"get":{"tags":["admin","admin"],"summary":"Get User Reports","description":"Get all user reports with optional status filter (admin only)","operationId":"get_user_reports_admin_user_reports_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status: pending, investigating, resolved, dismissed","title":"Status"},"description":"Filter by status: pending, investigating, resolved, dismissed"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/user-reports/{report_id}":{"get":{"tags":["admin","admin"],"summary":"Get User Report","description":"Get a single user report by ID (admin only)","operationId":"get_user_report_admin_user_reports__report_id__get","parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"string","title":"Report Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["admin","admin"],"summary":"Update User Report","description":"Update user report status and notes (admin only)","operationId":"update_user_report_admin_user_reports__report_id__patch","parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"string","title":"Report Id"}},{"name":"status_update","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status Update"}},{"name":"admin_notes","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Admin Notes"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/all-reports":{"get":{"tags":["admin","admin"],"summary":"Get All Reports","description":"Unified admin inbox over all three report systems.\n\nEach row carries a `report_category` ('content'|'user'|'tutor'), a normalized\n`status` (`pending`|`reviewing`|`resolved`|`dismissed`), and a `subject_user_id`\npointing to the user the admin should be assessing. The FE renders one\nBadActorAssessmentPanel per row regardless of category.","operationId":"get_all_reports_admin_all_reports_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by normalized status: pending/reviewing/resolved/dismissed/all","title":"Status"},"description":"Filter by normalized status: pending/reviewing/resolved/dismissed/all"},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by category: content/user/tutor/all","title":"Category"},"description":"Filter by category: content/user/tutor/all"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/all-reports/{category}/{report_id}":{"patch":{"tags":["admin","admin"],"summary":"Update Unified Report","description":"Cross-category report action.\n\nBody: { status?: normalized, resolution_notes?: str, action_taken?: str }\nThe endpoint dispatches to the underlying source-of-truth row (Report /\nUserReport / TutorReport) and translates the normalized status back to\nthat source's native vocabulary.","operationId":"update_unified_report_admin_all_reports__category___report_id__patch","parameters":[{"name":"category","in":"path","required":true,"schema":{"type":"string","title":"Category"}},{"name":"report_id","in":"path","required":true,"schema":{"type":"string","title":"Report Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/early-access-signups":{"get":{"tags":["admin","admin"],"summary":"Get Early Access Signups","description":"Get all early access signups for Dating and Business features (Admin only).\nReturns a list of signups with email, interest types, payment status, and timestamps.","operationId":"get_early_access_signups_admin_early_access_signups_get","parameters":[{"name":"interest_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by interest type: dating, business, or all","title":"Interest Type"},"description":"Filter by interest type: dating, business, or all"},{"name":"paid_only","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Show only paid signups","default":false,"title":"Paid Only"},"description":"Show only paid signups"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Number of signups to return","default":100,"title":"Limit"},"description":"Number of signups to return"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Number of signups to skip","default":0,"title":"Offset"},"description":"Number of signups to skip"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/user-goals":{"get":{"tags":["admin","admin"],"summary":"Get User Goals","description":"Get list of users with their learning goals, including anonymous users.\nSync (def) so cold-cache fills run in the threadpool, not on the event loop.","operationId":"get_user_goals_admin_user_goals_get","parameters":[{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50000,"minimum":1,"default":100,"title":"Limit"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/dau":{"get":{"tags":["admin","admin"],"summary":"Get Daily Active Users","description":"Get daily active users for the last N days, split into new vs returning.\nSync (def) + 5min cache — the 12-source activity union is heavy, the data\nmoves on day timescales, and the dashboard polls this on every refresh.","operationId":"get_daily_active_users_admin_dau_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":7,"default":30,"title":"Days"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/native-usage":{"get":{"tags":["admin","admin"],"summary":"Get Native App Usage","description":"Native app (Android/iOS) usage counts. Sync (def) + 5min cache.\n\n`registered` = users with an FCM device token (push registration on\nfirst sign-in inside the native app — best proxy we have for \"installed\n+ opened the app\", since Play Console install counts aren't synced\nhere).\n\n`ever_used` / `active_*` come from `user_sessions.client`, set from the\n`X-Client` header that the Capacitor build attaches on every request.","operationId":"get_native_app_usage_admin_native_usage_get","parameters":[{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/mau":{"get":{"tags":["admin","admin"],"summary":"Get Monthly Active Users","description":"Monthly Active Users for the last N calendar months (Eastern time).\nSync (def) + 5min cache.\n\nUses the same 12-source activity union as `/admin/dau`, bucketed by\nmonth instead of day. Splits each month's actives into \"new\" (signed\nup in that calendar month) and \"returning\".","operationId":"get_monthly_active_users_admin_mau_get","parameters":[{"name":"months","in":"query","required":false,"schema":{"type":"integer","maximum":24,"minimum":3,"default":12,"title":"Months"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/school-vs-consumer":{"get":{"tags":["admin","admin"],"summary":"Get School Vs Consumer Stats","description":"Core engagement/retention split: school-affiliated members (an active\nOrganizationMember row on an active Organization — bulk-enrolled by a\ncoordinator via roster import, never chose to sign up the way a consumer\ndid) vs organic consumer signups (no org affiliation). Blending these\ninto one number is what every other admin page currently does — see\ndocs/development/DEAD_CODE_AUDIT... no, see the 2026-09-01 admin\ndashboard audit. Sync (def) + 5min cache, same reasoning as /dau /mau.\n\nRevenue is deliberately NOT reported here as one number: schools are sold\non direct B2B contracts (see CRM), not per-seat Stripe subscriptions —\nOrganization.member_tier is an entitlement, not a price. Consumer revenue\nalready has a home on /admin/business-metrics (Stripe-tier subscription\ncounts); duplicating a dollar figure here under a different definition\nwould be exactly the \"same metric, two disagreeing answers\" pattern this\naudit was asked to avoid. What IS reported here: population, activity,\nand return-rate, split by segment — the numbers Nancy actually asked for.","operationId":"get_school_vs_consumer_stats_admin_school_vs_consumer_get","parameters":[{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/marketing/campaign-attribution":{"get":{"tags":["admin","admin"],"summary":"Get Campaign Attribution","description":"Get campaign attribution data for marketing dashboard","operationId":"get_campaign_attribution_admin_marketing_campaign_attribution_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":90,"title":"Days"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/marketing/funnel":{"get":{"tags":["admin","admin"],"summary":"Get Marketing Funnel","description":"Get full marketing funnel: visitors → onboarding → signup → engaged, split by source","operationId":"get_marketing_funnel_admin_marketing_funnel_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":30,"title":"Days"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/schools":{"get":{"tags":["admin","admin"],"summary":"List Schools For Segment Selector","description":"Just enough to populate the segment selector: id, name, slug.\n\nSelects three columns by name rather than loading the Organization model,\non purpose. Gamma routinely runs a migration or two behind development, and\nan ORM load fails wholesale on a column the database has not got yet — which\nwould empty the school dropdown and take the entire per-school filter with\nit. Three columns that have existed since the table did cannot do that.","operationId":"list_schools_for_segment_selector_admin_schools_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/fix-quota-reset":{"post":{"tags":["admin-fixes"],"summary":"Fix Quota Reset","description":"Fix quota reset issue for all affected paid subscribers.\nAdmin only.","operationId":"fix_quota_reset_admin_fix_quota_reset_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/translate/text":{"post":{"tags":["translation","translation"],"summary":"Translate Text Endpoint","description":"Translate a text passage (used for lesson translation toggle)","operationId":"translate_text_endpoint_translate_text_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextTranslationRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/translate/word":{"post":{"tags":["translation","translation"],"summary":"Translate Single Word","description":"Translate a single word with optional context","operationId":"translate_single_word_translate_word_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WordTranslationRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/translate/tts":{"post":{"tags":["translation","translation"],"summary":"Text To Speech","description":"Generate text-to-speech audio","operationId":"text_to_speech_translate_tts_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TTSRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/translate/tts-preview":{"post":{"tags":["translation","translation"],"summary":"Text To Speech Preview","description":"Generate text-to-speech audio for preview (no authentication required)","operationId":"text_to_speech_preview_translate_tts_preview_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TTSRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/translate/synonyms":{"post":{"tags":["translation","translation"],"summary":"Get Synonyms","description":"Get synonyms for a word in a specific language","operationId":"get_synonyms_translate_synonyms_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SynonymRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SynonymResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/translate/grammatical-info":{"post":{"tags":["translation","translation"],"summary":"Get Grammatical Info","description":"Get grammatical information (gender, part of speech) for a word","operationId":"get_grammatical_info_translate_grammatical_info_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrammaticalInfoRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrammaticalInfoResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/translate/detect-language":{"post":{"tags":["translation","translation"],"summary":"Detect Language","description":"Detect the language of the given text using FastText","operationId":"detect_language_translate_detect_language_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LanguageDetectionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LanguageDetectionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/reading-passages/":{"get":{"tags":["reading","reading"],"summary":"Get Reading Passages","description":"Get reading passages, optionally filtered by language and level","operationId":"get_reading_passages_reading_passages__get","parameters":[{"name":"language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"}},{"name":"level","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Level"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/PassageResponse"}},"title":"Response Get Reading Passages Reading Passages  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/reading-passages/record":{"post":{"tags":["reading","reading"],"summary":"Submit Reading Recording","description":"Submit a reading recording for analysis (non-blocking)","operationId":"submit_reading_recording_reading_passages_record_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingSubmission"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/reading-passages/record-preview":{"post":{"tags":["reading","reading"],"summary":"Submit Reading Recording Preview","description":"Submit a reading recording for analysis (anonymous users in onboarding preview)","operationId":"submit_reading_recording_preview_reading_passages_record_preview_post","parameters":[{"name":"user_id","in":"query","required":false,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordingSubmission"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/reading-passages/record/status/{recording_id}":{"get":{"tags":["reading","reading"],"summary":"Get Recording Status","description":"Poll for recording analysis status","operationId":"get_recording_status_reading_passages_record_status__recording_id__get","parameters":[{"name":"recording_id","in":"path","required":true,"schema":{"type":"string","title":"Recording Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/reading-passages/record/status-preview/{recording_id}":{"get":{"tags":["reading","reading"],"summary":"Get Recording Status Preview","description":"Poll for recording analysis status (preview/anonymous users)","operationId":"get_recording_status_preview_reading_passages_record_status_preview__recording_id__get","parameters":[{"name":"recording_id","in":"path","required":true,"schema":{"type":"string","title":"Recording Id"}},{"name":"user_id","in":"query","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/reading-passages/recordings":{"get":{"tags":["reading","reading"],"summary":"Get User Recordings","description":"Get user's reading recordings","operationId":"get_user_recordings_reading_passages_recordings_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/reading-passages/progress":{"get":{"tags":["reading","reading"],"summary":"Get Reading Progress","description":"Get user's reading progress statistics","operationId":"get_reading_progress_reading_passages_progress_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/reading-passages/recommended":{"get":{"tags":["reading","reading"],"summary":"Get Recommended Passages","description":"Get recommended passages based on user's level","operationId":"get_recommended_passages_reading_passages_recommended_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/reading-passages/reference-audio/{passage_id}":{"get":{"tags":["reading","reading"],"summary":"Get Passage Reference Audio","description":"Get pre-generated reference audio for a reading passage (Chacha's voice).\nThis audio is pre-generated using Google Cloud TTS Chirp 3 HD.","operationId":"get_passage_reference_audio_reading_passages_reference_audio__passage_id__get","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"head":{"tags":["reading","reading"],"summary":"Get Passage Reference Audio","description":"Get pre-generated reference audio for a reading passage (Chacha's voice).\nThis audio is pre-generated using Google Cloud TTS Chirp 3 HD.","operationId":"get_passage_reference_audio_reading_passages_reference_audio__passage_id__get","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/reading-passages/word-pronunciation/{language}/{word}":{"get":{"tags":["reading","reading"],"summary":"Get Word Pronunciation","description":"Get pronunciation audio for a specific word using Chacha's voice.\nThis is generated on-demand and cached for future use.","operationId":"get_word_pronunciation_reading_passages_word_pronunciation__language___word__get","parameters":[{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}},{"name":"word","in":"path","required":true,"schema":{"type":"string","title":"Word"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"head":{"tags":["reading","reading"],"summary":"Get Word Pronunciation","description":"Get pronunciation audio for a specific word using Chacha's voice.\nThis is generated on-demand and cached for future use.","operationId":"get_word_pronunciation_reading_passages_word_pronunciation__language___word__get","parameters":[{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}},{"name":"word","in":"path","required":true,"schema":{"type":"string","title":"Word"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/reading-passages/user-word-pronunciation/{recording_id}/{word}":{"get":{"tags":["reading","reading"],"summary":"Get User Word Pronunciation","description":"Extract and serve a specific word segment from the user's recording.\nUses Google Cloud Speech-to-Text to find word timestamps, then extracts that segment.","operationId":"get_user_word_pronunciation_reading_passages_user_word_pronunciation__recording_id___word__get","parameters":[{"name":"recording_id","in":"path","required":true,"schema":{"type":"string","title":"Recording Id"}},{"name":"word","in":"path","required":true,"schema":{"type":"string","title":"Word"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"head":{"tags":["reading","reading"],"summary":"Get User Word Pronunciation","description":"Extract and serve a specific word segment from the user's recording.\nUses Google Cloud Speech-to-Text to find word timestamps, then extracts that segment.","operationId":"get_user_word_pronunciation_reading_passages_user_word_pronunciation__recording_id___word__get","parameters":[{"name":"recording_id","in":"path","required":true,"schema":{"type":"string","title":"Recording Id"}},{"name":"word","in":"path","required":true,"schema":{"type":"string","title":"Word"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/reading-passages/record/{recording_id}":{"delete":{"tags":["reading","reading"],"summary":"Delete Reading Recording","description":"Delete a reading recording and its audio file.\nCalled when user closes the reading practice modal or navigates away.","operationId":"delete_reading_recording_reading_passages_record__recording_id__delete","parameters":[{"name":"recording_id","in":"path","required":true,"schema":{"type":"string","title":"Recording Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/reading-passages/passages/{passage_id}/sections":{"get":{"tags":["reading","reading"],"summary":"Get Passage Sections","description":"Get sections for a passage (if available).\nLong passages (>120 words) are automatically split into sections.","operationId":"get_passage_sections_reading_passages_passages__passage_id__sections_get","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/reading-passages/passages/{passage_id}/progress":{"get":{"tags":["reading","reading"],"summary":"Get Passage Section Progress","description":"Get user's progress on a sectioned passage.\nReturns which sections have been completed and overall scores.","operationId":"get_passage_section_progress_reading_passages_passages__passage_id__progress_get","parameters":[{"name":"passage_id","in":"path","required":true,"schema":{"type":"string","title":"Passage Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/call-assistance":{"post":{"tags":["call-assistance","learning"],"summary":"Get Call Assistance","description":"Provide real-time assistance during call simulation","operationId":"get_call_assistance_learning_call_assistance_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallAssistanceRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallAssistanceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/call-assistance-audio":{"post":{"tags":["call-assistance","learning"],"summary":"Analyze Call Audio Endpoint","description":"Analyze audio from a call and provide real-time assistance (non-blocking)","operationId":"analyze_call_audio_endpoint_learning_call_assistance_audio_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioCallAssistanceRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/call-assistance-audio/status/{analysis_id}":{"get":{"tags":["call-assistance","learning"],"summary":"Get Call Audio Analysis Status","description":"Poll for call audio analysis status","operationId":"get_call_audio_analysis_status_learning_call_assistance_audio_status__analysis_id__get","parameters":[{"name":"analysis_id","in":"path","required":true,"schema":{"type":"string","title":"Analysis Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/call-assistance/create-session":{"post":{"tags":["call-assistance","learning"],"summary":"Create Call Assistance Session","description":"Create a new live call assistance session with Deepgram","operationId":"create_call_assistance_session_learning_call_assistance_create_session_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_create_call_assistance_session_learning_call_assistance_create_session_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/call-assistance/process-audio":{"post":{"tags":["call-assistance","learning"],"summary":"Process Assistance Audio","description":"Process audio chunk for call assistance","operationId":"process_assistance_audio_learning_call_assistance_process_audio_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_process_assistance_audio_learning_call_assistance_process_audio_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/call-assistance/get-hint":{"post":{"tags":["call-assistance","learning"],"summary":"Get Assistance Hint","description":"Get a hint for what to say next","operationId":"get_assistance_hint_learning_call_assistance_get_hint_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_get_assistance_hint_learning_call_assistance_get_hint_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/call-assistance/end-session":{"post":{"tags":["call-assistance","learning"],"summary":"End Assistance Session","description":"End call assistance session and perform multimodal analysis","operationId":"end_assistance_session_learning_call_assistance_end_session_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_end_assistance_session_learning_call_assistance_end_session_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/call-chacha-chat":{"post":{"tags":["call-assistance","learning"],"summary":"Call Chacha Chat","description":"Chat with ChaCha AI coach during a 1:1 call.","operationId":"call_chacha_chat_learning_call_chacha_chat_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallChaChaRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/learning/call-chacha-silence-suggestions":{"post":{"tags":["call-assistance","learning"],"summary":"Call Chacha Silence Suggestions","description":"Generate contextual response suggestions when silence is detected during a call.","operationId":"call_chacha_silence_suggestions_learning_call_chacha_silence_suggestions_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallChaChasilenceSuggestionsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/feedback/submit":{"post":{"tags":["feedback"],"summary":"Submit Feedback","description":"Submit user feedback to Slack","operationId":"submit_feedback_feedback_submit_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/feedback/ai":{"post":{"tags":["feedback"],"summary":"Submit Ai Feedback","description":"Record a user's verdict on an AI judgment (thumbs / correction / note).","operationId":"submit_ai_feedback_feedback_ai_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiFeedbackIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/feedback/ai/summary":{"get":{"tags":["feedback"],"summary":"Ai Feedback Summary","description":"Admin readout: per-surface up/down counts and the latest comments/corrections.\n\nTeam rows are tagged, not dropped. The ratio is the thing team clicks\ndistort — six of us thumbs-downing a bad ChaCha reply moves a surface's\nscore in a way six users would not — so the aggregate counts only real\nusers, and `team` carries our own counts beside it. But a team member's\nwritten correction is often the most useful row on the page, so every\ncomment still comes back, marked `is_team` for whoever is reading.\n\nThis endpoint never joined users at all, which is why it had no exclusion\nto apply either way (2026-09-01 admin audit).","operationId":"ai_feedback_summary_feedback_ai_summary_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/feedback/wod/summary":{"get":{"tags":["feedback"],"summary":"Wod Feedback Summary","description":"Admin readout of Word of the Day feedback, broken out per activity slot.\n\n/ai/summary collapses the whole Daily Brew into one `wod_activity` row, which\nsays the session was disliked but not which of its six activities did it.\nThis reads the same table and splits on context.slot, so a bad phrase\nexercise is visible without a query.","operationId":"wod_feedback_summary_feedback_wod_summary_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/feedback/churn/summary":{"get":{"tags":["feedback"],"summary":"Churn Feedback Summary","description":"Admin readout: why users cancel subscriptions and unsubscribe from email.\n\nCounts by kind+reason, plus the latest free-text comments.","operationId":"churn_feedback_summary_feedback_churn_summary_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":90,"title":"Days"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/feedback/calls/summary":{"get":{"tags":["feedback"],"summary":"Call Ratings Summary","description":"Admin readout of call_ratings: experience + technical averages, split\nAI calls vs human/group calls, latest comments.","operationId":"call_ratings_summary_feedback_calls_summary_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/feedback/no-shows/summary":{"get":{"tags":["feedback"],"summary":"No Show Summary","description":"Admin readout: reported booking no-shows (who failed to appear).","operationId":"no_show_summary_feedback_no_shows_summary_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":90,"title":"Days"}},{"name":"segment","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Segment"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/breach/report":{"post":{"tags":["data-breach","data-breach"],"summary":"Report Data Breach","description":"Report a potential data breach (can be anonymous)\nThis endpoint is publicly accessible to allow for responsible disclosure","operationId":"report_data_breach_breach_report_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BreachReportRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/breach/incidents":{"get":{"tags":["data-breach","data-breach"],"summary":"List Breach Incidents","description":"Get all breach incidents (admin only)","operationId":"list_breach_incidents_breach_incidents_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"severity","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Severity"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BreachIncidentResponse"},"title":"Response List Breach Incidents Breach Incidents Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/breach/incidents/{breach_id}":{"get":{"tags":["data-breach","data-breach"],"summary":"Get Breach Incident","description":"Get detailed information about a breach incident (admin only)","operationId":"get_breach_incident_breach_incidents__breach_id__get","parameters":[{"name":"breach_id","in":"path","required":true,"schema":{"type":"string","title":"Breach Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BreachIncidentDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/breach/incidents/{breach_id}/assess":{"post":{"tags":["data-breach","data-breach"],"summary":"Assess Breach Risk","description":"Perform risk assessment on a breach incident (admin only)","operationId":"assess_breach_risk_breach_incidents__breach_id__assess_post","parameters":[{"name":"breach_id","in":"path","required":true,"schema":{"type":"string","title":"Breach Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/breach/incidents/{breach_id}/notify-authorities":{"post":{"tags":["data-breach","data-breach"],"summary":"Notify Authorities About Breach","description":"Notify data protection authorities about a breach (admin only)","operationId":"notify_authorities_about_breach_breach_incidents__breach_id__notify_authorities_post","parameters":[{"name":"breach_id","in":"path","required":true,"schema":{"type":"string","title":"Breach Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorityNotificationRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/breach/incidents/{breach_id}/notify-users":{"post":{"tags":["data-breach","data-breach"],"summary":"Notify Affected Users","description":"Notify users affected by a breach (admin only)","operationId":"notify_affected_users_breach_incidents__breach_id__notify_users_post","parameters":[{"name":"breach_id","in":"path","required":true,"schema":{"type":"string","title":"Breach Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserBreachNotificationRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/breach/incidents/{breach_id}/status":{"put":{"tags":["data-breach","data-breach"],"summary":"Update Breach Status","description":"Update the status of a breach incident (admin only)","operationId":"update_breach_status_breach_incidents__breach_id__status_put","parameters":[{"name":"breach_id","in":"path","required":true,"schema":{"type":"string","title":"Breach Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BreachStatusUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/breach/my-notifications":{"get":{"tags":["data-breach","data-breach"],"summary":"Get My Breach Notifications","description":"Get breach notifications for the current user","operationId":"get_my_breach_notifications_breach_my_notifications_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/current":{"get":{"tags":["subscriptions"],"summary":"Get Current Subscription","description":"Get the current user's subscription details.","operationId":"get_current_subscription_subscriptions_current_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/checkout":{"post":{"tags":["subscriptions"],"summary":"Create Checkout Session","description":"Create a Stripe Checkout session for subscription.","operationId":"create_checkout_session_subscriptions_checkout_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCheckoutSessionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCheckoutSessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/upgrade":{"post":{"tags":["subscriptions"],"summary":"Upgrade Subscription","description":"Upgrade or downgrade subscription tier.","operationId":"upgrade_subscription_subscriptions_upgrade_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpgradeSubscriptionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/scheduled-change":{"get":{"tags":["subscriptions"],"summary":"Get Scheduled Tier Change","description":"Get information about any scheduled tier changes.","operationId":"get_scheduled_tier_change_subscriptions_scheduled_change_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/cancel-scheduled-change":{"post":{"tags":["subscriptions"],"summary":"Cancel Scheduled Tier Change","description":"Cancel a scheduled tier change or pending cancellation.","operationId":"cancel_scheduled_tier_change_subscriptions_cancel_scheduled_change_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/cancel":{"post":{"tags":["subscriptions"],"summary":"Cancel Subscription","description":"Cancel the current subscription.","operationId":"cancel_subscription_subscriptions_cancel_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelSubscriptionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/tier-limits":{"get":{"tags":["subscriptions"],"summary":"Get Tier Limit Catalogue","description":"The allowances of every tier the pricing page draws, straight from\nTIER_LIMITS — the table the quota gates enforce.\n\nUnauthenticated on purpose: this is the price list, and the page that shows\nit is public.\n\nIt exists so the comparison table stops restating the numbers in translation\ncopy. Copy drifts — silently, in five languages at once — and a plan page\nthat promises a limit nobody applies is the single worst thing this page can\ndo. `school` is in here because a school seat is NOT \"free plus a bit\": it\ncarries free's ChaCha and premium's transcription, lessons and call length,\nand a student looking at a Free column captioned \"already covered by your\nschool\" is being told something false about what their school bought.\n\n`unlimited` is the sentinel the table uses; a client must render it as a\nword, never as the number.","operationId":"get_tier_limit_catalogue_subscriptions_tier_limits_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/subscriptions/usage":{"get":{"tags":["subscriptions"],"summary":"Get Usage Stats","description":"Get current period usage statistics.","operationId":"get_usage_stats_subscriptions_usage_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageStatsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/payments":{"get":{"tags":["subscriptions"],"summary":"Get Payment History","description":"Get payment history for the current user.","operationId":"get_payment_history_subscriptions_payments_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentHistoryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/tokens/balance":{"get":{"tags":["subscriptions"],"summary":"Get Token Balance","description":"Get native speaker token balance.","operationId":"get_token_balance_subscriptions_tokens_balance_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenBalanceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/tokens/purchase":{"post":{"tags":["subscriptions"],"summary":"Purchase Tokens","description":"Create a Stripe Checkout session for purchasing tokens.","operationId":"purchase_tokens_subscriptions_tokens_purchase_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseTokensRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/tokens/transactions":{"get":{"tags":["subscriptions"],"summary":"Get Token Transactions","description":"Get token transaction history.","operationId":"get_token_transactions_subscriptions_tokens_transactions_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/invite-codes/redeem":{"post":{"tags":["subscriptions"],"summary":"Redeem Invite Code","description":"Redeem an invite code.","operationId":"redeem_invite_code_subscriptions_invite_codes_redeem_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedeemInviteCodeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/webhooks/stripe":{"post":{"tags":["subscriptions"],"summary":"Stripe Webhook","description":"Handle Stripe webhook events.\n\nTRANSACTION STRUCTURE (fixed 2026-09-01 - see play_billing_routes.py's\n_record_event() for the reference pattern this now matches):\n\nEvery branch below uses `db.flush()`, never `db.commit()`. The ONLY\n`db.commit()` in this function is the single one at the very end, right\nafter the idempotency marker (WebhookEvent row) is added - so a webhook's\nbusiness effects (crediting tokens, updating a subscription's tier,\ngranting a signup bonus, ...) and the fact that we consider it \"processed\"\nland in one all-or-nothing transaction. If anything raises partway\nthrough, get_db()'s dependency rolls the whole session back and this\nfunction's own except-block returns 500, so:\n  - NOTHING partial was persisted (flush is visible within the\n    transaction for subsequent queries, but never durable until commit), and\n  - the event was NOT marked processed, so Stripe's automatic retry will\n    reprocess it cleanly - not double-apply it.\nBefore this fix, several branches called `db.commit()` directly, so a\ncrash (or a swallowed exception - checkout.session.completed used to\ncatch-and-log-only) between an early commit and the final marker write\ncould durably apply a business effect (e.g. credit purchased tokens)\nwhile never recording the event as processed, so Stripe's retry would\napply it AGAIN. See the regression tests in\nbackend/tests/unit/routes/test_stripe_webhook_idempotency.py.\n\nTwo shared services this handler calls also commit internally by default\n(`TokenEconomyService.add_bonus_tokens` / `get_or_create_token_account`,\nused elsewhere with their normal commit-immediately behavior) - both now\naccept `commit=False` and this handler passes it, so the signup-bonus\ngrant participates in the same one-transaction-per-event guarantee.\n`cost_tracking_service.track_trial_start`/`track_new_subscription` (called\nfrom the customer.subscription.created branch) still commit internally\nand swallow their own errors; that is a pre-existing, separate gap in a\nshared analytics-metrics helper (not customer-facing money/entitlements -\nworst case on a retry is a daily counter double-incrementing by one) and\nwas judged out of scope for this fix. Flagged here rather than silently\nleft unmentioned.","operationId":"stripe_webhook_subscriptions_webhooks_stripe_post","parameters":[{"name":"Stripe-Signature","in":"header","required":false,"schema":{"type":"string","title":"Stripe-Signature"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/ads/daily-limit":{"get":{"tags":["subscriptions"],"summary":"Get Ad Daily Limit","description":"Remaining rewarded-ad views the user can still watch today.","operationId":"get_ad_daily_limit_subscriptions_ads_daily_limit_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/ads/record-view":{"post":{"tags":["subscriptions"],"summary":"Record Ad View","description":"Record a completed rewarded-ad view and credit the matching daily quota.","operationId":"record_ad_view_subscriptions_ads_record_view_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordAdViewRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/subscriptions/rewards/daily-goals":{"get":{"tags":["subscriptions"],"summary":"Get Daily Goals","description":"Today's earnable daily goals + completion state, for the dashboard widget.\nReward labels live in the frontend catalog (config/rewards.js); we return the\nreward_key so display stays in one place.\n\nEarning is switched off (quota_rewards.EARNING_ENABLED, 2026-08-19), so there\nis nothing to earn and this returns an empty list: the widget and the /rewards\npage both render nothing rather than advertising a reward that never lands.\nThe endpoint itself stays so old clients get an empty list, not a 404.","operationId":"get_daily_goals_subscriptions_rewards_daily_goals_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/signup-flow/configure-trial":{"post":{"tags":["signup-flow","signup-flow"],"summary":"Configure Trial","description":"Configure trial based on user's choice during signup.\n\nThree paths:\n1. standard_trial - 7-day Pro trial with credit card required\n2. invite_code - Extended Pro trial with invite code (no credit card)\n3. skip_trial - Start with Free tier (no credit card)","operationId":"configure_trial_signup_flow_configure_trial_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrialChoiceRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrialChoiceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/signup-flow/trial-status":{"get":{"tags":["signup-flow","signup-flow"],"summary":"Get Trial Status","description":"Get current trial status for the user.","operationId":"get_trial_status_signup_flow_trial_status_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/signup-flow/validate-invite-code":{"post":{"tags":["signup-flow","signup-flow"],"summary":"Validate Invite Code","description":"Validate an invite code without redeeming it.","operationId":"validate_invite_code_signup_flow_validate_invite_code_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Request"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/signup-flow/create-stripe-customer":{"post":{"tags":["signup-flow","signup-flow"],"summary":"Create Stripe Customer","description":"Create a Stripe customer for the user (for standard trial).\nReturns customer ID to be used in frontend Stripe Elements.","operationId":"create_stripe_customer_signup_flow_create_stripe_customer_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/CreateStripeCustomerRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/signup-flow/beta-trial-eligibility":{"get":{"tags":["signup-flow","signup-flow"],"summary":"Check Beta Trial Eligibility","description":"Check if a user is eligible for beta trial.\nBeta users are those who signed up before the trial system was implemented.","operationId":"check_beta_trial_eligibility_signup_flow_beta_trial_eligibility_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/signup-flow/redeem-invite-code":{"post":{"tags":["signup-flow","signup-flow"],"summary":"Redeem Invite Code After Checkout","description":"Redeem an invite code after Stripe Checkout completes.\nThis marks the code as used and records the redemption.\nThe trial subscription is already created by Stripe.","operationId":"redeem_invite_code_after_checkout_signup_flow_redeem_invite_code_post","parameters":[{"name":"invite_code","in":"query","required":true,"schema":{"type":"string","title":"Invite Code"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/activity-tracking/session/start":{"post":{"tags":["activity-tracking"],"summary":"Start Session","description":"Start or resume a user session.\nThis endpoint is public to allow tracking before user authentication.\n\nSync (def, not async def) so FastAPI runs it in its threadpool — body is\nsync DB work (no awaits) and the client doesn't consume the response\nbody, just response.ok. Frees the event loop for call/WS traffic.","operationId":"start_session_activity_tracking_session_start_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionCreateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/activity-tracking/track":{"post":{"tags":["activity-tracking"],"summary":"Track Activity","description":"Track a user activity.\nThis endpoint is public to allow tracking for all users.","operationId":"track_activity_activity_tracking_track_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivityTrackRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/activity-tracking/session/end":{"post":{"tags":["activity-tracking"],"summary":"End Session","description":"End a user session.\nIf the session doesn't exist, returns success (idempotent operation).","operationId":"end_session_activity_tracking_session_end_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionEndRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/activity-tracking/admin/activities/recent":{"get":{"tags":["activity-tracking"],"summary":"Get Recent Activities Admin","description":"Get recent activities (admin only).","operationId":"get_recent_activities_admin_activity_tracking_admin_activities_recent_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"}},{"name":"activity_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Activity Type"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/activity-tracking/admin/analytics":{"get":{"tags":["activity-tracking"],"summary":"Get Activity Analytics Admin","description":"Get activity analytics (admin only).","operationId":"get_activity_analytics_admin_activity_tracking_admin_analytics_get","parameters":[{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Date"}},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End Date"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/activity-tracking/admin/session/{session_id}/replay":{"get":{"tags":["activity-tracking"],"summary":"Get Session Replay Admin","description":"Get session replay data (admin only).","operationId":"get_session_replay_admin_activity_tracking_admin_session__session_id__replay_get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/activity-tracking/signup-click":{"post":{"tags":["activity-tracking"],"summary":"Track Signup Click","description":"Track signup button click.\nThis endpoint is public to allow tracking before authentication.","operationId":"track_signup_click_activity_tracking_signup_click_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupClickTrackingRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/activity-tracking/talknow-click":{"post":{"tags":["activity-tracking"],"summary":"Track Talknow Click","description":"Track talk now button click.\nThis endpoint is public to allow tracking before authentication.","operationId":"track_talknow_click_activity_tracking_talknow_click_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TalkNowClickTrackingRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/activity-tracking/signup-funnel":{"post":{"tags":["activity-tracking"],"summary":"Track Signup Funnel","description":"Track signup funnel events.\nThis endpoint is public to allow tracking before authentication.","operationId":"track_signup_funnel_activity_tracking_signup_funnel_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupFunnelTrackingRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/activity-tracking/onboarding-funnel":{"post":{"tags":["activity-tracking"],"summary":"Track Onboarding Funnel","description":"Track onboarding funnel events.\nThis endpoint is public to allow tracking before authentication.","operationId":"track_onboarding_funnel_activity_tracking_onboarding_funnel_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingFunnelTrackingRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/daily-popup/check":{"get":{"tags":["daily-popup","daily_popup"],"summary":"Check Daily Popup","description":"Check if user should see a popup on login.\nReturns both user recommendations and profile update reminder if applicable.\n\nLogic:\n1. First login: No popup\n2. Second+ login: Show user recommendations (if available) + profile update reminder (if conditions met)\n3. Profile update reminder conditions:\n   - Profile needs update (never updated or >30 days old)\n   - Last profile update reminder was >24 hours ago","operationId":"check_daily_popup_daily_popup_check_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/daily-popup/dismiss":{"post":{"tags":["daily-popup","daily_popup"],"summary":"Dismiss Popup","description":"Mark today's popup as dismissed.","operationId":"dismiss_popup_daily_popup_dismiss_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/daily-popup/interact":{"post":{"tags":["daily-popup","daily_popup"],"summary":"Interact With Popup","description":"Mark that user interacted with today's popup (clicked on a recommendation).","operationId":"interact_with_popup_daily_popup_interact_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/daily-popup/send-icebreaker":{"post":{"tags":["daily-popup","daily_popup"],"summary":"Send Icebreaker Message","description":"Send an icebreaker message to start a chat with a recommended user.\nThe message format: ChaCha asked: <question>. <User> answered: <answer>","operationId":"send_icebreaker_message_daily_popup_send_icebreaker_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IcebreakerAnswerRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/dashboard/overview":{"get":{"tags":["dashboard","dashboard"],"summary":"Get Dashboard Overview","description":"Get comprehensive dashboard overview with all user stats, progress, and recommendations.\nexclude_user_ids: Comma-separated list of user IDs to exclude from matching (passed users)","operationId":"get_dashboard_overview_dashboard_overview_get","parameters":[{"name":"exclude_user_ids","in":"query","required":false,"schema":{"type":"string","title":"Exclude User Ids"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/dashboard/suggested-partner":{"get":{"tags":["dashboard","dashboard"],"summary":"Get Suggested Partner Endpoint","description":"Lightweight single-partner recommendation for the Featured Study Buddy\ncard — avoids fetching the whole /overview payload just to show one match.\nexclude_user_ids: comma-separated ids to skip (the 'See next' button).","operationId":"get_suggested_partner_endpoint_dashboard_suggested_partner_get","parameters":[{"name":"exclude_user_ids","in":"query","required":false,"schema":{"type":"string","title":"Exclude User Ids"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/early-access/signup":{"post":{"tags":["early-access"],"summary":"Create Early Access Signup","description":"Create a free waiting list signup for early access to Dating or Business features.","operationId":"create_early_access_signup_early_access_signup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EarlyAccessSignupRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EarlyAccessSignupResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/early-access/priority-signup":{"post":{"tags":["early-access"],"summary":"Create Priority Signup","description":"Create early access signup with donation for priority consideration.\nThis is a donation to support development, not a purchase.\nFeatures are subject to feasibility. No guarantee of delivery.","operationId":"create_priority_signup_early_access_priority_signup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EarlyAccessPaymentRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EarlyAccessSignupResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/early-access/create-checkout-session":{"post":{"tags":["early-access"],"summary":"Create Checkout Session","description":"Create a Stripe Checkout Session for early access donations.\nDonations support development and provide priority consideration for early access.\nThis is a donation, not a purchase. Features are subject to feasibility.","operationId":"create_checkout_session_early_access_create_checkout_session_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EarlyAccessSignupRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/early-access/webhook":{"post":{"tags":["early-access"],"summary":"Stripe Webhook","description":"Handle Stripe webhook events for early access payments.","operationId":"stripe_webhook_early_access_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/early-access/stats":{"get":{"tags":["early-access"],"summary":"Get Early Access Stats","description":"Get statistics on early access signups (admin only).","operationId":"get_early_access_stats_early_access_stats_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EarlyAccessStatsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/events/rsvp":{"post":{"tags":["events"],"summary":"Create Event Rsvp","description":"Create or update an RSVP for an event.\nIf user already has an RSVP, update it instead of creating a new one.","operationId":"create_event_rsvp_events_rsvp_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventRSVPRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventRSVPResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/events/rsvp-status/{event_id}":{"get":{"tags":["events"],"summary":"Get Event Rsvp Status","description":"Check if the current user has RSVP'd to a specific event.","operationId":"get_event_rsvp_status_events_rsvp_status__event_id__get","parameters":[{"name":"event_id","in":"path","required":true,"schema":{"type":"string","title":"Event Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventRSVPStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/events/rsvp-count/{event_id}":{"get":{"tags":["events"],"summary":"Get Event Rsvp Count","description":"Get the total count of RSVPs for a specific event.\nThis endpoint is public (doesn't require authentication).","operationId":"get_event_rsvp_count_events_rsvp_count__event_id__get","parameters":[{"name":"event_id","in":"path","required":true,"schema":{"type":"string","title":"Event Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventRSVPCountResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/events/rsvp/{event_id}":{"delete":{"tags":["events"],"summary":"Cancel Event Rsvp","description":"Cancel an existing RSVP for an event.","operationId":"cancel_event_rsvp_events_rsvp__event_id__delete","parameters":[{"name":"event_id","in":"path","required":true,"schema":{"type":"string","title":"Event Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/events/community-rsvp-status":{"get":{"tags":["events"],"summary":"Get Community Rsvp Status","description":"Check which community events the current user has RSVP'd to (for the upcoming week).","operationId":"get_community_rsvp_status_events_community_rsvp_status_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/events/community-info":{"get":{"tags":["events"],"summary":"Get Community Info","description":"Public endpoint: next occurrence + RSVP count for each community event.","operationId":"get_community_info_events_community_info_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/events/community-rsvp":{"post":{"tags":["events"],"summary":"Rsvp Community Event","description":"RSVP to a recurring community event and send a calendar invite email.","operationId":"rsvp_community_event_events_community_rsvp_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommunityRSVPRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/events/community-rooms/sync":{"post":{"tags":["events"],"summary":"Sync Community Rooms","description":"Cron-triggered endpoint that ensures the right number of voice chatrooms exist\nfor each community hour that's currently active or about to start.\n\nLogic per event:\n  1. Skip if we're not within the [start - lead, start + duration] window.\n  2. Count confirmed RSVPs for the upcoming/current occurrence.\n  3. Compute desired room count: max(1, ceil(rsvp_count / TARGET_PER_ROOM)).\n  4. Count existing chatrooms already auto-created for this occurrence\n     (matched by activity_config.community_event_id).\n  5. Create the difference, all with activity_type=canasta and the right language.\n\nIdempotent — safe to call repeatedly. Uses activity_config.community_event_id\nas the dedup key so we don't double-spawn within a single occurrence.","operationId":"sync_community_rooms_events_community_rooms_sync_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/events/community-rooms/spawn-overflow/{event_key}":{"post":{"tags":["events"],"summary":"Spawn Overflow Room","description":"Called by the frontend when a user tries to join a community hour room and finds\nthem all full. Spawns an additional overflow room they can join.\n\nRate-limited implicitly by the activity_config.community_event_id check —\nif all existing rooms are full, we add one. If they aren't, we no-op.","operationId":"spawn_overflow_room_events_community_rooms_spawn_overflow__event_key__post","parameters":[{"name":"event_key","in":"path","required":true,"schema":{"type":"string","title":"Event Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/presence/language-counts":{"get":{"tags":["presence"],"summary":"Get Active Users By Language","description":"Get count of active users grouped by language.\n\nReturns:\n    Dictionary with language codes as keys and counts of learners/natives as values.\n\nExample response:\n```json\n{\n    \"languages\": {\n        \"spanish\": {\"learners\": 5, \"natives\": 3},\n        \"english\": {\"learners\": 8, \"natives\": 12}\n    },\n    \"total_users\": 15,\n    \"timestamp\": \"2025-12-09T12:00:00Z\"\n}\n```\n\nNotes:\n- Only shows languages with at least 3 total users (privacy protection)\n- Results are cached for 30 seconds to reduce database load\n- Uses WebSocket heartbeat tracking to determine active users","operationId":"get_active_users_by_language_presence_language_counts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActiveUsersByLanguageResponse"}}}}}}},"/presence/live-snapshot":{"get":{"tags":["presence"],"summary":"Get Live Snapshot","description":"\"Who's practicing now\" snapshot for ConnectHub / Discover.\n\nReturns per-language {learners, natives, teababes} with the AI portion kept\nas its own field (honest hybrid stance — the UI badges TeaBabe counts).\nIncludes languages that have only TeaBabes so the surface never looks dead.","operationId":"get_live_snapshot_presence_live_snapshot_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiveSnapshotResponse"}}}}}}},"/presence/online-list":{"get":{"tags":["presence"],"summary":"Get Online List","description":"Up to 10 people + TeaBabes available for a language right now (for the\npracticing-now hover). Each: {id, full_name, profile_picture, is_ai}.\n\nAuth required: this returns real user identities (id + name + photo) and was\npreviously unauthenticated, i.e. a public who's-online roster of the member\nbase. Its only caller (PracticingNowStrip, inside ConnectHub) is a logged-in\nsurface. The aggregate sibling endpoints (/language-counts, /live-snapshot,\n/teababes) stay public — they return counts and AI personas, not people.","operationId":"get_online_list_presence_online_list_get","parameters":[{"name":"language","in":"query","required":false,"schema":{"type":"string","title":"Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/presence/teababes":{"get":{"tags":["presence"],"summary":"Get Teababe Cards","description":"TeaBabe (AI persona) cards for the Discover surface, optionally filtered by\nlanguage (ISO code or name). Always badged AI client-side.","operationId":"get_teababe_cards_presence_teababes_get","parameters":[{"name":"language","in":"query","required":false,"schema":{"type":"string","title":"Language"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/presence/heartbeat":{"post":{"tags":["presence"],"summary":"Update Presence","description":"Update user's presence heartbeat.\n\nThis endpoint should be called periodically (e.g., every 30 seconds) by the frontend\nto indicate the user is still active.\n\nNote: This is a placeholder - actual heartbeat is handled via WebSocket connections.","operationId":"update_presence_presence_heartbeat_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/tutor/become-tutor/draft":{"post":{"tags":["tutor"],"summary":"Save Tutor Draft","description":"Save tutor application draft for later completion.\nAllows users to save progress and come back later.","operationId":"save_tutor_draft_tutor_become_tutor_draft_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Draft Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["tutor"],"summary":"Get Tutor Draft","description":"Retrieve saved tutor application draft.\nReturns None if no draft exists.","operationId":"get_tutor_draft_tutor_become_tutor_draft_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["tutor"],"summary":"Delete Tutor Draft","description":"Delete saved tutor application draft.\nCalled after successful application submission.","operationId":"delete_tutor_draft_tutor_become_tutor_draft_delete","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/waitlist":{"post":{"tags":["tutor"],"summary":"Join Tutor Waitlist","description":"Public: add an interested tutor to the waitlist while intake is paused.\n\nReplaces the old dead-end \"applications closed\" page. Deduped by email — a\nrepeat submit refreshes the existing row instead of erroring. user_id is set\nonly if the submitter is logged in.","operationId":"join_tutor_waitlist_tutor_waitlist_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TutorWaitlistRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/become-tutor":{"post":{"tags":["tutor"],"summary":"Become Tutor","description":"Convert a student account to a free tutor account.\nCreates a tutor profile and updates the user's account_type.\nAlso handles re-applications for denied tutors.","operationId":"become_tutor_tutor_become_tutor_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BecomeTutorRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/upgrade-to-pro":{"post":{"tags":["tutor"],"summary":"Upgrade To Pro Tutor","description":"Upgrade from Free Tutor to Pro Tutor.\nThis endpoint prepares the account for Pro Tutor subscription.\nActual payment is handled through Stripe checkout.","operationId":"upgrade_to_pro_tutor_tutor_upgrade_to_pro_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/confirm-pro-upgrade":{"post":{"tags":["tutor"],"summary":"Confirm Pro Tutor Upgrade","description":"Confirm Pro Tutor upgrade after successful Stripe payment.\nCalled by the frontend after successful checkout.\nThis updates the account_type and enables featured status.","operationId":"confirm_pro_tutor_upgrade_tutor_confirm_pro_upgrade_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/profile":{"get":{"tags":["tutor"],"summary":"Get Own Tutor Profile","description":"Get the current user's tutor profile.","operationId":"get_own_tutor_profile_tutor_profile_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["tutor"],"summary":"Update Tutor Profile","description":"Update the current user's tutor profile.","operationId":"update_tutor_profile_tutor_profile_put","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTutorProfileRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/upload-intro-video":{"post":{"tags":["tutor"],"summary":"Upload Intro Video","description":"Upload an introduction video for tutor profile.\nSupports local storage and cloud storage (Cloudinary, S3).\nMaximum file size: 100MB\nAllowed formats: mp4, webm, mov, avi","operationId":"upload_intro_video_tutor_upload_intro_video_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_intro_video_tutor_upload_intro_video_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/profile/visibility":{"put":{"tags":["tutor"],"summary":"Toggle Tutor Visibility","description":"Toggle tutor profile visibility in the directory.\nTutors can hide their profile temporarily without deleting their account.","operationId":"toggle_tutor_visibility_tutor_profile_visibility_put","parameters":[{"name":"is_visible","in":"query","required":true,"schema":{"type":"boolean","title":"Is Visible"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/packages":{"get":{"tags":["tutor"],"summary":"Get Tutor Packages","description":"Get all packages for the current tutor.","operationId":"get_tutor_packages_tutor_packages_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TutorPackageResponse"},"title":"Response Get Tutor Packages Tutor Packages Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["tutor"],"summary":"Create Package","description":"Create a new pricing package.","operationId":"create_package_tutor_packages_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePackageRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TutorPackageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/packages/{package_id}":{"put":{"tags":["tutor"],"summary":"Update Package","description":"Update an existing package.","operationId":"update_package_tutor_packages__package_id__put","parameters":[{"name":"package_id","in":"path","required":true,"schema":{"type":"string","title":"Package Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePackageRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["tutor"],"summary":"Delete Package","description":"Delete a package.","operationId":"delete_package_tutor_packages__package_id__delete","parameters":[{"name":"package_id","in":"path","required":true,"schema":{"type":"string","title":"Package Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/saved/{tutor_id}":{"post":{"tags":["tutor"],"summary":"Save Tutor","description":"Save/favorite a tutor (idempotent).","operationId":"save_tutor_tutor_saved__tutor_id__post","parameters":[{"name":"tutor_id","in":"path","required":true,"schema":{"type":"string","title":"Tutor Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["tutor"],"summary":"Unsave Tutor","description":"Remove a saved tutor (idempotent).","operationId":"unsave_tutor_tutor_saved__tutor_id__delete","parameters":[{"name":"tutor_id","in":"path","required":true,"schema":{"type":"string","title":"Tutor Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/saved":{"get":{"tags":["tutor"],"summary":"Get Saved Tutors","description":"Get list of saved tutor IDs for the current user.","operationId":"get_saved_tutors_tutor_saved_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/directory":{"get":{"tags":["tutor"],"summary":"List Tutors","description":"List all verified tutors in the directory.\n\nUses track_user_activity_optional to update the viewer's activity (if logged in)\nand get_fresh_db to ensure fresh data (same as /users/discover endpoint for accurate online status).","operationId":"list_tutors_tutor_directory_get","parameters":[{"name":"language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by single language taught","title":"Language"},"description":"Filter by single language taught"},{"name":"languages","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by multiple languages (comma-separated)","title":"Languages"},"description":"Filter by multiple languages (comma-separated)"},{"name":"timezone","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by timezone","title":"Timezone"},"description":"Filter by timezone"},{"name":"min_rating","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":5,"minimum":0},{"type":"null"}],"description":"Minimum rating","title":"Min Rating"},"description":"Minimum rating"},{"name":"sort_by","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Sort by: rating, reviews, featured","default":"rating","title":"Sort By"},"description":"Sort by: rating, reviews, featured"},{"name":"school_only","in":"query","required":false,"schema":{"type":"boolean","description":"School Spaces: only tutors affiliated with the viewer's organization(s)","default":false,"title":"School Only"},"description":"School Spaces: only tutors affiliated with the viewer's organization(s)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TutorDirectoryResponse"},"title":"Response List Tutors Tutor Directory Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/directory/featured":{"get":{"tags":["tutor"],"summary":"List Featured Tutors","description":"Get featured tutors (Pro Tutors only) with personalized weighted randomization.","operationId":"list_featured_tutors_tutor_directory_featured_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"default":10,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TutorDirectoryResponse"},"title":"Response List Featured Tutors Tutor Directory Featured Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/directory/{tutor_id}":{"get":{"tags":["tutor"],"summary":"Get Tutor Profile","description":"Get full tutor profile by ID (public endpoint for viewing tutors).","operationId":"get_tutor_profile_tutor_directory__tutor_id__get","parameters":[{"name":"tutor_id","in":"path","required":true,"schema":{"type":"string","title":"Tutor Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/directory/{tutor_id}/reviews":{"get":{"tags":["tutor"],"summary":"Get Tutor Reviews","description":"Get public reviews for a tutor.","operationId":"get_tutor_reviews_tutor_directory__tutor_id__reviews_get","parameters":[{"name":"tutor_id","in":"path","required":true,"schema":{"type":"string","title":"Tutor Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"default":10,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TutorReviewResponse"},"title":"Response Get Tutor Reviews Tutor Directory  Tutor Id  Reviews Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["tutor"],"summary":"Submit Review","description":"Submit a review for a tutor.","operationId":"submit_review_tutor_directory__tutor_id__reviews_post","parameters":[{"name":"tutor_id","in":"path","required":true,"schema":{"type":"string","title":"Tutor Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitReviewRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/directory/{tutor_id}/report":{"post":{"tags":["tutor"],"summary":"Report Tutor","description":"Report a tutor for safety/conduct issues.\nReasons: harassment, inappropriate_content, spam, scam, other","operationId":"report_tutor_tutor_directory__tutor_id__report_post","parameters":[{"name":"tutor_id","in":"path","required":true,"schema":{"type":"string","title":"Tutor Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportTutorRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/directory/{tutor_id}/availability":{"get":{"tags":["tutor"],"summary":"Get Tutor Availability","description":"Get tutor's availability for a date range.\nReturns blocked times, booked slots, and available slots.","operationId":"get_tutor_availability_tutor_directory__tutor_id__availability_get","parameters":[{"name":"tutor_id","in":"path","required":true,"schema":{"type":"string","title":"Tutor Id"}},{"name":"start_date","in":"query","required":true,"schema":{"type":"string","format":"date-time","description":"Start date for availability check","title":"Start Date"},"description":"Start date for availability check"},{"name":"end_date","in":"query","required":true,"schema":{"type":"string","format":"date-time","description":"End date for availability check","title":"End Date"},"description":"End date for availability check"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TutorAvailabilityResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/students":{"get":{"tags":["tutor"],"summary":"Get My Students","description":"Get all students for the current tutor.\nOnly includes students who have accepted the invitation.","operationId":"get_my_students_tutor_students_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/StudentProgressResponse"},"title":"Response Get My Students Tutor Students Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["tutor"],"summary":"Invite Student","description":"Send an invitation to a student to become your student.\nCreates a 'pending' relationship that requires student acceptance.","operationId":"invite_student_tutor_students_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddStudentRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/tutor-requests":{"post":{"tags":["tutor"],"summary":"Request Tutor","description":"Student requests to connect with a tutor.\nCreates a 'pending' relationship that requires tutor acceptance.","operationId":"request_tutor_tutor_tutor_requests_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestTutorRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/students/{student_id}/invitation":{"delete":{"tags":["tutor"],"summary":"Cancel Student Invitation","description":"Cancel a pending student invitation sent by a tutor.\nOnly the tutor who sent the invitation can cancel it.","operationId":"cancel_student_invitation_tutor_students__student_id__invitation_delete","parameters":[{"name":"student_id","in":"path","required":true,"schema":{"type":"string","title":"Student Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/tutor-requests/{tutor_id}":{"delete":{"tags":["tutor"],"summary":"Cancel Tutor Request","description":"Cancel a pending tutor request sent by a student.\nOnly the student who sent the request can cancel it.","operationId":"cancel_tutor_request_tutor_tutor_requests__tutor_id__delete","parameters":[{"name":"tutor_id","in":"path","required":true,"schema":{"type":"string","title":"Tutor Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/disconnect/{user_id}":{"post":{"tags":["tutor"],"summary":"Disconnect Tutor Student","description":"Disconnect from a tutor-student relationship.\nEither the tutor or student can disconnect.\nThis sets status to 'disconnected' without blocking - they can reconnect later.","operationId":"disconnect_tutor_student_tutor_disconnect__user_id__post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/requests/received":{"get":{"tags":["tutor"],"summary":"Get Student Requests","description":"Get all pending student requests for this tutor.","operationId":"get_student_requests_tutor_requests_received_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/requests/{request_id}/accept":{"post":{"tags":["tutor"],"summary":"Accept Student Request","description":"Tutor accepts a student's connection request.","operationId":"accept_student_request_tutor_requests__request_id__accept_post","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/requests/{request_id}/decline":{"post":{"tags":["tutor"],"summary":"Decline Student Request","description":"Tutor declines a student's connection request.","operationId":"decline_student_request_tutor_requests__request_id__decline_post","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/students/{student_id}/progress":{"get":{"tags":["tutor"],"summary":"Get Student Progress","description":"Get progress for a specific student.","operationId":"get_student_progress_tutor_students__student_id__progress_get","parameters":[{"name":"student_id","in":"path","required":true,"schema":{"type":"string","title":"Student Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudentProgressResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/students/{student_id}/notes":{"put":{"tags":["tutor"],"summary":"Update Student Notes","description":"Update private notes for a student (Pro Tutors only).","operationId":"update_student_notes_tutor_students__student_id__notes_put","parameters":[{"name":"student_id","in":"path","required":true,"schema":{"type":"string","title":"Student Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStudentNotesRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/students/{student_id}/calls":{"get":{"tags":["tutor"],"summary":"Get Student Call History","description":"Get call history with a specific student.","operationId":"get_student_call_history_tutor_students__student_id__calls_get","parameters":[{"name":"student_id","in":"path","required":true,"schema":{"type":"string","title":"Student Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"default":10,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/flashcards/create-deck":{"post":{"tags":["tutor"],"summary":"Create Empty Flashcard Deck","description":"Create an empty flashcard deck for tutors to share with students.","operationId":"create_empty_flashcard_deck_tutor_flashcards_create_deck_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDeckRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/flashcards/share":{"post":{"tags":["tutor"],"summary":"Share Flashcard Deck","description":"Share a flashcard deck with students.","operationId":"share_flashcard_deck_tutor_flashcards_share_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareFlashcardDeckRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/flashcards/unshare/{deck_id}/{student_id}":{"delete":{"tags":["tutor"],"summary":"Unshare Flashcard Deck","description":"Remove a student's access to a shared deck.","operationId":"unshare_flashcard_deck_tutor_flashcards_unshare__deck_id___student_id__delete","parameters":[{"name":"deck_id","in":"path","required":true,"schema":{"type":"string","title":"Deck Id"}},{"name":"student_id","in":"path","required":true,"schema":{"type":"string","title":"Student Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/flashcards/shared":{"get":{"tags":["tutor"],"summary":"Get Shared Decks","description":"Get all decks shared by this tutor.","operationId":"get_shared_decks_tutor_flashcards_shared_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SharedDeckResponse"},"title":"Response Get Shared Decks Tutor Flashcards Shared Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/students/{student_id}/study-deck":{"get":{"tags":["tutor"],"summary":"Get Student Study Deck","description":"Get a student's Study Deck so tutor can view and suggest cards.","operationId":"get_student_study_deck_tutor_students__student_id__study_deck_get","parameters":[{"name":"student_id","in":"path","required":true,"schema":{"type":"string","title":"Student Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/contact-limit":{"get":{"tags":["tutor"],"summary":"Check Tutor Contact Limit","description":"Check if user can contact tutors and how many contacts remaining this month.\nFree users: 3 contacts per month\nPremium/Pro/Ultimate/Tutors: Unlimited","operationId":"check_tutor_contact_limit_tutor_contact_limit_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/track-contact/{tutor_id}":{"post":{"tags":["tutor"],"summary":"Track Tutor Contact","description":"Track when a user contacts a tutor.\nReturns success if contact is allowed, error if limit reached.","operationId":"track_tutor_contact_tutor_track_contact__tutor_id__post","parameters":[{"name":"tutor_id","in":"path","required":true,"schema":{"type":"string","title":"Tutor Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/my-tutors":{"get":{"tags":["tutor"],"summary":"Get My Tutors","description":"Get all tutors this student is connected with.","operationId":"get_my_tutors_tutor_my_tutors_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/shared-decks":{"get":{"tags":["tutor"],"summary":"Get Decks Shared With Me","description":"Get all flashcard decks shared with this student by tutors.","operationId":"get_decks_shared_with_me_tutor_shared_decks_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/activity-feed":{"get":{"tags":["tutor"],"summary":"Get Activity Feed","description":"Get recent activity feed for tutor dashboard.\nCombines: messages received, completed sessions, new students.","operationId":"get_activity_feed_tutor_activity_feed_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ActivityFeedItem"},"title":"Response Get Activity Feed Tutor Activity Feed Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/calendar.ics":{"get":{"tags":["tutor"],"summary":"Get Calendar Feed","description":"Return an iCal feed of all upcoming bookings for this tutor.\n\nTutors can subscribe to this URL in Google Calendar / Apple Calendar\nto keep their teaching schedule synced automatically.","operationId":"get_calendar_feed_tutor_calendar_ics_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/stats/period":{"get":{"tags":["tutor"],"summary":"Get Stats By Period","description":"Get tutor statistics for a specific time period.\nPeriod options: week, month, all","operationId":"get_stats_by_period_tutor_stats_period_get","parameters":[{"name":"period","in":"query","required":false,"schema":{"type":"string","pattern":"^(week|month|all)$","default":"month","title":"Period"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TutorStatsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/analytics":{"get":{"tags":["tutor"],"summary":"Get Tutor Analytics","description":"Business analytics for tutors: trial conversion, response rate, booking stats.","operationId":"get_tutor_analytics_tutor_analytics_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/my-invitations":{"get":{"tags":["tutor"],"summary":"Get My Tutor Invitations","description":"Get pending tutor invitations for the current student.\nOptimized with eager loading to avoid N+1 queries.","operationId":"get_my_tutor_invitations_tutor_my_invitations_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TutorInvitationResponse"},"title":"Response Get My Tutor Invitations Tutor My Invitations Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/invitations/{invitation_id}/accept":{"post":{"tags":["tutor"],"summary":"Accept Tutor Invitation","description":"Accept a tutor invitation.\nGrants the tutor access to student's learning progress and flashcards.","operationId":"accept_tutor_invitation_tutor_invitations__invitation_id__accept_post","parameters":[{"name":"invitation_id","in":"path","required":true,"schema":{"type":"string","title":"Invitation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/invitations/{invitation_id}/decline":{"post":{"tags":["tutor"],"summary":"Decline Tutor Invitation","description":"Decline a tutor invitation.\nRemoves the pending invitation.","operationId":"decline_tutor_invitation_tutor_invitations__invitation_id__decline_post","parameters":[{"name":"invitation_id","in":"path","required":true,"schema":{"type":"string","title":"Invitation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/disconnect-tutor/{tutor_id}":{"delete":{"tags":["tutor"],"summary":"Disconnect From Tutor","description":"Disconnect from a tutor, revoking their access to progress data.\nTutor keeps their private notes about the student.","operationId":"disconnect_from_tutor_tutor_disconnect_tutor__tutor_id__delete","parameters":[{"name":"tutor_id","in":"path","required":true,"schema":{"type":"string","title":"Tutor Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/badges/{tutor_id}":{"get":{"tags":["tutor"],"summary":"Get Tutor Badges","description":"Get all badges for a tutor.\nPublic endpoint - anyone can see tutor badges.","operationId":"get_tutor_badges_tutor_badges__tutor_id__get","parameters":[{"name":"tutor_id","in":"path","required":true,"schema":{"type":"string","title":"Tutor Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BadgeResponse"},"title":"Response Get Tutor Badges Tutor Badges  Tutor Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/recalculate-badges":{"post":{"tags":["tutor"],"summary":"Recalculate My Badges","description":"Recalculate badges for the current tutor.\nOnly available to tutor accounts.","operationId":"recalculate_my_badges_tutor_recalculate_badges_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/block-student/{student_id}":{"post":{"tags":["tutor"],"summary":"Block Student","description":"Block a student from booking or messaging.\nOnly tutors can block students. Blocked students cannot leave reviews.","operationId":"block_student_tutor_block_student__student_id__post","parameters":[{"name":"student_id","in":"path","required":true,"schema":{"type":"string","title":"Student Id"}},{"name":"reason","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/unblock-student/{student_id}":{"post":{"tags":["tutor"],"summary":"Unblock Student","description":"Unblock a previously blocked student.","operationId":"unblock_student_tutor_unblock_student__student_id__post","parameters":[{"name":"student_id","in":"path","required":true,"schema":{"type":"string","title":"Student Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/blocked-students":{"get":{"tags":["tutor"],"summary":"Get Blocked Students","description":"Get list of students blocked by the current tutor.","operationId":"get_blocked_students_tutor_blocked_students_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/students/{student_id}/goals":{"get":{"tags":["tutor"],"summary":"Get Student Goals","description":"Get learning goals for a specific student.\nBoth tutors and students can view goals.","operationId":"get_student_goals_tutor_students__student_id__goals_get","parameters":[{"name":"student_id","in":"path","required":true,"schema":{"type":"string","title":"Student Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LearningGoalsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["tutor"],"summary":"Update Student Goals","description":"Update learning goals for a specific student.\nOnly tutors can update goals.","operationId":"update_student_goals_tutor_students__student_id__goals_put","parameters":[{"name":"student_id","in":"path","required":true,"schema":{"type":"string","title":"Student Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LearningGoalsUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LearningGoalsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/blocked-times":{"get":{"tags":["tutor"],"summary":"Get My Blocked Times","description":"Get all blocked time slots for the current tutor.","operationId":"get_my_blocked_times_tutor_blocked_times_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BlockedTimeResponse"},"title":"Response Get My Blocked Times Tutor Blocked Times Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["tutor"],"summary":"Create Blocked Time","description":"Create a new blocked time slot.","operationId":"create_blocked_time_tutor_blocked_times_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlockedTimeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlockedTimeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/{tutor_id}/blocked-times":{"get":{"tags":["tutor"],"summary":"Get Tutor Blocked Times","description":"Get blocked time slots for a specific tutor (public endpoint for students).","operationId":"get_tutor_blocked_times_tutor__tutor_id__blocked_times_get","parameters":[{"name":"tutor_id","in":"path","required":true,"schema":{"type":"string","title":"Tutor Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BlockedTimeResponse"},"title":"Response Get Tutor Blocked Times Tutor  Tutor Id  Blocked Times Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/blocked-times/{blocked_time_id}":{"put":{"tags":["tutor"],"summary":"Update Blocked Time","description":"Update an existing blocked time slot.","operationId":"update_blocked_time_tutor_blocked_times__blocked_time_id__put","parameters":[{"name":"blocked_time_id","in":"path","required":true,"schema":{"type":"string","title":"Blocked Time Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlockedTimeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlockedTimeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["tutor"],"summary":"Delete Blocked Time","description":"Delete a blocked time slot.","operationId":"delete_blocked_time_tutor_blocked_times__blocked_time_id__delete","parameters":[{"name":"blocked_time_id","in":"path","required":true,"schema":{"type":"string","title":"Blocked Time Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/students/{student_id}/analytics/generate":{"post":{"tags":["tutor"],"summary":"Generate Student Analytics","description":"Generate AI-powered analytics report for a student (Pro Tutors only).\nUses Gemini Flash and tracks AI usage against tutor's account.","operationId":"generate_student_analytics_tutor_students__student_id__analytics_generate_post","parameters":[{"name":"student_id","in":"path","required":true,"schema":{"type":"string","title":"Student Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateAnalyticsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyticsReportResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/students/{student_id}/analytics":{"get":{"tags":["tutor"],"summary":"Get Student Analytics Reports","description":"Get all analytics reports for a student (Pro Tutors only).","operationId":"get_student_analytics_reports_tutor_students__student_id__analytics_get","parameters":[{"name":"student_id","in":"path","required":true,"schema":{"type":"string","title":"Student Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AnalyticsReportResponse"},"title":"Response Get Student Analytics Reports Tutor Students  Student Id  Analytics Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/students/{student_id}/analytics/{report_id}":{"get":{"tags":["tutor"],"summary":"Get Analytics Report","description":"Get a specific analytics report (Pro Tutors only).","operationId":"get_analytics_report_tutor_students__student_id__analytics__report_id__get","parameters":[{"name":"student_id","in":"path","required":true,"schema":{"type":"string","title":"Student Id"}},{"name":"report_id","in":"path","required":true,"schema":{"type":"string","title":"Report Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyticsReportResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/students/{student_id}/flashcards/{deck_id}/suggest":{"post":{"tags":["tutor"],"summary":"Suggest Flashcard Edit","description":"Suggest an edit, addition, or removal of a flashcard (Pro Tutors only).","operationId":"suggest_flashcard_edit_tutor_students__student_id__flashcards__deck_id__suggest_post","parameters":[{"name":"student_id","in":"path","required":true,"schema":{"type":"string","title":"Student Id"}},{"name":"deck_id","in":"path","required":true,"schema":{"type":"string","title":"Deck Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlashcardSuggestionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlashcardSuggestionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/students/{student_id}/flashcard-suggestions":{"get":{"tags":["tutor"],"summary":"Get Flashcard Suggestions","description":"Get all flashcard suggestions for a student (Pro Tutors only).","operationId":"get_flashcard_suggestions_tutor_students__student_id__flashcard_suggestions_get","parameters":[{"name":"student_id","in":"path","required":true,"schema":{"type":"string","title":"Student Id"}},{"name":"status_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(pending|approved|rejected)$"},{"type":"null"}],"title":"Status Filter"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FlashcardSuggestionResponse"},"title":"Response Get Flashcard Suggestions Tutor Students  Student Id  Flashcard Suggestions Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/flashcard-suggestions/{suggestion_id}":{"delete":{"tags":["tutor"],"summary":"Cancel Flashcard Suggestion","description":"Cancel a pending flashcard suggestion (Pro Tutors only).","operationId":"cancel_flashcard_suggestion_tutor_flashcard_suggestions__suggestion_id__delete","parameters":[{"name":"suggestion_id","in":"path","required":true,"schema":{"type":"string","title":"Suggestion Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tutor/reactivate":{"post":{"tags":["tutor"],"summary":"Reactivate Tutor","description":"Reactivate a deactivated tutor account.","operationId":"reactivate_tutor_tutor_reactivate_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bookings/bookings":{"post":{"tags":["bookings"],"summary":"Create Booking","description":"Create a new class booking.\nStudents book classes with tutors. Tutors can also create bookings on behalf of students.","operationId":"create_booking_bookings_bookings_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBookingRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["bookings"],"summary":"List Bookings","description":"List all bookings for the current user (as student or tutor).","operationId":"list_bookings_bookings_bookings_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status","title":"Status"},"description":"Filter by status"},{"name":"upcoming","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Only upcoming bookings","title":"Upcoming"},"description":"Only upcoming bookings"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"default":50,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BookingResponse"},"title":"Response List Bookings Bookings Bookings Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bookings/bookings/{booking_id}/confirm":{"post":{"tags":["bookings"],"summary":"Confirm Booking","description":"Confirm a pending booking (tutor only). Optionally add notes.","operationId":"confirm_booking_bookings_bookings__booking_id__confirm_post","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmBookingRequest","default":{}}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bookings/bookings/{booking_id}/join-room":{"post":{"tags":["bookings"],"summary":"Join Booking Room","description":"Join the scheduled room for a confirmed booking.\nReturns a LiveKit token. Available 15 min before and during the session.","operationId":"join_booking_room_bookings_bookings__booking_id__join_room_post","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bookings/bookings/{booking_id}/reject":{"post":{"tags":["bookings"],"summary":"Reject Booking","description":"Reject a pending booking (tutor only).","operationId":"reject_booking_bookings_bookings__booking_id__reject_post","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RejectBookingRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bookings/bookings/next":{"get":{"tags":["bookings"],"summary":"Get Next Booking","description":"Get the next upcoming booking for the current user.\nReturns pending or confirmed bookings (not cancelled or completed).","operationId":"get_next_booking_bookings_bookings_next_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/BookingResponse"},{"type":"null"}],"title":"Response Get Next Booking Bookings Bookings Next Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bookings/bookings/{booking_id}":{"get":{"tags":["bookings"],"summary":"Get Booking","description":"Get details of a specific booking.","operationId":"get_booking_bookings_bookings__booking_id__get","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["bookings"],"summary":"Update Booking","description":"Update a booking (reschedule or update notes).\nBoth tutor and student can reschedule, subject to cancellation policy.","operationId":"update_booking_bookings_bookings__booking_id__put","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBookingRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bookings/bookings/{booking_id}/cancel":{"post":{"tags":["bookings"],"summary":"Cancel Booking","description":"Cancel a booking.\nPlatform does NOT process refunds - students must contact tutor directly.","operationId":"cancel_booking_bookings_bookings__booking_id__cancel_post","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelBookingRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bookings/bookings/{booking_id}/complete":{"post":{"tags":["bookings"],"summary":"Mark Booking Completed","description":"Mark a booking as completed.\nTypically called after a video call session ends.","operationId":"mark_booking_completed_bookings_bookings__booking_id__complete_post","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}},{"name":"call_session_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Call Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bookings/bookings/{booking_id}/no-show":{"post":{"tags":["bookings"],"summary":"Report No Show","description":"Report that the other party did not show up for a confirmed booking.\n\nTutor reporting -> no_show_student; student reporting -> no_show_tutor.\nOnly after the scheduled time has passed, and only from 'confirmed'\n(a completed class cannot retroactively become a no-show).","operationId":"report_no_show_bookings_bookings__booking_id__no_show_post","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bookings/bookings/{booking_id}/resend-invite":{"post":{"tags":["bookings"],"summary":"Resend Booking Invite","description":"Resend calendar invite email for a confirmed booking.","operationId":"resend_booking_invite_bookings_bookings__booking_id__resend_invite_post","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/bookings/bookings/{booking_id}/cancel-recurring":{"post":{"tags":["bookings"],"summary":"Cancel Recurring Bookings","description":"Cancel all future bookings in a recurring series.","operationId":"cancel_recurring_bookings_bookings_bookings__booking_id__cancel_recurring_post","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelBookingRequest","default":{}}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/homework/generate":{"post":{"tags":["homework"],"summary":"Generate Homework","description":"Generate AI-powered homework assignment for a student.\nOnly tutors can generate homework.","operationId":"generate_homework_homework_generate_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HomeworkGenerateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HomeworkResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/homework/student/{student_id}":{"get":{"tags":["homework"],"summary":"Get Student Homework","description":"Get all homework assignments for a specific student.\nTutors can see homework they assigned.\nStudents can see their own homework.","operationId":"get_student_homework_homework_student__student_id__get","parameters":[{"name":"student_id","in":"path","required":true,"schema":{"type":"string","title":"Student Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/HomeworkResponse"},"title":"Response Get Student Homework Homework Student  Student Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/homework/{homework_id}":{"get":{"tags":["homework"],"summary":"Get Homework","description":"Get a specific homework assignment.","operationId":"get_homework_homework__homework_id__get","parameters":[{"name":"homework_id","in":"path","required":true,"schema":{"type":"string","title":"Homework Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HomeworkResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/homework/{homework_id}/submit":{"post":{"tags":["homework"],"summary":"Submit Homework","description":"Submit completed homework.\nOnly the assigned student can submit.","operationId":"submit_homework_homework__homework_id__submit_post","parameters":[{"name":"homework_id","in":"path","required":true,"schema":{"type":"string","title":"Homework Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HomeworkSubmission"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/homework/{homework_id}/feedback":{"post":{"tags":["homework"],"summary":"Provide Feedback","description":"Provide feedback on submitted homework.\nOnly the tutor who assigned it can provide feedback.","operationId":"provide_feedback_homework__homework_id__feedback_post","parameters":[{"name":"homework_id","in":"path","required":true,"schema":{"type":"string","title":"Homework Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HomeworkFeedback"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/teaching-feedback/generate/{booking_id}":{"post":{"tags":["teaching-feedback","teaching-feedback"],"summary":"Generate Lesson Feedback","description":"Generate AI-powered teaching feedback for a completed lesson.\nOnly tutors can generate feedback for their own lessons.","operationId":"generate_lesson_feedback_teaching_feedback_generate__booking_id__post","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackGenerateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeachingFeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/teaching-feedback/generate-from-call/{call_session_id}":{"post":{"tags":["teaching-feedback","teaching-feedback"],"summary":"Generate Feedback From Call","description":"Generate AI-powered teaching feedback directly from a call session (no booking required).\nOnly tutors can generate feedback for calls they participated in.","operationId":"generate_feedback_from_call_teaching_feedback_generate_from_call__call_session_id__post","parameters":[{"name":"call_session_id","in":"path","required":true,"schema":{"type":"string","title":"Call Session Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackGenerateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeachingFeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/teaching-feedback/quick/{booking_id}":{"post":{"tags":["teaching-feedback","teaching-feedback"],"summary":"Generate Quick Feedback","description":"Generate quick, focused feedback on a specific teaching aspect.\nFocus areas: general, pacing, engagement, clarity, patience, adaptation","operationId":"generate_quick_feedback_teaching_feedback_quick__booking_id__post","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}},{"name":"focus_area","in":"query","required":false,"schema":{"type":"string","default":"general","title":"Focus Area"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuickFeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/teaching-feedback/tutor/{tutor_id}":{"get":{"tags":["teaching-feedback","teaching-feedback"],"summary":"Get Tutor Feedback","description":"Get all teaching feedback for a specific tutor.\nOnly the tutor themselves can view their own feedback.\nReturns most recent feedback first.","operationId":"get_tutor_feedback_teaching_feedback_tutor__tutor_id__get","parameters":[{"name":"tutor_id","in":"path","required":true,"schema":{"type":"string","title":"Tutor Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TeachingFeedbackResponse"},"title":"Response Get Tutor Feedback Teaching Feedback Tutor  Tutor Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/teaching-feedback/{feedback_id}":{"get":{"tags":["teaching-feedback","teaching-feedback"],"summary":"Get Feedback","description":"Get a specific teaching feedback by ID.","operationId":"get_feedback_teaching_feedback__feedback_id__get","parameters":[{"name":"feedback_id","in":"path","required":true,"schema":{"type":"string","title":"Feedback Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeachingFeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/teaching-feedback/booking/{booking_id}":{"get":{"tags":["teaching-feedback","teaching-feedback"],"summary":"Get Feedback By Booking","description":"Get teaching feedback for a specific booking.","operationId":"get_feedback_by_booking_teaching_feedback_booking__booking_id__get","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeachingFeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/teaching-feedback/stats/{tutor_id}":{"get":{"tags":["teaching-feedback","teaching-feedback"],"summary":"Get Feedback Stats","description":"Get summary statistics from all feedback for a specific tutor.\nUseful for dashboard display.\nOnly the tutor themselves can view their stats.","operationId":"get_feedback_stats_teaching_feedback_stats__tutor_id__get","parameters":[{"name":"tutor_id","in":"path","required":true,"schema":{"type":"string","title":"Tutor Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/teaching-feedback/transcript/{booking_id}":{"get":{"tags":["teaching-feedback","teaching-feedback"],"summary":"Get Lesson Transcript","description":"Get the conversation transcript for a specific lesson.\nAccessible by the tutor or student involved in the booking.","operationId":"get_lesson_transcript_teaching_feedback_transcript__booking_id__get","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/teaching-feedback/improvement-plan/{tutor_id}":{"post":{"tags":["teaching-feedback","teaching-feedback"],"summary":"Generate Improvement Plan","description":"Generate a personalized 30-day improvement plan based on recent feedback.\nAnalyzes patterns across multiple lessons.\nOnly the tutor themselves can generate their plan.","operationId":"generate_improvement_plan_teaching_feedback_improvement_plan__tutor_id__post","parameters":[{"name":"tutor_id","in":"path","required":true,"schema":{"type":"string","title":"Tutor Id"}},{"name":"recent_count","in":"query","required":false,"schema":{"type":"integer","default":5,"title":"Recent Count"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/upload/video":{"post":{"tags":["upload","upload"],"summary":"Upload Video","description":"Upload a video file (for tutor introductions, etc.)\nMax file size: 100MB\nSupported formats: MP4, WebM, MOV, AVI","operationId":"upload_video_api_upload_video_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_video_api_upload_video_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/upload/profile-picture":{"post":{"tags":["upload","upload"],"summary":"Upload Profile Picture","description":"Upload a profile picture file.\nMax file size: 5MB\nSupported formats: JPG, JPEG, PNG, WebP","operationId":"upload_profile_picture_api_upload_profile_picture_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_profile_picture_api_upload_profile_picture_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/upload/chat-image":{"post":{"tags":["upload","upload"],"summary":"Upload Chat Image","description":"Upload an image pasted/attached into a 1:1 or group call chat.\nReturns a 60-minute signed URL; the caller sends a chat message\nreferencing it. Max file size: 8MB. Supported formats: JPG, PNG, WebP, GIF.\n\n🔒 Used to target the PUBLIC `fluentea-profile-pictures` bucket with\nlong-cache settings, unlike the DM/channel chat-image path\n(chat-image-ephemeral below), which was already migrated to the PRIVATE\n30-day-expiry bucket served via short-lived signed URLs. This is the same\nexposure for call chat — including school class calls with minors — so a\npasted image was a permanent, unauthenticated, publicly-fetchable URL\nforever. Now uploads to the same private bucket and returns a signed URL\nin the same `{\"success\", \"url\"}` shape the frontend already expects (no\nfrontend change needed): in-call chat is not persisted server-side (see\nreference_incall_chat_not_persisted), so every viewer sees the image\nlive, well within the 60-minute signed-URL window.","operationId":"upload_chat_image_api_upload_chat_image_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_chat_image_api_upload_chat_image_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/upload/chat-image-ephemeral":{"post":{"tags":["upload","upload"],"summary":"Upload Chat Image Ephemeral","description":"Upload an image attached in a 1:1 DM or a channel to the PRIVATE,\n30-day-auto-expiring chat bucket. Returns a stable gs:// ref (store this in\nthe message's metadata.image_ref) plus a short-lived signed URL for the\nsender's immediate display. Max 8MB; JPG/PNG/WebP/GIF.","operationId":"upload_chat_image_ephemeral_api_upload_chat_image_ephemeral_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_chat_image_ephemeral_api_upload_chat_image_ephemeral_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/upload/chat-image-url":{"post":{"tags":["upload","upload"],"summary":"Resolve Chat Image Url","description":"Mint a short-lived signed URL for a private chat-image gs:// ref (stored in a\nmessage's metadata.image_ref). Auth-gated and restricted to the chat-uploads\nbucket so it can't sign arbitrary objects. Returns {url} or 410 if the\nobject has already expired (deleted by the 30-day lifecycle rule).","operationId":"resolve_chat_image_url_api_upload_chat_image_url_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_resolve_chat_image_url_api_upload_chat_image_url_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/upload/chat-image-urls":{"post":{"tags":["upload","upload"],"summary":"Resolve Chat Image Urls","description":"Batch variant of /chat-image-url: mint signed URLs for many private chat-image\ngs:// refs in ONE round-trip. The message list resolves all visible images at\nonce (was one POST per image, each a separate GCS existence round-trip + auth).\nThe per-object .exists() checks run concurrently in a thread pool so the batch\nis bounded by the slowest single check, not their sum.\n\nReturns {results: {ref: {url} | {expired: true} | {error: true}}}. Same\nbucket restriction and per-object expiry semantics as the singular endpoint.","operationId":"resolve_chat_image_urls_api_upload_chat_image_urls_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_resolve_chat_image_urls_api_upload_chat_image_urls_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/upload/assignment-file":{"post":{"tags":["upload","upload"],"summary":"Upload Assignment File","description":"Upload a school assignment document (teacher material or student\nsolution). Max 20MB; PDF/Office/text/image types. Returns the URL the\nassignment or submission then references.","operationId":"upload_assignment_file_api_upload_assignment_file_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_assignment_file_api_upload_assignment_file_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/livekit-webhook":{"post":{"tags":["chatrooms"],"summary":"Livekit Webhook","description":"Receive LiveKit server webhook events for real-time presence updates.","operationId":"livekit_webhook_chatrooms_livekit_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/chatrooms/create":{"post":{"tags":["chatrooms"],"summary":"Create Chatroom","description":"Create a new voice chatroom. Premium users, tutors, and admins.","operationId":"create_chatroom_chatrooms_create_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateChatroomRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/create-class":{"post":{"tags":["chatrooms"],"summary":"Create Class Chatroom","description":"Start an instant ORG-LINKED class room and drop the teacher in as host.\n\nUnlike /create (which produces a regular, non-school room), the room here has\norganization_id set, so it flows into the school's Classes & reports. The\ncaller must be a teacher/admin/owner of the org. Returns the same shape as\n/create (room_name, join_code, token, server_url) so the frontend can\nnavigate straight into the room.","operationId":"create_class_chatroom_chatrooms_create_class_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateClassRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/active":{"get":{"tags":["chatrooms"],"summary":"List Active Chatrooms","description":"List all active chatrooms (public + the viewer's org-scoped rooms).","operationId":"list_active_chatrooms_chatrooms_active_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/featured":{"get":{"tags":["chatrooms"],"summary":"List Featured Chatrooms","description":"Active FluenTeam-hosted live rooms, for the dashboard \"Live — practice\nyour X\" card. Lightweight: most users see an empty list, so we only hit\nRedis for participant counts when featured rooms actually exist.","operationId":"list_featured_chatrooms_chatrooms_featured_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/status":{"get":{"tags":["chatrooms"],"summary":"Get Room Status","description":"Lightweight liveness check for the channel call card. A call is LIVE only\nwhile someone is actually in it (participant_count > 0). The room hash alone is\nnot enough: an ended room's Redis key can linger (empty-timeout cleanup can miss\nit, and it may carry no TTL), which would keep the card stuck on 'Join'.","operationId":"get_room_status_chatrooms__room_name__status_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/join":{"post":{"tags":["chatrooms"],"summary":"Join Chatroom","description":"Join a chatroom.\n\n/join is the single source of truth for \"becoming joinable\": if the room is\nstill `scheduled` but within its start window (15 min before `scheduled_at`\nonward), it is transitioned to `active` below — AFTER the private-room gate —\nso every entry point (CommuniTea cards, shared invite links, the calendar,\nfuture callers) works whether or not it remembered to call /start first.\nPreviously only some frontend paths called /start, so the rest 404'd with\n\"Chatroom not found or not active\" on a perfectly valid scheduled room.","operationId":"join_chatroom_chatrooms__room_name__join_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"join_code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Join Code"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/adopt-direct":{"post":{"tags":["chatrooms"],"summary":"Adopt Direct Call Room","description":"Wrap an accepted 1:1 peer call in a private VoiceChatroom so the group\ncall surface (ChatroomSession) can run it — the call-interface convergence\n(docs/development/CALL_CONVERGENCE_INVENTORY.md).\n\nThe 1:1 signaling (/video/call/initiate + /respond) is unchanged and has\nalready created the CallSession for room_name `room-{caller}-{callee}-{ts}`.\nThis endpoint ADOPTS that session: the direct room points at it, so\nlearner/teacher attribution, token reservation and end-of-call finalization\nall keep working. Idempotent — either peer may call it; the second call\nreturns the existing room.","operationId":"adopt_direct_call_room_chatrooms_adopt_direct_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdoptDirectRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/breakouts/preview":{"post":{"tags":["chatrooms"],"summary":"Preview Breakouts","description":"Compute the proposed breakout assignment WITHOUT creating any rooms, so the\nteacher can see who's in each room and adjust before starting. Host only.","operationId":"preview_breakouts_chatrooms__room_name__breakouts_preview_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBreakoutsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/breakouts":{"post":{"tags":["chatrooms"],"summary":"Create Breakouts","description":"Split a class room's current participants into balanced breakout rooms.\n\nHost (room creator) or admin only. Distribution balances the two exchange\nsides unless the teacher supplies explicit `assignments`. Returns the child\nrooms + who goes where; the teacher's client then broadcasts each assignment\nover the parent room's data channel so students auto-move.","operationId":"create_breakouts_chatrooms__room_name__breakouts_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBreakoutsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["chatrooms"],"summary":"Get Breakouts Overview","description":"Teacher overview: every open breakout under this room with its live state\n(participant count, recent transcript lines, talk-share, language-mix,\nsilence flag). Host/admin only. Meant to be polled every few seconds.","operationId":"get_breakouts_overview_chatrooms__room_name__breakouts_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/breakouts/layouts":{"get":{"tags":["chatrooms"],"summary":"List Room Breakout Layouts","description":"Groupings this class prepared in advance, fitted to who is here now.\n\nHost only. A grouping is saved against the SECTION (organization_groups),\nso this resolves the room to its section and reconciles each one against\nthe current participants — the teacher sees \"3 rooms, 2 people missing,\n1 to place\" before applying anything, rather than finding out afterwards.\n\nA room with no section has nothing to offer and says so with an empty list,\nnot an error: a personal call is not a broken class.","operationId":"list_room_breakout_layouts_chatrooms__room_name__breakouts_layouts_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["chatrooms"],"summary":"Save Room Breakout Layout","description":"Keep the arrangement on screen for next time. Host only.\n\nSaved against the room's SECTION, not the room: the room is one class, and\nthe whole point is that the grouping outlives it. A room with no section has\nnowhere to keep it, and says so rather than pretending to save.","operationId":"save_room_breakout_layout_chatrooms__room_name__breakouts_layouts_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveBreakoutLayoutRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/breakouts/assign-latecomer":{"post":{"tags":["chatrooms"],"summary":"Assign Latecomer To Breakout","description":"Place one late-joining student (currently in the parent room) into an\nalready-open breakout. Host/admin only.\n\nThe assignment is published SERVER-SIDE into the parent room, so the student\nreceives it even when the teacher is off inside another breakout — a host-client\nbroadcast would only reach the host's *current* room's data channel. Reuses the\nsame `breakout_assign` message the client already handles: the student's app\nfilters the assignments by user_id and auto-hops into the room with its code.","operationId":"assign_latecomer_to_breakout_chatrooms__room_name__breakouts_assign_latecomer_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignLatecomerRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/breakouts/move":{"post":{"tags":["chatrooms"],"summary":"Move Student Between Breakouts","description":"Move a student who is ALREADY inside a breakout into a different breakout.\nHost/admin only.\n\nUnlike assign-latecomer (whose target student sits in the parent room), the\nstudent here is inside a child room, so a parent-only broadcast wouldn't\nreach them. We publish the same `breakout_assign` message the client already\nhandles into the parent AND every active child, so it reaches the student\nwherever they currently are; every other student ignores it (the client\nfilters the assignment list by its own user_id).","operationId":"move_student_between_breakouts_chatrooms__room_name__breakouts_move_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MoveBreakoutRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/breakouts/close":{"post":{"tags":["chatrooms"],"summary":"Close Breakouts","description":"End all breakouts under this room and bring everyone back to the main\nroom. Host/admin only. Returns the parent room_name so clients can rejoin.","operationId":"close_breakouts_chatrooms__room_name__breakouts_close_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/breakouts/broadcast":{"post":{"tags":["chatrooms"],"summary":"Broadcast To Breakouts","description":"Host broadcasts a message, sound cue, or screen-share signal to EVERY\nactive breakout under this parent at once (like Zoom's 'broadcast to breakout\nrooms'). Screen sharing itself is published by the host into the PARENT room;\nthis only signals breakouts to open a subscribe-only view of it (watch-token).","operationId":"broadcast_to_breakouts_chatrooms__room_name__breakouts_broadcast_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BreakoutBroadcastRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/chat/log":{"post":{"tags":["chatrooms"],"summary":"Log Room Chat Message","description":"Record one in-call chat message so it survives the room.\n\n**This is not the delivery path.** Messages reach the other participants\nover the LiveKit data channel; the client calls this alongside that, after\nit has already published. Persistence must never be able to delay or drop a\nmessage in a live class, so a failure here costs the record and not the\nconversation — which is why the client fires it and does not await it.\n\nIdempotent on `client_id`: a retry, or the class broadcast echoing a message\nback into its own room, updates nothing and returns the existing row.","operationId":"log_room_chat_message_chatrooms__room_name__chat_log_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Payload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/chat/history":{"get":{"tags":["chatrooms"],"summary":"Get Room Chat History","description":"What was typed in this room, oldest first.\n\nRead on joining, so someone who arrives late or reconnects sees the\nconversation instead of an empty panel — and so the link a teacher pasted\nten minutes ago is still there.\n\nScoped to the ROOM, not the class: a breakout's chat belongs to that\nbreakout. Class-wide gathering is the class report's job.\n\nPRIVATE ROWS ARE EXCLUDED, and that exclusion is load-bearing rather than\ntidy. Every client fetches this on connect; without the filter, a student\nrejoining after a dropped connection would be handed every other student's\nprivate question to the teacher, in the ordinary chat panel, with no\nindication that it was ever meant to be private. The quiet lane has its own\nendpoint with its own gate — see `get_class_private_messages`.","operationId":"get_room_chat_history_chatrooms__room_name__chat_history_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":300,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/chat/{message_id}/reactions":{"post":{"tags":["chatrooms"],"summary":"Toggle Room Chat Reaction","description":"Add or remove MY reaction to one message in this room's chat.\n\nAsked for by an advisor, in the chat of a real class on 2026-08-26: \"ohh\nalso react to the comments in the chat?\" The call already had reactions -\nthe ones that drift over the video - but a line somebody typed could not be\nanswered without typing back, which in a class of forty is forty lines of\n\"yes\".\n\n**Unlike `/chat/log`, the state here has to be readable back.** A message is\npublished over the data channel first and recorded second, because a live\nclass must never wait on a database write; a reaction keeps that order, but\nit is a TOGGLE on shared state rather than an append, so somebody who joins\nlate or reconnects has to be able to ask what the current state is, and this\ntable is the only place that answers. The client publishes for immediacy and\ncalls this for the record - the same both/and the message itself uses.\n\nToggling is per user, and the whole update says \"this user now does / does\nnot have this emoji on this message\". Two people reacting at once therefore\ncannot conflict: each owns only their own key, so the data-channel deltas\nconverge on the state the database already holds.\n\nPRIVATE ROWS ARE NOT REACTABLE, and that is a gate rather than a\nsimplification. The quiet lane is private FROM THE CLASSMATES; this endpoint\nonly checks that the caller is in the room, so without the filter any\nstudent could probe a message id and both confirm that a private question\nexists and write themselves into it.","operationId":"toggle_room_chat_reaction_chatrooms__room_name__chat__message_id__reactions_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatReactionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/chat/private":{"post":{"tags":["chatrooms"],"summary":"Send Private Class Message","description":"Ask the teaching staff something the rest of the class will not see —\nor, as staff, answer one.\n\nWHO MAY SEND WHAT:\n\n  * A STUDENT sends a question. `to_user_id` is refused: they are asking\n    whoever is teaching, not a named adult, and letting a student address\n    one specific member of staff is the first half of a private channel we\n    do not want to exist.\n  * STAFF may only REPLY, and only into a thread that a student already\n    opened (`to_user_id` must name someone who has asked in this class).\n    There is no way for a teacher to open a private thread with a student.\n\nThat asymmetry IS the safeguarding design, so it is enforced here and not in\nthe UI. Students in a school class are often minors; a lane an adult can\nopen is a back-channel, and a lane only a child can open is a help desk.\nNothing is hidden either way: `_may_read_class_private` lets the school read\nevery thread, during the class and afterwards.\n\nSynchronous and boring on purpose — no data-channel publish, no push. See\nthe lane comment above `PRIVATE_ASK_MAX_CHARS`.","operationId":"send_private_class_message_chatrooms__room_name__chat_private_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PrivateAskRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["chatrooms"],"summary":"Get Class Private Messages","description":"The quiet lane, as this caller is allowed to see it. Oldest first.\n\nThis is the ONLY delivery path — clients poll it. A teacher's count of\nwaiting questions goes up when they ask for it, never when a student sends\none, which is precisely the property Sinead asked for.\n\nWHAT EACH CALLER GETS:\n  * teaching staff, and anyone who may read the class report: every private\n    thread in the class (the parent room and all of its breakouts).\n  * a student: their own thread only — what they asked, and what staff\n    replied to them.\n\n`since_ms` makes the poll cheap: pass the newest `ts` you already hold.\nRead access does NOT require being in the room right now, so a coordinator\ncan audit the lane a week later; sending does (`_require_room_participant`).","operationId":"get_class_private_messages_chatrooms__room_name__chat_private_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"since_ms","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Since Ms"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/chat/broadcast":{"post":{"tags":["chatrooms"],"summary":"Broadcast Chat To Class","description":"Send a chat message to EVERYONE in the class — the main room and all\nactive breakout rooms — regardless of which room the sender is currently in.\n\nPowers the chat scope toggle (\"this room\" vs \"everyone\"): normal messages go\nover the sender's own LiveKit data channel (room-local); this endpoint covers\nthe rest of the class server-side. The sender's OWN room is excluded — their\nclient already published the message locally there (with local echo).\n\nOpen to any current participant of the origin room, not just the host —\nstudents in a breakout can ask the whole class / reach the teacher too.","operationId":"broadcast_chat_to_class_chatrooms__room_name__chat_broadcast_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClassChatBroadcastRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/watch-token":{"get":{"tags":["chatrooms"],"summary":"Get Broadcast Watch Token","description":"Subscribe-only token so a breakout participant can watch the host's screen\nbeing broadcast from the PARENT room. `room_name` is the parent; the caller must\ncurrently be a participant of the parent or one of its breakouts. A distinct\n`watch-` identity keeps it a silent observer that doesn't affect rosters.","operationId":"get_broadcast_watch_token_chatrooms__room_name__watch_token_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/recording/start":{"post":{"tags":["chatrooms"],"summary":"Start Recording","description":"Start a full-video recording of the room (LiveKit composite egress → GCS).\nHost/admin only. The host's client should broadcast a recording notice so all\nparticipants know they're being recorded.","operationId":"start_recording_chatrooms__room_name__recording_start_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/recording/stop":{"post":{"tags":["chatrooms"],"summary":"Stop Recording","description":"Stop the active recording for this room. Host/admin only.","operationId":"stop_recording_chatrooms__room_name__recording_stop_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/recordings":{"get":{"tags":["chatrooms"],"summary":"List Recordings","description":"List this room's recordings with short-lived playback URLs. Host/admin.","operationId":"list_recordings_chatrooms__room_name__recordings_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/hosts/{user_id}":{"post":{"tags":["chatrooms"],"summary":"Add Room Host","description":"Promote a participant to co-host (dashboard powers). Any existing host may\npromote. Org staff already host automatically; this is for ad-hoc grants.","operationId":"add_room_host_chatrooms__room_name__hosts__user_id__post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["chatrooms"],"summary":"Remove Room Host","description":"Demote a co-host. Host-only. The room CREATOR cannot be demoted, and org\nstaff keep their automatic host role (this only clears an ad-hoc grant).","operationId":"remove_room_host_chatrooms__room_name__hosts__user_id__delete","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/class-report":{"get":{"tags":["chatrooms"],"summary":"Get Class Report","description":"End-of-class report for org staff: per-student rollup, class-wide patterns,\nand feedback on the teacher's own facilitation. Aggregates the per-call\nsummaries across the main room + all its breakouts.\n\nAccess is ORG-STAFF only (owner/admin/teacher of the room's organization) or\na platform admin — NOT the room creator by itself. A regular (non-school) call\nwith no organization_id has no class report.","operationId":"get_class_report_chatrooms__room_name__class_report_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/class-report/note":{"put":{"tags":["chatrooms"],"summary":"Set Class Report Note Endpoint","description":"The teacher's right of reply on a class report.\n\n`{\"body\": \"...\", \"student_user_id\": \"<uuid>\"}` — omit `student_user_id` for\nthe note on the class as a whole. An empty body clears the note.\n\nAccess is the same ORG-STAFF gate as reading the report, deliberately: the\npeople who can be judged by this report are the people who can annotate it.\nA coordinator can add context too — they are staff on the section and may be\nthe one who knows why a class went the way it did.","operationId":"set_class_report_note_endpoint_chatrooms__room_name__class_report_note_put","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Payload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/report/regenerate":{"post":{"tags":["chatrooms"],"summary":"Regenerate Class Report","description":"Regenerate all per-student feedback cards + grammar/vocab for this class\n(main room + every breakout), then the class report reflects it on next open.\n\nTeachers use this after editing their teaching philosophy so the feedback is\nre-written in their style — or after a bad generation. Heavy (several Gemini\ncalls per student per room), so it runs as a background task; the client\nshould re-fetch the report after a minute or two.","operationId":"regenerate_class_report_chatrooms__room_name__report_regenerate_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/class-transcript":{"get":{"tags":["chatrooms"],"summary":"Get Class Transcript","description":"Merged, time-sorted transcript across the class (main room + every breakout).\n\nFor an audio-only class the recording video is blank (nothing to composite), so\nthis chronological \"who said what, when\" timeline IS the record of who was talking\nand when. Org-staff only, same access as the class report.","operationId":"get_class_transcript_chatrooms__room_name__class_transcript_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/my-report":{"get":{"tags":["chatrooms"],"summary":"Get My Report","description":"A learner's own slice of the class report for this room.\n\nAny authenticated user; returns ONLY their own per-student row. Works for\nBOTH school and regular rooms — a learner can always see their own data.\nResolves the class parent (via parent_room_id or self), computes the report,\nand returns the row whose user_id matches the caller (or null if they have\nno data yet — 200, not an error).","operationId":"get_my_report_chatrooms__room_name__my_report_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/me/attended-classes":{"get":{"tags":["chatrooms"],"summary":"My Attended Classes","description":"The current user's attended SCHOOL class sessions (for \"My progress\").\n\nA class session = a parent room (parent_room_id NULL) with organization_id\nset, that the user attended — either via a CallParticipant row or by appearing\nas a transcript speaker on its CallSession (breakout speech counts toward the\nparent). Newest first.","operationId":"my_attended_classes_chatrooms_me_attended_classes_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/listen-token":{"get":{"tags":["chatrooms"],"summary":"Get Breakout Listen Token","description":"Subscribe-only token so the teacher can monitor a breakout's audio/video\nWITHOUT joining as a speaker or counting toward the room's roster.\n\n`room_name` is the child breakout; only the parent's host (or an admin) may\nlisten. Uses a distinct `listen-` identity and can_publish=False so they\nstay a silent observer and Redis participant counts remain accurate.","operationId":"get_breakout_listen_token_chatrooms__room_name__listen_token_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/invite":{"post":{"tags":["chatrooms"],"summary":"Invite To Chatroom","description":"Invite users to a (private) room — creator only. Returns the join code too.","operationId":"invite_to_chatroom_chatrooms__room_name__invite_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/leave":{"post":{"tags":["chatrooms"],"summary":"Leave Chatroom","description":"Leave a chatroom.","operationId":"leave_chatroom_chatrooms__room_name__leave_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}":{"delete":{"tags":["chatrooms"],"summary":"End Chatroom","description":"End a chatroom. Creator or admin only.","operationId":"end_chatroom_chatrooms__room_name__delete","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["chatrooms"],"summary":"Get Chatroom Details","description":"Get details about a chatroom.\n\nTwo payload tiers (the read path used to hand everything to any logged-in\ncaller, with no membership check at all):\n\n- **Member** (creator/host, invited, valid `join_code`, current or former\n  participant): the full payload, including the participant identity list\n  and room credentials.\n- **Non-member**: the room SUMMARY only — enough to render the join screen\n  (room exists, topic, language, status, schedule, `is_org_class` /\n  `organization_name`, and the participant COUNT) but no participant names\n  or photos and no join codes. Private rooms 404 for non-members instead of\n  403 so the endpoint doesn't confirm that a private room_name exists.","operationId":"get_chatroom_details_chatrooms__room_name__get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"join_code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Join Code"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/upcoming":{"get":{"tags":["chatrooms"],"summary":"List Upcoming Chatrooms","description":"List scheduled chatrooms ordered by scheduled_at ASC, with RSVP counts.\n\nOnly returns rooms whose scheduled_at is in the future (UTC-aware comparison).","operationId":"list_upcoming_chatrooms_chatrooms_upcoming_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/my-upcoming":{"get":{"tags":["chatrooms"],"summary":"List My Upcoming Chatrooms","description":"The current user's OWN upcoming scheduled rooms — ones they created OR\nconfirmed-RSVP'd. Feeds the connect-page \"Your Calendar\" (unlike /upcoming,\nwhich lists ALL public scheduled rooms for discovery). Future only.","operationId":"list_my_upcoming_chatrooms_chatrooms_my_upcoming_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/reschedule":{"put":{"tags":["chatrooms"],"summary":"Reschedule Chatroom","description":"Host reschedules a still-scheduled room to a new future time.","operationId":"reschedule_chatroom_chatrooms__room_name__reschedule_put","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/sessions/analytics":{"get":{"tags":["chatrooms"],"summary":"List Chatroom Sessions For Analytics","description":"List chatroom sessions where the current user participated, for analytics.\n\nReturns chatroom call sessions with transcript availability, participant info,\nand the user's join/leave window. Sessions appear as soon as the user leaves\nthe chatroom (left_at is set), even if the room is still active.","operationId":"list_chatroom_sessions_for_analytics_chatrooms_sessions_analytics_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/invited":{"get":{"tags":["chatrooms"],"summary":"List Invited Chatrooms","description":"Private rooms the current user has been invited to (for the\n'Invited to' section). Defined before /{room_name} so it isn't swallowed\nby the catch-all route.","operationId":"list_invited_chatrooms_chatrooms_invited_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/by-code/{code}":{"get":{"tags":["chatrooms"],"summary":"Resolve Chatroom By Code","description":"Resolve a private room's join code to its room_name so a standalone\ncode (shared out-of-band) can be redeemed. Defined before /{room_name}\nso the literal 'by-code' prefix isn't swallowed by the catch-all. The\nactual access gate still runs in join_chatroom — this only maps code → room.","operationId":"resolve_chatroom_by_code_chatrooms_by_code__code__get","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","title":"Code"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/roster-status":{"get":{"tags":["chatrooms"],"summary":"Roster Status","description":"Which people in this class are actually on the section roster. HOST ONLY.\n\nAngie, describing how the first classes will really go (2026-08-24): most\nstudents click the link without signing up, so the teacher spends the start\nof the lesson fixing it, and by the second class wants to check by NAME.\nThey cannot do that today. The roster panel shows who is in the room; it has\nno idea which of them the register will actually record.\n\nThree states, and the middle one is the one nobody expects:\n\n  * `registered: false`  — an anonymous guest. Their attendance cannot be\n    matched to anybody, because a guest join mints a fresh anon id per visit.\n  * `in_section: false`  — a REAL account, signed in, and still not on this\n    section's roster. Looks completely normal on the stage and is silently\n    absent from the register. This is the state that produces \"but I was\n    there\" a fortnight later.\n  * both true — counted.\n\nHost-gated because it is a statement about named people's enrolment, which is\nthe teacher's business and not the class's. `is_room_host` covers the room\ncreator, promoted co-hosts, org staff and admins.\n\nTakes the identities currently in the LiveKit room rather than returning the\nsection roster, so nothing here can be used to enumerate a school's students:\nthe caller only learns about people already visibly in the room with them.","operationId":"roster_status_chatrooms__room_name__roster_status_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"identities","in":"query","required":false,"schema":{"type":"string","default":"","title":"Identities"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/rsvp":{"post":{"tags":["chatrooms"],"summary":"Rsvp Chatroom","description":"RSVP to a scheduled chatroom.","operationId":"rsvp_chatroom_chatrooms__room_name__rsvp_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["chatrooms"],"summary":"Cancel Rsvp","description":"Cancel RSVP to a scheduled chatroom.","operationId":"cancel_rsvp_chatrooms__room_name__rsvp_delete","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/start":{"post":{"tags":["chatrooms"],"summary":"Start Scheduled Chatroom","description":"Any authenticated user transitions a scheduled room to active (creates LiveKit room).\n\nCan only be started within 15 minutes of the scheduled time (or after it).","operationId":"start_scheduled_chatroom_chatrooms__room_name__start_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/kick/{user_id}":{"post":{"tags":["chatrooms"],"summary":"Kick Participant","description":"Kick a participant from a chatroom. Creator or admin only.","operationId":"kick_participant_chatrooms__room_name__kick__user_id__post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/readmit/{user_id}":{"post":{"tags":["chatrooms"],"summary":"Readmit Participant","description":"Let a removed student back into this room. Host only.\n\nA removal has to be reversible. Kicking is one tap next to several others in\na live class, and without this a mis-tap would shut a student out of a\nlesson they are enrolled in, with no way back for either of them.","operationId":"readmit_participant_chatrooms__room_name__readmit__user_id__post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/report/{user_id}":{"post":{"tags":["chatrooms"],"summary":"Report Participant","description":"Report a participant in a chatroom.","operationId":"report_participant_chatrooms__room_name__report__user_id__post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/chacha-prompts":{"get":{"tags":["chatrooms"],"summary":"Get Chacha Prompts","description":"Generate beginner-friendly bilingual discussion questions for a chatroom.","operationId":"get_chacha_prompts_chatrooms__room_name__chacha_prompts_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"native_language","in":"query","required":false,"schema":{"type":"string","default":"English","title":"Native Language"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/chacha-chat":{"post":{"tags":["chatrooms"],"summary":"Chatroom Chacha Chat","description":"Chat with ChaCha in the context of a voice chatroom.","operationId":"chatroom_chacha_chat_chatrooms__room_name__chacha_chat_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatroomChaChaRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/chacha-speak":{"post":{"tags":["chatrooms"],"summary":"Chatroom Chacha Speak","description":"Voice a ChaCha answer so the whole room can hear it.\n\nGenerates TTS (ChaCha's Callirrhoe voice), mirrors the wav into Redis so\nANY pod can serve it (multi-pod gamma), and returns the audio both as\nbase64 (requester plays immediately) and as an audio_id the requester\nbroadcasts over the room's LiveKit data channel; other clients fetch it\nfrom /chatrooms/{room}/chacha-audio/{audio_id}.","operationId":"chatroom_chacha_speak_chatrooms__room_name__chacha_speak_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChaChaSpeakRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/chacha-transcribe":{"post":{"tags":["chatrooms"],"summary":"Chatroom Chacha Transcribe","description":"One-shot STT for a voice question to ChaCha in the call panel.\n\nDeliberately does NOT record voice-call minutes, live-transcription\nminutes, or the daily transcription quota (unlike /voice/transcribe) —\na panel voice ask consumes exactly ONE ChaCha message, charged by the\nchacha-chat call that follows. (Nancy: \"talking to ChaCha in the group\ncalls should not count towards voice minutes.\") The Deepgram cost of a\nfew seconds of STT per ask is accepted untracked.","operationId":"chatroom_chacha_transcribe_chatrooms__room_name__chacha_transcribe_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_chatroom_chacha_transcribe_chatrooms__room_name__chacha_transcribe_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/chacha-speak-stream":{"post":{"tags":["chatrooms"],"summary":"Chatroom Chacha Speak Stream","description":"Stream ChaCha TTS as raw PCM (24kHz mono s16le) while it generates.\n\nFirst audible chunk in <1s vs 2.6-16s for the batch endpoint. The\nresponse header carries X-Chacha-Audio-Id; when the body completes the\nfull wav has been written locally AND mirrored to Redis, so the client\ncan safely broadcast the id to the room (listeners fetch the batch way).","operationId":"chatroom_chacha_speak_stream_chatrooms__room_name__chacha_speak_stream_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChaChaSpeakRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/chacha-audio/{audio_id}":{"get":{"tags":["chatrooms"],"summary":"Get Chatroom Chacha Audio","description":"Serve a chacha-speak wav to the other participants in the room.\n\nLocal disk first (same pod that generated it), Redis mirror otherwise.\naudio_id is locked to the exact filename shape chacha-speak produces —\nno traversal, no serving arbitrary files.","operationId":"get_chatroom_chacha_audio_chatrooms__room_name__chacha_audio__audio_id__get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"audio_id","in":"path","required":true,"schema":{"type":"string","title":"Audio Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/plan":{"get":{"tags":["chatrooms"],"summary":"Get Room Class Plan","description":"The class plan for this room, for the in-call agenda panel.\n\nHosts get the full plan including their private notes. Everyone else gets\nit only once it is `published` AND `visible_to_students`, with every `notes`\nfield stripped — and `{\"plan\": null}` otherwise, so the panel renders empty\nrather than the client having to interpret a 403.","operationId":"get_room_class_plan_chatrooms__room_name__plan_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/plan/items/{item_id}/cover":{"post":{"tags":["chatrooms"],"summary":"Cover Room Class Plan Item","description":"Tick an agenda item off mid-class: `{covered: bool, actual_minutes?: int}`.\n\nHost only. The client broadcasts the same event over the LiveKit data\nchannel so every panel follows the teacher instantly; this call is what\nmakes it survive a refresh and what feeds planned-vs-actual into the class\nreport and the next bump.","operationId":"cover_room_class_plan_item_chatrooms__room_name__plan_items__item_id__cover_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"item_id","in":"path","required":true,"schema":{"type":"string","title":"Item Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoverPlanItemRequest","default":{"covered":true}}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/plan/materials":{"post":{"tags":["chatrooms"],"summary":"Share Class Plan Material","description":"Attach an uploaded file to this class's plan. Host only.\n\nThe client uploads to `/api/upload/assignment-file` first and posts the\nreference it got back. Returns the material as a client reads it — a\nfreshly signed link, never the stored reference.","operationId":"share_class_plan_material_chatrooms__room_name__plan_materials_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareClassMaterialRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/plan/materials/{material_id}":{"patch":{"tags":["chatrooms"],"summary":"Set Class Plan Material Shared","description":"Show a handout to the class, or take it back. Host only.","operationId":"set_class_plan_material_shared_chatrooms__room_name__plan_materials__material_id__patch","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"material_id","in":"path","required":true,"schema":{"type":"string","title":"Material Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetClassMaterialSharedRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["chatrooms"],"summary":"Remove Class Plan Material","description":"Detach a handout from the plan. Host only.\n\nThe object in the bucket is left alone — see `remove_material`.","operationId":"remove_class_plan_material_chatrooms__room_name__plan_materials__material_id__delete","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"material_id","in":"path","required":true,"schema":{"type":"string","title":"Material Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/recordings/{recording_id}/share":{"post":{"tags":["recordings"],"summary":"Create Share Link","description":"Mint (or return) a permanent link for this recording. Host only.\n\nIdempotent on purpose: asking twice hands back the same link rather than\nlittering the recording with tokens nobody can tell apart.\n\nSync def: single-worker gamma, and this does sync DB work.","operationId":"create_share_link_recordings__recording_id__share_post","parameters":[{"name":"recording_id","in":"path","required":true,"schema":{"type":"string","title":"Recording Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/recordings/{recording_id}/shares":{"get":{"tags":["recordings"],"summary":"List Share Links","description":"Every link ever minted for this recording, live and revoked. Host only.","operationId":"list_share_links_recordings__recording_id__shares_get","parameters":[{"name":"recording_id","in":"path","required":true,"schema":{"type":"string","title":"Recording Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/recordings/shares/{token}":{"delete":{"tags":["recordings"],"summary":"Revoke Share Link","description":"Kill a link. Anyone holding it gets a 410 from then on. Host only.","operationId":"revoke_share_link_recordings_shares__token__delete","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/recordings/shared/{token}":{"get":{"tags":["recordings"],"summary":"Open Shared Recording","description":"Public playback. No auth — holding the link IS the credential.\n\nRedirects to a signed URL minted right now, which is why the link itself\nnever goes stale. Deliberately short (30 min) on the far side: the redirect\ntarget may end up in a browser history or a proxy log, and a leaked\nsignature should die quickly even though the share link does not.","operationId":"open_shared_recording_recordings_shared__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/canasta/start":{"post":{"tags":["canasta"],"summary":"Start Canasta Game","description":"Any participant starts a Canasta game in the chatroom.\n\nIdempotent: if an active game already exists, return it as-is. This\nprevents race conditions where concurrent /start clicks would each\nend the previous game and create a new one, effectively killing\nwhoever clicked first.","operationId":"start_canasta_game_chatrooms__room_name__canasta_start_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/canasta/state":{"get":{"tags":["canasta"],"summary":"Get Canasta State","description":"Get current game state. Describer sees the word, others don't.\nServer-side auto-advance: completes turns when timer expires, advances\nrounds when all current participants have described, and handles scoring timeout.","operationId":"get_canasta_state_chatrooms__room_name__canasta_state_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/canasta/guess":{"post":{"tags":["canasta"],"summary":"Submit Guess","description":"Submit a typed guess. Server fuzzy-matches against target word.","operationId":"submit_guess_chatrooms__room_name__canasta_guess_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuessRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/canasta/skip":{"post":{"tags":["canasta"],"summary":"Skip Word","description":"Describer skips the current word (no points). Limited to one skip per turn.","operationId":"skip_word_chatrooms__room_name__canasta_skip_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/canasta/advance":{"post":{"tags":["canasta"],"summary":"Advance Round","description":"Advance to the next round type after scoring. Any participant can call this.\n\nDeliberately a plain `def`, like every other mutating handler here: it takes a\n`FOR UPDATE` row lock through a SYNCHRONOUS session and contains no awaits.\nAs `async def` it ran on the event loop, so when three clients hit the end of\na scoring countdown together — which is exactly what they do, the countdown\nis the same for everyone — one request held the lock while the loop was\nblocked by the next request waiting for it, and the whole backend stopped\nanswering anybody. FastAPI runs a sync handler in a threadpool, where waiting\nfor a lock costs one thread instead of the entire server.","operationId":"advance_round_chatrooms__room_name__canasta_advance_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/canasta/next-round":{"post":{"tags":["canasta"],"summary":"Next Round","description":"Advance to the next round (legacy endpoint, kept for compatibility).","operationId":"next_round_chatrooms__room_name__canasta_next_round_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NextRoundRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/canasta/acknowledge":{"post":{"tags":["canasta"],"summary":"Acknowledge Word","description":"Describer clicked 'OK Got It' — reset turn timer so preview time doesn't count.","operationId":"acknowledge_word_chatrooms__room_name__canasta_acknowledge_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/canasta/time-up":{"post":{"tags":["canasta"],"summary":"Time Up","description":"Timer expired — exhaust remaining words for current describer, trigger rotation.","operationId":"time_up_chatrooms__room_name__canasta_time_up_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/canasta/foul":{"post":{"tags":["canasta"],"summary":"Describer Foul","description":"Describer said the word — penalty and turn ends.\n\nAny participant can call this (detected via transcript). The describer\nloses FOUL_PENALTY points and their turn is immediately completed.","operationId":"describer_foul_chatrooms__room_name__canasta_foul_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/canasta/end":{"post":{"tags":["canasta"],"summary":"End Game","description":"End the game FOR EVERYONE, and return final scores.\n\nUntil now the only control wired to this lived on the final scoreboard —\na screen you reach by playing the game to its natural conclusion — so a game\nin progress could not be stopped at all. It is reachable mid-game now, which\nmakes the question of who may press it a real one: see engine.may_end_game.\nThe teacher, or whoever started it. Everyone else has the header X, which\ncloses the panel for them and leaves the game running.\n\nThe scores stand. They were earned, and ending early is not a reason to throw\nthem away.","operationId":"end_game_chatrooms__room_name__canasta_end_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/games/catalog":{"get":{"tags":["games"],"summary":"Game Catalog","description":"What this room can play, and what it cannot play yet and why.","operationId":"game_catalog_chatrooms__room_name__games_catalog_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/games/start":{"post":{"tags":["games"],"summary":"Start Game","description":"Start a format in this room.\n\nIdempotent in the same way Canasta's start is: if a game is already running,\nreturn it rather than replacing it. Concurrent taps on a picker would\notherwise each kill the previous game, and whoever tapped first would lose.","operationId":"start_game_chatrooms__room_name__games_start_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartGameRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/games/start-in-breakouts":{"post":{"tags":["games"],"summary":"Start Game In Breakouts","description":"Start the same game in every open breakout, from the main room.\n\nAsked for after the 2026-08-27 workshop: a teacher who sends four groups off\nto play something had to visit each room and start it there, by which time\nthe first group has been sitting in silence for two minutes.\n\nThe teacher is not IN any of these rooms, so this authorises on the PARENT\n(host only) and starts on their behalf without seating them anywhere. Each\nbreakout gets its own seating from its own participants, which is why the\nfan-out reuses _seat_and_start rather than reimplementing it.\n\nPartial success is the normal case and is reported rather than hidden: a\nbreakout with one person in it cannot play a two-player game, and the\nteacher needs to know which one that was. One room failing never stops the\nothers.","operationId":"start_game_in_breakouts_chatrooms__room_name__games_start_in_breakouts_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartGameEverywhereRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/games/state":{"get":{"tags":["games"],"summary":"Game State","description":"The caller's view of the game, plus server-side catch-up.\n\nThe poll is the hottest path in the product: every client in the room hits\nit every two seconds for the whole game. It must not take the row lock in\nthe common case, and until 19 Aug it took it on every single call.\n\nThe bug was the predicate, not the intent. `needs_work` read\n`round_phase != PHASE_COMPLETE`, which is not \"is there work to do\", it is\n\"is the game still running\" — true for every poll of every live game. So\nN clients serialised on one lock, each holding a threadpool thread and a\npooled DB connection while it waited. Latency compounds from there: slower\nresponses leave more polls outstanding, more outstanding polls queue deeper\non the lock. On gamma with three people it went 2.9s -> 32s -> 122s, drained\nthe 50-connection pool, and took down everything that had nothing to do with\ngames (Deepgram transcript writes, reminders, the room cleanup task) before\n/health stopped answering and kubelet killed the container.\n\nSo: run the catch-up speculatively on the UNLOCKED row. `auto_advance`\nreports whether it changed anything, and for a live game the answer is no on\nalmost every poll — a turn timer has to expire, or a lap has to finish.\nOnly then do we discard the speculative work, re-read FOR UPDATE and do it\nagain for real. Asking the spec directly is what keeps this honest: a\nhand-written \"is it due yet\" predicate beside six formats' `tick` methods is\nexactly the thing that drifts back into locking on every call.","operationId":"game_state_chatrooms__room_name__games_state_get","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/games/action":{"post":{"tags":["games"],"summary":"Game Action","operationId":"game_action_chatrooms__room_name__games_action_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/games/take-seat":{"post":{"tags":["games"],"summary":"Game Take Seat","description":"Join a game that is already running, if it has a free seat.\n\nA teacher who fans a game out to the breakouts is deliberately not seated in\nany of them: they are outside every room at that moment, and seating an\nabsent person would hand somebody the first turn who is not there. But the\nteacher then hops INTO a room, and until now there was no way back in. They\nwatched a game they had started and could not play (Nancy, 2026-08-31).\n\nCanasta's own start endpoint has always done this, idempotently, for exactly\nthis reason. This is the same act for the shared engine, said once rather\nthan hidden inside \"start\".\n\nNot restricted to the host. Anyone in the room may take an empty seat: a\nstudent who joined late is in the same position and the seat is either free\nor it is not.\n\nPlain `def`, and it takes the row lock: two people reaching for the last\nseat at the same instant must not both get it. See game_state above for why\n`async def` around this lock is how canasta froze the server.","operationId":"game_take_seat_chatrooms__room_name__games_take_seat_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/games/speech":{"post":{"tags":["games"],"summary":"Game Speech","description":"Report a finalised transcript line the CALLER spoke.\n\nThe speaker is taken from the token, never from the request body. Every\nclient in the room receives captions for everyone, so letting a client name\nthe speaker would let it claim someone else's words — and self-reporting is\nboth safer and simpler, since a client always knows what its own microphone\njust produced.","operationId":"game_speech_chatrooms__room_name__games_speech_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpeechRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatrooms/{room_name}/games/end":{"post":{"tags":["games"],"summary":"End Game","description":"End the running game, for everyone.\n\nTwo gates, and they are different questions. `require_room_access` asks\nwhether you are in this room at all — Canasta's equivalent had no check, so\nanyone who knew a room name could end a class's game from outside.\n`require_game_ender` asks whether ending a SHARED activity is yours to do:\nthe teacher's, or the starter's. Every other player has the header X, which\ncloses the panel for them and leaves the game running for the room.","operationId":"end_game_chatrooms__room_name__games_end_post","parameters":[{"name":"room_name","in":"path","required":true,"schema":{"type":"string","title":"Room Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/peer-calls/invite":{"post":{"tags":["peer-calls"],"summary":"Create Peer Call Invite","description":"Create a new peer call invitation.","operationId":"create_peer_call_invite_peer_calls_invite_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePeerCallRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/peer-calls":{"get":{"tags":["peer-calls"],"summary":"List Peer Calls","description":"List peer call invites for the current user (sent and received).","operationId":"list_peer_calls_peer_calls_get","parameters":[{"name":"status_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status Filter"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/peer-calls/{invite_id}/accept":{"post":{"tags":["peer-calls"],"summary":"Accept Peer Call","description":"Accept a peer call invitation. Generates a room name and sends confirmation emails.","operationId":"accept_peer_call_peer_calls__invite_id__accept_post","parameters":[{"name":"invite_id","in":"path","required":true,"schema":{"type":"string","title":"Invite Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/peer-calls/{invite_id}/decline":{"post":{"tags":["peer-calls"],"summary":"Decline Peer Call","description":"Decline a peer call invitation.","operationId":"decline_peer_call_peer_calls__invite_id__decline_post","parameters":[{"name":"invite_id","in":"path","required":true,"schema":{"type":"string","title":"Invite Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/peer-calls/{invite_id}/cancel":{"post":{"tags":["peer-calls"],"summary":"Cancel Peer Call","description":"Cancel a peer call invitation. Either party can cancel while pending or accepted.","operationId":"cancel_peer_call_peer_calls__invite_id__cancel_post","parameters":[{"name":"invite_id","in":"path","required":true,"schema":{"type":"string","title":"Invite Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/peer-calls/{invite_id}/reschedule":{"patch":{"tags":["peer-calls"],"summary":"Reschedule Peer Call","description":"Reschedule a peer call. Either party can reschedule while pending\nor accepted. Mirrors the tutor-booking reschedule shape so the\nfrontend can route by call type without diverging payload schemas.","operationId":"reschedule_peer_call_peer_calls__invite_id__reschedule_patch","parameters":[{"name":"invite_id","in":"path","required":true,"schema":{"type":"string","title":"Invite Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReschedulePeerCallRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/peer-calls/{invite_id}/join-room":{"post":{"tags":["peer-calls"],"summary":"Join Peer Call Room","description":"Join the scheduled room for an accepted peer call.\nReturns a LiveKit token. Available 15 min before and during the session.","operationId":"join_peer_call_room_peer_calls__invite_id__join_room_post","parameters":[{"name":"invite_id","in":"path","required":true,"schema":{"type":"string","title":"Invite Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/progress/summary":{"get":{"tags":["progress"],"summary":"Get Progress Summary","description":"Aggregated progress data for the My Progress page.\n\nStays `async def` because the Premium ChaCha report branch awaits\n`_generate_chacha_report` (caught in production via hotfix-77 crash).\nThe 30s per-user cache still wins back the vast majority of repeat\nhits on the loop — only the cold-miss path runs the heavy aggregation.\n`regenerate=True` (Premium refresh) bypasses cache + invalidates.","operationId":"get_progress_summary_api_progress_summary_get","parameters":[{"name":"regenerate","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Regenerate"}},{"name":"role","in":"query","required":false,"schema":{"type":"string","default":"learner","title":"Role"}},{"name":"lang","in":"query","required":false,"schema":{"type":"string","default":"","title":"Lang"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/invites/my-code":{"get":{"tags":["invites","invites"],"summary":"Get My Referral Code","description":"Get or generate the current user's referral code.","operationId":"get_my_referral_code_invites_my_code_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/invites/call-invite":{"post":{"tags":["invites","invites"],"summary":"Create Call Invite","description":"Create a call invite (with optional scheduled time).","operationId":"create_call_invite_invites_call_invite_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCallInviteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/invites/info/{token}":{"get":{"tags":["invites","invites"],"summary":"Get Invite Info","description":"Public endpoint — returns invite details for the landing page.","operationId":"get_invite_info_invites_info__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/invites/{token}/accept":{"post":{"tags":["invites","invites"],"summary":"Accept Invite","description":"Accept an invite after signup — links referral and schedules call if needed.","operationId":"accept_invite_invites__token__accept_post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/invites/sent":{"get":{"tags":["invites","invites"],"summary":"Get Sent Invites","description":"List all invites sent by the current user.","operationId":"get_sent_invites_invites_sent_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/invites/{token}/cancel":{"post":{"tags":["invites","invites"],"summary":"Cancel Invite","description":"Cancel a pending invite.","operationId":"cancel_invite_invites__token__cancel_post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/invites/send-email":{"post":{"tags":["invites","invites"],"summary":"Send Invite Email","description":"Send (or resend) an invite email for an existing invite.","operationId":"send_invite_email_invites_send_email_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendInviteEmailRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/channels":{"get":{"tags":["channels","channels"],"summary":"List Channels","operationId":"list_channels_channels_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["channels","channels"],"summary":"Create Channel","description":"Create a new channel. Any authenticated user can create.","operationId":"create_channel_channels_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/channels/school":{"get":{"tags":["channels","channels"],"summary":"List School Channels","description":"School Spaces: the private channel(s) of the organization(s) the current\nuser belongs to. Auto-joins them as a ChannelMember (for unread tracking).\n\n`org_id` scopes the answer to ONE school, and the school surfaces pass it.\nWithout it this returns every school the viewer belongs to merged into one\nlist, which is right for the chat sidebar — that is the viewer's own list of\nplaces, not a page about one institution — and wrong everywhere else. A\ncoordinator of two schools opened one school's Channels tab and saw the\nother school's channels in it.\n\nNot a filter the client could have applied for itself: the auto-join below\nis a WRITE, so an unscoped call quietly enrols the viewer in every channel\nof every school they belong to whichever page they happened to open.\nScoping the query scopes the write with it.\n\nMembership is still what decides. `org_id` may only narrow what the viewer\nwould have been shown anyway — asking for a school you are not in returns\nnothing rather than 403, because the honest answer to \"your channels at that\nschool\" is that you have none, and a 403 would confirm the school exists.","operationId":"list_school_channels_channels_school_get","parameters":[{"name":"org_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Scope to ONE school. Omit for every school you are in.","title":"Org Id"},"description":"Scope to ONE school. Omit for every school you are in."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/channels/{channel_id}/notifications":{"put":{"tags":["channels","channels"],"summary":"Set Channel Notifications","description":"Whether this channel reaches your phone: {\"push\": true}.\n\nOff for everybody until they say otherwise. A channel message travelled over\nthe websocket only, so if the app was not open nothing arrived — a teacher\nposting to a class channel could not actually reach anybody. This is the\nopt-in that fixes that without turning every channel into a buzzing phone.\n\nSet on the MEMBERSHIP, so it is \"this person, in this channel\". You must\nalready be a member: joining is a separate act, and a bell on a channel you\nare not in would be a subscription by the back door.\n\nDeliberately OVERRIDES the global push categories (Nancy, 2026-08-28).\n`send_fcm_to_user` is called without a category for this path, so a channel\nsomebody switched on reaches them even if their broad \"school\" or \"messages\"\ntoggle is off — the reading being \"I want this one\". That is only safe\nbecause the default is off: every bell is a deliberate, specific act rather\nthan something they were enrolled in. The cost, stated so it is not a\nsurprise later: somebody who turns everything off in Settings still hears\nfrom a channel they turned on months ago.","operationId":"set_channel_notifications_channels__channel_id__notifications_put","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/channels/{channel_id}/join":{"post":{"tags":["channels","channels"],"summary":"Join Channel","operationId":"join_channel_channels__channel_id__join_post","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/channels/{channel_id}/leave":{"post":{"tags":["channels","channels"],"summary":"Leave Channel","operationId":"leave_channel_channels__channel_id__leave_post","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/channels/{channel_id}/messages":{"get":{"tags":["channels","channels"],"summary":"Get Channel Messages","operationId":"get_channel_messages_channels__channel_id__messages_get","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Before"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"default":50,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["channels","channels"],"summary":"Send Channel Message","operationId":"send_channel_message_channels__channel_id__messages_post","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/channels/my":{"get":{"tags":["channels","channels"],"summary":"List My Channels","description":"List channels the current user is a member of.","operationId":"list_my_channels_channels_my_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/channels/by-slug/{slug}":{"get":{"tags":["channels","channels"],"summary":"Get Channel By Slug","description":"Get channel details by slug, including recent messages.","operationId":"get_channel_by_slug_channels_by_slug__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/channels/by-slug/{slug}/join":{"post":{"tags":["channels","channels"],"summary":"Join Channel By Slug","description":"Join a channel by slug.","operationId":"join_channel_by_slug_channels_by_slug__slug__join_post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/channels/by-slug/{slug}/leave":{"post":{"tags":["channels","channels"],"summary":"Leave Channel By Slug","description":"Leave a channel by slug.","operationId":"leave_channel_by_slug_channels_by_slug__slug__leave_post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/channels/auto-join":{"post":{"tags":["channels","channels"],"summary":"Auto Join Channels","operationId":"auto_join_channels_channels_auto_join_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/matching/availability":{"get":{"tags":["matching","matching"],"summary":"Get Availability","description":"Get current user's availability slots.","operationId":"get_availability_api_matching_availability_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"put":{"tags":["matching","matching"],"summary":"Save Availability","description":"Save user's availability slots (replaces all existing).","operationId":"save_availability_api_matching_availability_put","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/matching/meet-sip":{"post":{"tags":["matching","matching"],"summary":"Find Peer Matches","description":"Find up to 3 matching language exchange partners.","operationId":"find_peer_matches_api_matching_meet_sip_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/matching/meet-sip/invite":{"post":{"tags":["matching","matching"],"summary":"Send Peer Invite","description":"Send a peer call invite to a matched partner.","operationId":"send_peer_invite_api_matching_meet_sip_invite_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/matching/find-tutor":{"post":{"tags":["matching","matching"],"summary":"Find Tutor Matches","description":"Find up to 3 matching tutors with overlapping availability.","operationId":"find_tutor_matches_api_matching_find_tutor_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/matching/find-tutor/book":{"post":{"tags":["matching","matching"],"summary":"Book Trial Lesson","description":"Book a 15-minute trial lesson with a matched tutor.","operationId":"book_trial_lesson_api_matching_find_tutor_book_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/polls":{"post":{"tags":["polls","polls"],"summary":"Create Poll","operationId":"create_poll_api_polls_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/polls/mine":{"get":{"tags":["polls","polls"],"summary":"List My Polls","operationId":"list_my_polls_api_polls_mine_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/polls/{poll_id}":{"get":{"tags":["polls","polls"],"summary":"Get Poll","operationId":"get_poll_api_polls__poll_id__get","parameters":[{"name":"poll_id","in":"path","required":true,"schema":{"type":"string","title":"Poll Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/polls/{poll_id}/summary":{"get":{"tags":["polls","polls"],"summary":"Get Poll Summary","description":"Cheap state check for the channel poll card (avoids fetching all options):\nhas the viewer responded, and is the poll finalized.","operationId":"get_poll_summary_api_polls__poll_id__summary_get","parameters":[{"name":"poll_id","in":"path","required":true,"schema":{"type":"string","title":"Poll Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/polls/{poll_id}/respond":{"post":{"tags":["polls","polls"],"summary":"Respond To Poll","operationId":"respond_to_poll_api_polls__poll_id__respond_post","parameters":[{"name":"poll_id","in":"path","required":true,"schema":{"type":"string","title":"Poll Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/polls/{poll_id}/finalize":{"post":{"tags":["polls","polls"],"summary":"Finalize Poll","operationId":"finalize_poll_api_polls__poll_id__finalize_post","parameters":[{"name":"poll_id","in":"path","required":true,"schema":{"type":"string","title":"Poll Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/polls/{poll_id}/reopen":{"post":{"tags":["polls","polls"],"summary":"Reopen Poll","description":"Creator undoes a finalized poll: cancels the scheduled session (removes the\nroom + its RSVPs) and either REOPENS it to pick a new time (default) or marks it\nCANCELLED (body {\"cancel\": true}).","operationId":"reopen_poll_api_polls__poll_id__reopen_post","parameters":[{"name":"poll_id","in":"path","required":true,"schema":{"type":"string","title":"Poll Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/chacha/memory":{"get":{"tags":["chacha-memory","chacha-memory"],"summary":"Get Memory","description":"The facts ChaCha currently remembers about the learner (editable).","operationId":"get_memory_api_chacha_memory_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/chacha/memory/facts":{"put":{"tags":["chacha-memory","chacha-memory"],"summary":"Replace Facts","description":"Replace the learner's facts with the edited list (add/edit/delete happen\nclient-side; this stores the result).","operationId":"replace_facts_api_chacha_memory_facts_put","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/reports":{"post":{"tags":["reports","reports"],"summary":"Create Report","operationId":"create_report_reports_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/reports":{"get":{"tags":["reports","reports"],"summary":"List Reports","operationId":"list_reports_admin_reports_get","deprecated":true,"parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"default":"open","title":"Status"}},{"name":"target_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Type"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/reports/{report_id}":{"patch":{"tags":["reports","reports"],"summary":"Update Report","operationId":"update_report_admin_reports__report_id__patch","deprecated":true,"parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"string","title":"Report Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users/{user_id}/assessment":{"get":{"tags":["reports","reports"],"summary":"Get User Assessment","description":"Run the bad-actor rubric against a single user and return a structured report.","operationId":"get_user_assessment_admin_users__user_id__assessment_get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users/lookup":{"get":{"tags":["reports","reports"],"summary":"Lookup User","description":"Find a user by email / username / name. Used for the manual-assessment lookup box.","operationId":"lookup_user_admin_users_lookup_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"title":"Q"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/users/{user_id}/ban":{"post":{"tags":["reports","reports"],"summary":"Ban User","description":"One-click ban — works for any user including tutors. Sets is_suspended=True,\ndeactivates the account, and stamps the reason. Idempotent.","operationId":"ban_user_admin_users__user_id__ban_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/internal/teababes/tick":{"post":{"tags":["teababes-internal","teababes-internal"],"summary":"Teababe Tick","description":"Run one floating-activity tick. Idempotent and self-rate-limited via Redis;\nsafe to call on a fixed schedule.","operationId":"teababe_tick_internal_teababes_tick_post","parameters":[{"name":"x-internal-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Internal-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/mine":{"get":{"tags":["organizations","organizations"],"summary":"My Organizations","description":"The current user's organizations + their role (drives the School Space UI).","operationId":"my_organizations_organizations_mine_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/by-slug/{slug}/join":{"get":{"tags":["organizations","organizations"],"summary":"Join Info","description":"Public: resolve a school slug to its branding + active join code.\n\nPowers the /join/<slug> landing page so incoming students get a link with\nthe school baked in (no code to type). Returns the org-stamped invite code\nto stash client-side; redemption (which creates the OrganizationMember and\ngrants premium) still happens through the normal redeem path after signup.\nInvite codes are meant to be distributed in the school's welcome email, so\nsurfacing one for a known slug is intentional, not a leak.","operationId":"join_info_organizations_by_slug__slug__join_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/branding":{"get":{"tags":["organizations","organizations"],"summary":"Public Branding","description":"Public: branding for a school slug, for the LOGIN and signup screens.\n\nDeliberately separate from `/by-slug/{slug}/join`, in two ways that matter:\n\n* it never returns an invite code. The join page hands one out on purpose\n  (the school mails that link out); a login screen has no business minting\n  one, so this endpoint carries branding and nothing else.\n* it ALWAYS returns 200. A login screen has no school context of its own —\n  the slug arrives as `?school=` on the URL the school sent, or is\n  remembered client-side from an earlier /join/<slug> visit, and very often\n  is simply absent. Unknown, inactive and missing all resolve to FluenTea's\n  own branding rather than an error, because the correct answer to \"which\n  university is this student from?\" when we do not know is *ours*: showing a\n  student the wrong university's logo is far worse than showing them none.","operationId":"public_branding_organizations_branding_get","parameters":[{"name":"slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/by-slug/{slug}/current-session":{"get":{"tags":["organizations","organizations"],"summary":"Current Session","description":"Public: resolve a school slug to its live/imminent session room, if any.\n\nPowers the \"straight into the call\" behavior of the /join/<slug> landing page.\nA school's weekly session gets a freshly generated room + rotating join_code\neach occurrence, so there is no durable per-room link — the durable entry point\nis the slug, resolved here at click time.\n\nReturns the joinable room (with its current join_code) when one is live or\nwithin its 15-minute pre-start window, so the landing page can redirect the\nvisitor — member or guest — straight in. Otherwise returns the next upcoming\nsession time for the welcome card. No auth: guests hit this before signing up.\n\n`code` is the GROUP code off the join link (/join/<slug>?code=VPSHVJ), and it\nis the only thing that identifies WHICH SECTION the visitor belongs to before\nthey have an account. Without it this endpoint could only answer about the\nschool as a whole, so a student holding a Grupo 1 link was shown Grupo 2's\nnext class — the section they are not in, at a time they should ignore.\n`_viewer_group_ids` cannot help here: the person reading this page usually has\nno membership row yet, which is the entire reason they were sent a link.\n\nAn unknown or expired code is ignored rather than refused. This is a public\npage whose job is to get somebody into a class; degrading to the school-wide\nanswer is the correct failure, and a 404 here would strand a student whose\nteacher rotated the code.","operationId":"current_session_organizations_by_slug__slug__current_session_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations":{"post":{"tags":["organizations","organizations"],"summary":"Create Org","description":"Create a school. FluenTea platform staff only (`ADMIN_USER_IDS`).\n\nA school is SOLD, not signed up for. That is a commercial decision, and it\nis also the only one this endpoint can safely encode, because membership of\nan organization IS an entitlement: `usage_limiter._get_effective_tier`\nhands every active member `org.member_tier`. An open create endpoint is a\nbutton that provisions a paid product for whoever clicks it and everybody\nthey invite.\n\nA short-lived self-serve version of this endpoint existed (7203678ff) and\nwas reverted here on the founder's call. What survives from it is the part\nthat was about correctness rather than about who may click:\n\n  * THE CLOCK IS ASKED FOR, and refused empty. A school that never answers\n    this question sets its whole first term's deadlines in UTC and every\n    \"late\" is computed against the wrong hour (JOURNEY-15). It was possible\n    to create a school without one; it is not any more.\n  * THE ADDRESS IS DERIVED, never supplied. `/join/<slug>` is a public\n    namespace read aloud to students; nobody on the far side of HTTP picks\n    an entry in it, not even us. `seed_organization.py` still calls the\n    service directly when a curated slug is genuinely wanted.\n\nOwner: `owner_user_id`, or `owner_email` resolved against the account\nbehind it — a staff member setting up a signed school has the\ncoordinator's address in the contract, not their UUID. Defaults to the\ncaller, which is what the demo and load-test schools want.","operationId":"create_org_organizations_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}":{"get":{"tags":["organizations","organizations"],"summary":"Get Org","operationId":"get_org_organizations__org_id__get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/members":{"get":{"tags":["organizations","organizations"],"summary":"Members","description":"The school's people, scoped to what this staff member is answerable for.\n\nOwner/admin and platform staff read the whole school WITH email addresses:\nthey administer accounts, and `POST /members` — the flow this same screen\ncarries — is keyed on an email, so the column is the thing they work from.\n\nA plain teacher reads THEIR OWN sections' students plus the school's staff,\nand no email addresses at all. Two separate judgements, both deliberate:\n\n  * scope — the rail one inch above this list says \"YOUR SECTIONS\", and the\n    product refuses this teacher the other section's gradebook, roster and\n    assignments (`_require_group_staff`). A flat school directory behind\n    the same login is the same leak by another door: 23 students' names to\n    somebody responsible for 12 of them.\n  * emails — nothing a teacher DOES here consumes one. Announcements fan\n    out to each student's own address without the teacher ever seeing it\n    (that is the whole point of the one compose box), a question is a DM,\n    and adding or removing a member is `_require_admin`. An address the UI\n    never uses is personal data we are storing on a screen for decoration,\n    which is precisely what a school's data-protection review asks about.\n    `/roster` has drawn this same line since it shipped (\"privacy-filtered,\n    no emails\"); this is the staff-side half of it.\n\nA teacher in a school with NO sections at all still gets everyone — the\nGuanajuato rule `_resolve_class_scope` already follows, because a school\nthat never adopted sections must not have its people page silently blanked.\nA teacher in a school that DOES use sections but staffs none of them is a\nteacher with no class, and correctly sees staff only.","operationId":"members_organizations__org_id__members_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["organizations","organizations"],"summary":"Add Member","description":"One person into this school — the same row a pasted roster is made of.\n\nThis used to refuse anybody without a FluenTea account: `404 No user with\nthat email`. Beside it, `/enrolment/commit` took fifty strangers and CREATED\nfifty accounts. Two doors, opposite rules, and the rule was invisible until\nit failed — a coordinator could paste a hundred people who had never heard\nof us, and be refused the hundred-and-first typed on its own.\n\nSo an address that belongs to nobody is provisioned here exactly as it is in\na paste: `school_enrolment_service` does the classification, the account\ncreation, the activation token and the invitation, and the response carries\nthe activation link so a coordinator can hand it out on paper. There is one\nimplementation, and the outcome words are the list's own — `created`,\n`enrolled`, `section_added`, `already`, `invalid`, `failed`.\n\nTHIS DOOR IS NOT A BULK DOOR. It is the only place a coordinator or an owner\nmay be appointed, because it takes ONE address and runs the role ladder\n(`_require_role_grant`) against that one named person first. A list may only\nhand out `ENROLABLE_ROLES` (student, teacher) and that has not changed —\nwhich is exactly why the widened vocabulary is passed in here rather than\nread from the service: paste two lines into this box and it is a 400, not\ntwo coordinators.\n\nAdding somebody who is already a member reports `already` and rewrites\nnothing — not their role, not their name, not their section elsewhere. A\ncoordinator typing their own teacher's address into an add box is not asking\nto demote them. Re-roling is the PATCH below, a deliberate act with its own\nauthorization.\n\n`user_id` still works (the id-keyed door, used by operators), and so does\n`premium_until`. `group_id` puts them in a section in the same act.","operationId":"add_member_organizations__org_id__members_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/roster":{"get":{"tags":["organizations","organizations"],"summary":"Roster","description":"Classmate roster any member can see (privacy-filtered, no emails).","operationId":"roster_organizations__org_id__roster_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/members/{user_id}":{"patch":{"tags":["organizations","organizations"],"summary":"Update Member","description":"Change what a member IS ({\"role\": \"teacher\"}) or what they are CALLED\n({\"full_name\": \"Ana Ruiz\"}). Two fields, two different ladders.\n\nROLE is owner/admin, through `_require_role_grant`. The missing half of\nmaking a teacher: the People tab could only create students and the section\nstaff list only accepts non-students, so there was no path between them and\nevery school onboarding needed an engineer with a SQL prompt open.\n\nFULL_NAME is `_require_may_rename` — coordinators school-wide, teachers over\ntheir own sections' students. It exists because a name can now be WRONG\nthrough no fault of the person wearing it: onboarding scratch state was kept\nin `localStorage` under a fixed key with nothing tying it to an account, so\ntwo students sharing a lab computer produced two accounts carrying the first\none's name (`src/utils/profileScratch.js` is the fix; this is the repair).\nBoth may be sent in one call; each is authorized on its own.","operationId":"update_member_organizations__org_id__members__user_id__patch","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["organizations","organizations"],"summary":"Remove Member","description":"Take somebody out of the school.\n\nStaff, not only the front office (Nancy, 2026-08-31), but a teacher may\nremove STUDENTS only: handing them the power to remove a colleague or a\ncoordinator is a different decision that nobody has made.\n\nThe person is told. Losing your school's plan without being told why is\nthe kind of silence that reads as a bug to the person it happens to.","operationId":"remove_member_organizations__org_id__members__user_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/faq":{"put":{"tags":["organizations","organizations"],"summary":"Set Faq","operationId":"set_faq_organizations__org_id__faq_put","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/timezone":{"put":{"tags":["organizations","organizations"],"summary":"Set Org Timezone","description":"Set the school's clock: {\"timezone\": \"America/Mexico_City\"}.\n\nAdmin-only, and an IANA name only — deadlines are the school's time for\neveryone, so this is the one value that decides what \"due Friday\" means.","operationId":"set_org_timezone_organizations__org_id__timezone_put","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/student-wall":{"put":{"tags":["organizations","organizations"],"summary":"Set Student Wall","description":"Raise or lower this school's student wall: {\"students_walled\": true}.\n\nThe rule lives in `services/student_wall.py`, written for Ingrid's under-18s\nat TESI: a walled school's STUDENTS may reach only people who share one of\ntheir schools, and only students — staff keep the whole product. What was\nmissing was any way to turn it on. The flag existed, the server enforced it,\nthe nav hid CommuniTea for it, and the only way to set it was an UPDATE\ntyped by an engineer against the database. A safeguarding control nobody can\nswitch on is a safeguarding control that is off, and TESI started enrolling\nwith it off.\n\nOwner/admin, like every other school setting. That is a deliberate call over\n\"platform staff only\": the person accountable for those students is the\ncoordinator, and making them file a ticket to protect their own minors is\nhow a term starts unwalled. Symmetric, too — a school that can raise the\nwall can lower it. A coordinator locked out of undoing their own switch is\na support ticket by another route.","operationId":"set_student_wall_organizations__org_id__student_wall_put","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/plans-required":{"put":{"tags":["organizations","organizations"],"summary":"Set Org Plans Required","description":"Does this school want a FluenTea plan for every class?\n{\"plans_required\": true|false}\n\nMariana (advisor): \"why does it have to be planned on FluenTea? Just\nbecause it is not on fluentea, it does not mean it is not planned.\" A school\nthat plans in its own system was being told, in red, that its week had\nfailed — and its teachers nudged about work they had already done somewhere\nelse. Turning this off drops the red banner, the \"Unplanned\" alarm and the\nnudge; the coverage grid still SHOWS which classes have a plan here, because\nthat is a fact either way.\n\nAdmin-only: it changes what the whole school is told about itself.","operationId":"set_org_plans_required_organizations__org_id__plans_required_put","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/languages":{"put":{"tags":["organizations","organizations"],"summary":"Set Org Languages","description":"The school's two languages: {\"language\": \"English\",\n                                \"student_native_language\": \"Spanish\"}.\n\nThey are not the same language and conflating them is the bug this exists\nto prevent. `language` is what the school TEACHES — it stamps the channels\nand rooms the school creates. `student_native_language` is what its students\nREAD, and it decides what language we write to them in when their account\nis too new to have a preference of its own. A Mexican academy teaching\nEnglish answers \"English\" and \"Spanish\".\n\nEither key may be omitted to leave it alone, or sent empty to clear it.\nClearing the student language means new students are written to in English.","operationId":"set_org_languages_organizations__org_id__languages_put","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/resources":{"put":{"tags":["organizations","organizations"],"summary":"Set Resources","description":"Replace the org's curated student resources (welcome videos, documents, links).","operationId":"set_resources_organizations__org_id__resources_put","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/branding":{"put":{"tags":["organizations","organizations"],"summary":"Set Branding","description":"Set the school's accent colour, logo URL, cover image and logo shape.\n\nOwner/admin only — this changes what every student of the school sees on\nevery screen, which is a settings-level act, not a teaching one (a teacher\ncan edit the FAQ and resources; they cannot restyle the institution).\n\nPartial: only the keys present change. An explicit null clears one back to\nthe product default. `accent_ink`, `accent_on_surface` and `accent_tint` are\nNOT accepted — they are derived from `accent_color` server-side so that no\nclient can put an unreadable pair into the row.","operationId":"set_branding_organizations__org_id__branding_put","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/logo":{"post":{"tags":["organizations","organizations"],"summary":"Upload Logo","description":"Upload the school's logo. Owner/admin only. PNG/JPEG/WebP, max 2MB.\n\nRe-encoded to WebP at most 512px on the long edge, with transparency kept,\nand the shape ('wordmark' | 'square') and alpha measured from the pixels —\na frontend has to pick the logo's slot before the image loads and cannot\ninfer either from a URL.","operationId":"upload_logo_organizations__org_id__logo_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_logo_organizations__org_id__logo_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/cover":{"post":{"tags":["organizations","organizations"],"summary":"Upload Cover","description":"Upload the school's cover photo. Owner/admin only. PNG/JPEG/WebP, max 8MB.\n\n`PUT /branding` has always taken `cover_image_url`, which is the right shape\nfor a school that has its banner on its own web server and the wrong one for\nevery other school — a coordinator has a JPEG, not a URL, and until this\nexisted the only way to set a cover was a seeding script (see\n`seed_demo_school.seed_branding`, which says so). The logo had an upload and\nthe cover did not, so half the branding screen could not be operated by the\nperson it is for.\n\nFlattened onto white and capped at 1600px wide: the same \"what every student\ndownloads\" argument the logo's 512px cap is made of, one order of magnitude\nup because this one is a photograph.","operationId":"upload_cover_organizations__org_id__cover_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_cover_organizations__org_id__cover_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/channel":{"post":{"tags":["organizations","organizations"],"summary":"Create Channel","operationId":"create_channel_organizations__org_id__channel_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/channels":{"get":{"tags":["organizations","organizations"],"summary":"List Org Channels","description":"Every channel of the school, for a staff management screen.\n\nStudents do not use this — they get their own channels, already joined,\nfrom `GET /channels/school`. `can_manage` is per row because a teacher may\nrename or delete only what belongs to a section they staff, and\n`is_default` marks the two automatic kinds, which nobody may touch.","operationId":"list_org_channels_organizations__org_id__channels_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["organizations","organizations"],"summary":"Create Org Channel","description":"Open a named channel. Body: {name, group_id?, description?, member_ids?}.\n\n`group_id` absent/null = school-wide (every member sees it); set = scoped to\nthat section (its students, plus school staff). `member_ids` non-empty =\na hand-picked channel for exactly those people (plus the creator); it\nignores group_id, and ANY staff role may open one — the audience is chosen,\nso there is no scope to guard.","operationId":"create_org_channel_organizations__org_id__channels_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/channels/{channel_id}/members":{"get":{"tags":["organizations","organizations"],"summary":"List Channel Members","description":"Who is in this channel. Nancy, 2026-08-25: \"for existing channels, we\nalso need a way to add new people to the channel and see who is in it.\"\n\n`can_manage` says whether the Add control should be drawn, and\n`follows_scope` says why there may be no Add control at all: a school-wide\nor section channel's membership is its scope, and the list is a record of\nwho has actually landed in it rather than who belongs there.","operationId":"list_channel_members_organizations__org_id__channels__channel_id__members_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["organizations","organizations"],"summary":"Add Channel Members","description":"Add people to a hand-picked channel. Body: {user_ids: [...]}.\n\nWhoever is already in it may add — Nancy: \"specific people channels should\nbe private so only people who are already in it can add new people\". Not the\ncoordinator from outside, even though they can read it: reading a room for\noversight and deciding who else is in it are different powers.","operationId":"add_channel_members_organizations__org_id__channels__channel_id__members_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/channels/{channel_id}/members/{user_id}":{"delete":{"tags":["organizations","organizations"],"summary":"Remove Channel Member","description":"Take somebody out again.\n\nStricter than adding on purpose. Adding is an invitation and anybody in the\nroom may extend one; removing is throwing a person out of a conversation\nthey are part of, so it stays with the person who opened the channel or a\nmember who staffs the school. Otherwise a student could remove their\nteacher from the room the teacher opened.","operationId":"remove_channel_member_organizations__org_id__channels__channel_id__members__user_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/channels/{channel_id}":{"patch":{"tags":["organizations","organizations"],"summary":"Update Org Channel","description":"Rename a staff-created channel, or change its description. The scope\n(school-wide vs section) is fixed at creation: moving a channel between\nsections would move its history along with it, in front of students who\nwere never in the conversation.","operationId":"update_org_channel_organizations__org_id__channels__channel_id__patch","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["organizations","organizations"],"summary":"Delete Org Channel","description":"Delete a staff-created channel — but only while it is still empty.\n\nA channel opened by mistake has to be removable or it is a support ticket.\nA channel with a conversation in it is a different object: `messages` has no\ncascade to lean on, so deleting one is either a crash or, once cascaded, a\nsection's history silently gone. Archiving is the right answer for that and\nis deliberately NOT in this pass — it needs a column on `channels` and a\nread filter everywhere channels are listed.","operationId":"delete_org_channel_organizations__org_id__channels__channel_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/session-series":{"get":{"tags":["organizations","organizations"],"summary":"List Session Series","operationId":"list_session_series_organizations__org_id__session_series_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["organizations","organizations"],"summary":"Create Session Series","operationId":"create_session_series_organizations__org_id__session_series_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/session-series/{series_id}":{"delete":{"tags":["organizations","organizations"],"summary":"Delete Session Series","operationId":"delete_session_series_organizations__org_id__session_series__series_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"series_id","in":"path","required":true,"schema":{"type":"string","title":"Series Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/stats":{"get":{"tags":["organizations","organizations"],"summary":"Org Stats","description":"School-admin dashboard stats (owner/admin/teacher or staff).","operationId":"org_stats_organizations__org_id__stats_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/pilot-report":{"get":{"tags":["organizations","organizations"],"summary":"Org Pilot Report","description":"Director-facing pilot summary across all the org's class rooms + breakouts:\nsessions run, participation, aggregate outcomes, shared weaknesses, and a\nshort narrative. Owner/admin/teacher (or staff) only.","operationId":"org_pilot_report_organizations__org_id__pilot_report_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":90,"title":"Days"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/classes":{"get":{"tags":["organizations","organizations"],"summary":"List Org Classes","description":"The org's class sessions (top-level rooms, newest first) for the school UI.\n\nA class session = a VoiceChatroom with organization_id == this org and\nparent_room_id NULL (breakouts are its children, not separate classes).\nOwner/admin/teacher of the org (or platform staff) only.\n\nOptionally scoped to one section; a teacher with no explicit `group_id`\ndefaults to the sections they teach (see `_resolve_class_scope`). Rooms with\n`group_id` NULL — everything from before the column existed, plus school-wide\nsessions — are INCLUDED under a scope by default, because a section filter\nmust not make a school's history disappear. `include_unattributed=false`\nnarrows to rooms explicitly attributed to the section.","operationId":"list_org_classes_organizations__org_id__classes_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Group Id"}},{"name":"include_unattributed","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Include Unattributed"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/attendance-summary":{"get":{"tags":["organizations","organizations"],"summary":"Org Attendance Summary","description":"Roster x class-session attendance matrix for required-coursework proof.\n\nWho attended each of the org's recent class sessions, how long they stayed,\nand who was a no-show (roster students who never joined). Does NOT require\nthe teacher to have been in the calls. Owner/admin/teacher (or staff) only.\n\nScoping mirrors `/classes`: explicit `group_id`, else a teacher's own\nsections, else school-wide. Under a scope the ROSTER narrows to those\nsections' members too — a Grupo 1 attendance sheet listing Grupo 2's students\nas no-shows is worse than useless as coursework proof. Unattributed rooms\n(group_id NULL) stay in the columns unless `include_unattributed=false`.","operationId":"org_attendance_summary_organizations__org_id__attendance_summary_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}},{"name":"min_minutes","in":"query","required":false,"schema":{"type":"integer","default":1,"title":"Min Minutes"}},{"name":"group_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Group Id"}},{"name":"include_unattributed","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Include Unattributed"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/tutors/{tutor_user_id}":{"post":{"tags":["organizations","organizations"],"summary":"Affiliate Tutor","operationId":"affiliate_tutor_organizations__org_id__tutors__tutor_user_id__post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"tutor_user_id","in":"path","required":true,"schema":{"type":"string","title":"Tutor User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups":{"get":{"tags":["organizations","organizations"],"summary":"List Groups","description":"Group list with join codes + member counts (staff only — codes grant entry).","operationId":"list_groups_organizations__org_id__groups_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["organizations","organizations"],"summary":"Create Group","operationId":"create_group_organizations__org_id__groups_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}":{"patch":{"tags":["organizations","organizations"],"summary":"Update Group","operationId":"update_group_organizations__org_id__groups__group_id__patch","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/level":{"put":{"tags":["organizations","organizations"],"summary":"Set Group Level","description":"The class's CEFR level: {\"level\": \"A1\"} — or null to clear it.\n\nIts own endpoint, and STAFF of the section rather than `_require_admin`,\nwhich is what the rest of `PATCH /groups/{id}` takes. Renaming a section or\ndeactivating it is the coordinator's business; what level the class is at is\nthe teacher's, they are the one who finds out in week one, and behind an\nadmin-only screen the answer was invisible to the person who knows it\n(Nancy, 2026-08-26: \"there is no section called section details\" — as a\nteacher, there wasn't).","operationId":"set_group_level_organizations__org_id__groups__group_id__level_put","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/reset-code":{"post":{"tags":["organizations","organizations"],"summary":"Reset Group Code","description":"New join code for a leaked one (Classroom 'reset code' pattern).","operationId":"reset_group_code_organizations__org_id__groups__group_id__reset_code_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/members":{"post":{"tags":["organizations","organizations"],"summary":"Add Group Member","operationId":"add_group_member_organizations__org_id__groups__group_id__members_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/members/{user_id}":{"delete":{"tags":["organizations","organizations"],"summary":"Remove Group Member","operationId":"remove_group_member_organizations__org_id__groups__group_id__members__user_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/members/{user_id}/move":{"post":{"tags":["organizations","organizations"],"summary":"Move Group Member","description":"Move a student from this section to another: {\"to_group_id\": \"...\"}.\n\nDoing this as a remove and an add was always possible and is the reason\nthis exists: between the two calls the student belongs to no section, and\nif the second one fails they simply vanish from the school's roster with\nnobody notified. One call, one transaction, both or neither.\n\nStaff on BOTH sections is required, and the destination is the half that\nmatters. A teacher may take a student out of their own class; putting one\nINTO a class they do not teach is filling somebody else's roster, which is\nexactly the boundary `_require_group_staff` draws everywhere else.","operationId":"move_group_member_organizations__org_id__groups__group_id__members__user_id__move_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/import-roster":{"post":{"tags":["organizations","organizations"],"summary":"Import Group Roster","description":"Bulk-assign existing users by email. Body: {\"rows\": [{\"email\": ...}]}.\nUnmatched emails come back in the response — a typo never silently drops\na student (Oxford CSV-import pattern, with recovery).","operationId":"import_group_roster_organizations__org_id__groups__group_id__import_roster_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/enrolment/preview":{"post":{"tags":["organizations","organizations"],"summary":"Preview Enrolment","description":"What WOULD happen, row by row. Writes nothing.\n\nBody: {\"text\": \"pasted list or CSV\"} or {\"rows\": [{email, name?, role?}]},\nplus optional \"group_id\" and \"role\" (the default for rows that carry none).\n\nA coordinator will paste the wrong column at least once. This is the screen\nwhere she finds that out, and it is computed by the same `classify()` the\ncommit runs, so the table she confirms cannot disagree with the work.","operationId":"preview_enrolment_organizations__org_id__enrolment_preview_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/enrolment/commit":{"post":{"tags":["organizations","organizations"],"summary":"Commit Enrolment","description":"Create the accounts, enrol everybody, and send each new student a link\nthat sets their first password.\n\nThe response carries every activation link, deliberately. Schools hand these\nout on paper and at induction sessions as often as by email, and a school\nwhose student addresses are aliases nobody reads is a real school — a\ncoordinator who cannot recover the links is a coordinator waiting on 700\ninboxes again, which is the thing this endpoint exists to end. It is\nowner/admin-only over TLS, which is the same footing as the email column on\nthe People tab.","operationId":"commit_enrolment_organizations__org_id__enrolment_commit_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/breakout-layouts":{"get":{"tags":["organizations","organizations"],"summary":"List Group Breakout Layouts","description":"Groupings saved for this section. Staff only.","operationId":"list_group_breakout_layouts_organizations__org_id__groups__group_id__breakout_layouts_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["organizations","organizations"],"summary":"Save Group Breakout Layout","description":"Save a grouping, or replace one. `{name, rooms: [[user_id, ...], ...], id?}`\n\nStaff only. Nothing here checks that the named students are members of the\nsection: a teacher preparing groups on Sunday for a roster that changes on\nMonday would be blocked by that check for no gain, and `reconcile` handles\nthe mismatch at the only moment it matters.","operationId":"save_group_breakout_layout_organizations__org_id__groups__group_id__breakout_layouts_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/breakout-layouts/{layout_id}":{"delete":{"tags":["organizations","organizations"],"summary":"Delete Group Breakout Layout","description":"Forget a grouping. Staff only.","operationId":"delete_group_breakout_layout_organizations__org_id__groups__group_id__breakout_layouts__layout_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"layout_id","in":"path","required":true,"schema":{"type":"string","title":"Layout Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/staff":{"get":{"tags":["organizations","organizations"],"summary":"List Group Staff","description":"This section's teaching staff, lead first.","operationId":"list_group_staff_organizations__org_id__groups__group_id__staff_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["organizations","organizations"],"summary":"Add Group Staff","description":"Body: {user_id | email, staff_role?: lead|co_teacher|assistant|substitute}.\n`lead` moves the teacher of record (and `organization_groups.teacher_id`).","operationId":"add_group_staff_organizations__org_id__groups__group_id__staff_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/staff/{user_id}/lead":{"put":{"tags":["organizations","organizations"],"summary":"Set Group Lead","description":"Promote this person to lead. The sitting lead becomes a co-teacher; the\nstaff row and `teacher_id` move in one transaction.","operationId":"set_group_lead_organizations__org_id__groups__group_id__staff__user_id__lead_put","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/staff/{user_id}":{"delete":{"tags":["organizations","organizations"],"summary":"Remove Group Staff","operationId":"remove_group_staff_organizations__org_id__groups__group_id__staff__user_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/join-group":{"post":{"tags":["organizations","organizations"],"summary":"Join Group By Code","description":"Single join action: group code grants org + group membership.","operationId":"join_group_by_code_organizations_join_group_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/mine":{"get":{"tags":["organizations","organizations"],"summary":"My Group","operationId":"my_group_organizations__org_id__groups_mine_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/my-day":{"get":{"tags":["organizations","organizations"],"summary":"My Day","description":"One read for everything school-related the learner dashboard shows.\n\nThe dashboard is the hottest page in the product, so school gets exactly\none request there — not one per concept. This returns the ranked work\nitems, the honest counts behind them, the section payload the dashboard\nused to fetch from `/groups/mine` (so a school student's request count is\nunchanged, not one higher), and the annotation hooks that let the existing\nlesson and next-up surfaces carry \"assigned by Marco, due Friday\" instead\nof a bolted-on school widget.\n\nSections come from `_viewer_group_ids`, the same gate `_require_group_access`\nenforces per section — a student sees their own sections' work and nothing\nelse. Owners and admins have no sections of their own and correctly get an\nempty day; they run the school from /school, not from here.","operationId":"my_day_organizations__org_id__my_day_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/announcements":{"post":{"tags":["organizations","organizations"],"summary":"Create Announcement","description":"Staff broadcast. Body: {title?, body, group_ids?: [..], important?} —\nno group_ids means org-wide; several ids fan out one announcement per\nsection (Classroom multi-class 'For' pattern). `important` is the only\ndelivery control a teacher gets, and it is about the MESSAGE, not the\nchannel: it reaches students on the daily digest right away.\n\nThe audience is authorized by `_require_announcement_scope`, not by\n`_require_staff_role`: \"is a teacher somewhere in this school\" was standing\nin for \"may address these people\", which is the same substitution\n`_require_group_staff` was written to end on the section endpoints.","operationId":"create_announcement_organizations__org_id__announcements_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["organizations","organizations"],"summary":"List Announcements","operationId":"list_announcements_organizations__org_id__announcements_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/announcements/preview":{"post":{"tags":["organizations","organizations"],"summary":"Preview Announcement Audience","description":"How many students this reaches, and on which channels, BEFORE sending.\n\nPOST rather than GET because the audience is a list of group ids and a\nteacher can pick a dozen sections. Body: {group_ids?: [..]}.\n\nGated by the SAME rule as the send. A preview that happily counts an\naudience the send will refuse is how the composer came to advertise \"Goes\nto 25 people\" to a teacher of twelve.","operationId":"preview_announcement_audience_organizations__org_id__announcements_preview_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"default":{},"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/announcements/{announcement_id}/read":{"post":{"tags":["organizations","organizations"],"summary":"Mark Announcement Read","operationId":"mark_announcement_read_organizations__org_id__announcements__announcement_id__read_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"announcement_id","in":"path","required":true,"schema":{"type":"string","title":"Announcement Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/announcements/{announcement_id}/summary":{"get":{"tags":["organizations","organizations"],"summary":"Announcement Summary","description":"Who received and who read one announcement — BY NAME.\n\nScoped like the send, plus the author: a delivery summary is a named list\nof students who have not read something, which is exactly the roster a\nplain teacher does not get for a section they do not staff. Without this,\nthe send gate would have been walkable by reading the receipts instead.","operationId":"announcement_summary_organizations__org_id__announcements__announcement_id__summary_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"announcement_id","in":"path","required":true,"schema":{"type":"string","title":"Announcement Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/response-hours":{"put":{"tags":["organizations","organizations"],"summary":"Set Response Hours","description":"Staff set their OWN availability windows; null body clears them.","operationId":"set_response_hours_organizations__org_id__response_hours_put","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/response-state/{user_id}":{"get":{"tags":["organizations","organizations"],"summary":"Get Response State","description":"Member-visible: a staff member's hours + whether they are open now.\nPowers the student-side soft-gate banner ('Ms. García replies 2-4 PM').","operationId":"get_response_state_organizations__org_id__response_state__user_id__get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/dashboard-config":{"put":{"tags":["organizations","organizations"],"summary":"Set Group Dashboard Config","description":"ORPHANED 2026-08-18 — no client calls this route any more.\n\nIt let staff curate the tiles on their students' FluenTea dashboards\n(dev-log P1 #13). The frontend deliberately no longer offers that: a\nstudent's FluenTea dashboard is the student's personal space, and the\nschool's demands belong in the school space. The teacher-side editor,\nthe student-side consumer and `setGroupDashboardConfig` in\nsrc/services/organizationService.js were all removed.\n\nKept intact and working (dropping `organization_groups.dashboard_config`\nneeds a migration). Do NOT re-wire a caller — this was a product\ndecision, not an oversight.","operationId":"set_group_dashboard_config_organizations__org_id__groups__group_id__dashboard_config_put","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/report-preferences":{"put":{"tags":["organizations","organizations"],"summary":"Set Report Preferences","description":"Staff set their OWN report sections (dev-log P1 #14).","operationId":"set_report_preferences_organizations__org_id__report_preferences_put","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["organizations","organizations"],"summary":"Get Report Preferences","operationId":"get_report_preferences_organizations__org_id__report_preferences_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments":{"post":{"tags":["organizations","organizations"],"summary":"Create Group Assignment","description":"Body: {title, passage_ids?|items?, unlock_at?, lock_at?, instructions?,\ndue_at?, points_possible?, grading_mode?, allow_late?, status?, topic?}.\n\nAn `items` entry is one of:\n  {kind:'lesson', lesson_passage_id}\n  {kind:'file'|'link', title, url}\n  {kind:'practice', practice_mode, title, min_minutes|min_turns,\n   target_vocab?, target_grammar?}   ← assigned speaking practice\n\n`unlock_at`/`lock_at` are the availability window; `due_at` is the\nDEADLINE and is a different thing — work stays submittable past it (that is\nwhat \"late\" means) unless `allow_late` is false. Bare datetimes are read in\nthe SCHOOL's timezone, not the server's. The availability window is also the\nwindow assigned practice is verified inside (school_practice_service).\n\nCreating as `published` (the default) notifies the section; create as\n`draft` and call `/publish` when you are ready for that.","operationId":"create_group_assignment_organizations__org_id__groups__group_id__assignments_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["organizations","organizations"],"summary":"List Group Assignments","description":"Members of THIS section see its unit tree with lock states and their own\ncompletion; locked units are visible but not openable (client enforces).\n\nGroup-scoped, not org-scoped: a student of another section gets 403.\nDrafts are staff-only — an unpublished assignment does not exist to a\nstudent, so it is filtered out of the list rather than merely flagged.","operationId":"list_group_assignments_organizations__org_id__groups__group_id__assignments_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/{assignment_id}":{"patch":{"tags":["organizations","organizations"],"summary":"Update Group Assignment","description":"Partial edit. Every field is optional and absent means \"leave it\";\nan explicit null clears it (that is why the sentinel is not None).\n\nFlipping `status` draft → published here notifies the section, exactly as\n`/publish` does — a teacher should not be able to publish by accident\nthrough a different door and have nobody told.","operationId":"update_group_assignment_organizations__org_id__groups__group_id__assignments__assignment_id__patch","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["organizations","organizations"],"summary":"Delete Group Assignment","operationId":"delete_group_assignment_organizations__org_id__groups__group_id__assignments__assignment_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/gradebook":{"get":{"tags":["organizations","organizations"],"summary":"Group Gradebook","description":"Staff: students × assignments completion grid + time-on-task +\nclass average (Oxford gradebook shape).","operationId":"group_gradebook_organizations__org_id__groups__group_id__gradebook_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/curriculum-rollup":{"get":{"tags":["organizations","organizations"],"summary":"Curriculum Rollup","description":"Coordinator view: one row per group (size, assignments, avg completion).","operationId":"curriculum_rollup_organizations__org_id__curriculum_rollup_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/announcements/{announcement_id}":{"patch":{"tags":["organizations","organizations"],"summary":"Edit Announcement","description":"Author or admin edits in place; shows '(edited)'. Pushes/emails\nalready delivered stay delivered.","operationId":"edit_announcement_organizations__org_id__announcements__announcement_id__patch","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"announcement_id","in":"path","required":true,"schema":{"type":"string","title":"Announcement Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["organizations","organizations"],"summary":"Delete Announcement","operationId":"delete_announcement_organizations__org_id__announcements__announcement_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"announcement_id","in":"path","required":true,"schema":{"type":"string","title":"Announcement Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/{assignment_id}/submit":{"post":{"tags":["organizations","organizations"],"summary":"Submit Assignment Solution","description":"Student turns in work: {file_url?} (from POST /upload/assignment-file)\nand/or {note?}. Resubmitting overwrites and flips back to turned_in.\n\nGroup-scoped for the same reason the read is (B2): org membership alone\nwould let a student of another section write into this one's submissions.","operationId":"submit_assignment_solution_organizations__org_id__groups__group_id__assignments__assignment_id__submit_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/{assignment_id}/submissions":{"get":{"tags":["organizations","organizations"],"summary":"Assignment Submissions","description":"Teacher review list: turned_in / returned / missing per member.","operationId":"assignment_submissions_organizations__org_id__groups__group_id__assignments__assignment_id__submissions_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/{assignment_id}/submissions/{student_user_id}/return":{"post":{"tags":["organizations","organizations"],"summary":"Return Assignment Submission","description":"Teacher returns with an optional comment and optional score.\n\n`comment` APPENDS to that student's thread now instead of overwriting the\nprevious one, so a second round of feedback no longer erases the first.","operationId":"return_assignment_submission_organizations__org_id__groups__group_id__assignments__assignment_id__submissions__student_user_id__return_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"student_user_id","in":"path","required":true,"schema":{"type":"string","title":"Student User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/{assignment_id}/publish":{"post":{"tags":["organizations","organizations"],"summary":"Publish Group Assignment","description":"draft → published, and the section is told. Idempotent: publishing an\nalready-published assignment is a no-op and notifies nobody a second time.","operationId":"publish_group_assignment_organizations__org_id__groups__group_id__assignments__assignment_id__publish_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/{assignment_id}/items":{"put":{"tags":["organizations","organizations"],"summary":"Set Group Assignment Items","description":"Full item replacement: `{items: [...]}` in the order they should appear.\n\nAdd, remove and reorder are all this one call — a partial reorder against\nthe non-deferrable `uq_group_assignment_item_position` is a two-phase dance\nnobody should have to write twice.","operationId":"set_group_assignment_items_organizations__org_id__groups__group_id__assignments__assignment_id__items_put","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/{assignment_id}/duplicate":{"post":{"tags":["organizations","organizations"],"summary":"Duplicate Group Assignment","description":"Copy into other sections or forward into another term.\n\nBody: `{target_group_ids?: [...], shift_days?: int, unlock_at?, lock_at?,\ndue_at?, status?}`. Copies land as drafts by default so the teacher checks\nthe dates before a section is notified. Staff access is enforced on EVERY\ntarget, not just the source — otherwise this is a way to push work into a\nclass you do not teach.","operationId":"duplicate_group_assignment_organizations__org_id__groups__group_id__assignments__assignment_id__duplicate_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/{assignment_id}/return-all":{"post":{"tags":["organizations","organizations"],"summary":"Return All Assignment Submissions","description":"Release every graded piece of work on this assignment at once.\n\n`{only_scored?: bool}` — with it, work carrying no mark is left alone, for a\nteacher halfway down a roster who wants to hand back what they have\nfinished. Scores are withheld from students until their work is returned, so\nthis is the moment the class hears back; running it twice is harmless\nbecause already-returned work is skipped.","operationId":"return_all_assignment_submissions_organizations__org_id__groups__group_id__assignments__assignment_id__return_all_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"default":{},"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/{assignment_id}/submissions/{student_user_id}/grade":{"post":{"tags":["organizations","organizations"],"summary":"Grade Assignment Submission","description":"Teacher enters a score: `{score, comment?, return_now?}`.\n\nSets `score_source='manual'`, which is a LOCK — no later auto re-derivation\nfrom lesson progress will overwrite it. Send `score: null` to clear the\noverride and hand the assignment back to auto-grading.\n\nOn a two-state assignment (`grade_scale` pass_fail / complete) send `mark`\ninstead — \"pass\"/\"fail\" or \"complete\"/\"incomplete\". It resolves to the same\nnumeric score (full points or zero), which is why averages and the gradebook\nneed to know nothing about scales. `score` still works and wins if both are\nsent, so an older client keeps grading exactly as it did.","operationId":"grade_assignment_submission_organizations__org_id__groups__group_id__assignments__assignment_id__submissions__student_user_id__grade_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"student_user_id","in":"path","required":true,"schema":{"type":"string","title":"Student User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/{assignment_id}/submissions/{student_user_id}/excuse":{"post":{"tags":["organizations","organizations"],"summary":"Excuse Assignment Submission","description":"`{excused: bool}`. An excused assignment leaves the average alone rather\nthan counting as a zero, and the row is created even when the student\nturned in nothing — excusing someone who handed nothing in is the point.","operationId":"excuse_assignment_submission_organizations__org_id__groups__group_id__assignments__assignment_id__submissions__student_user_id__excuse_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"student_user_id","in":"path","required":true,"schema":{"type":"string","title":"Student User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/{assignment_id}/questions":{"get":{"tags":["organizations","organizations"],"summary":"Assignment Questions","description":"STAFF ONLY. The full question set of every question item, answer key and\nper-blank tolerances included — what the editor loads before a teacher\nchanges a question or fixes a wrong answer.\n\nQuestion ids come back and must be sent back on save: a student's answers\nare keyed on them, so an editor that drops them orphans a class's work.","operationId":"assignment_questions_organizations__org_id__groups__group_id__assignments__assignment_id__questions_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/{assignment_id}/answers":{"post":{"tags":["organizations","organizations"],"summary":"Save Assignment Answers","description":"Student answers: `{answers: {question_id: [value, ...]}, submit?: bool}`.\n\n`submit: false` (the default) saves a draft so the homework can be done in\ntwo sittings; nothing unsubmitted is ever marked. `submit: true` hands it\nin, and once the answers become visible it can no longer be changed.\n\nGroup-scoped, like every other student write here: org membership alone\nwould let a student of another section answer into this one.\n\nThe response deliberately carries NO marking. What the student may see of\ntheir own result is decided in one place — `list_assignments` /\n`my-grade`, both gated on the answer-release policy — and a second,\nchattier answer from the write path is exactly how the two come to\ndisagree.","operationId":"save_assignment_answers_organizations__org_id__groups__group_id__assignments__assignment_id__answers_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/{assignment_id}/submissions/{student_user_id}/questions/{question_id}/override":{"post":{"tags":["organizations","organizations"],"summary":"Override Question Mark","description":"Teacher overrides ONE question's mark for ONE student: `{correct, note?}`.\n\n`correct: null` clears the override and hands the question back to the\nmatcher. Who and when are recorded, and the student is shown that a person\ndecided — Google Forms lets a teacher change a mark and keeps no record\nthat anybody did.\n\nThis changes the INPUT to the score, not the authority over it: the\nassignment stays auto-scored and keeps recomputing. Typing a number into\nthe gradebook is still the other thing, and still a lock.","operationId":"override_question_mark_organizations__org_id__groups__group_id__assignments__assignment_id__submissions__student_user_id__questions__question_id__override_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"student_user_id","in":"path","required":true,"schema":{"type":"string","title":"Student User Id"}},{"name":"question_id","in":"path","required":true,"schema":{"type":"string","title":"Question Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/{assignment_id}/my-grade":{"get":{"tags":["organizations","organizations"],"summary":"My Assignment Grade","description":"A student's own standing: state, score (once released), why it is that\nnumber, the feedback thread, and the class average for context.\n\nStrictly own-row: there is no `student_user_id` parameter, so there is\nnothing to tamper with. Staff wanting somebody else's number read the\ngradebook or the submissions list.","operationId":"my_assignment_grade_organizations__org_id__groups__group_id__assignments__assignment_id__my_grade_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/{assignment_id}/comments":{"get":{"tags":["organizations","organizations"],"summary":"List Assignment Comments","description":"The private teacher↔student thread, oldest first. Staff pass\n`?student_user_id=`; a student gets their own and may not ask for another's.","operationId":"list_assignment_comments_organizations__org_id__groups__group_id__assignments__assignment_id__comments_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"student_user_id","in":"query","required":false,"schema":{"type":"string","title":"Student User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["organizations","organizations"],"summary":"Add Assignment Comment","description":"`{body, student_user_id?}` — the id is staff-only and required of them.\n\nThe thread is keyed on (assignment, student) rather than on a submission,\nso it survives a resubmission and can exist before there is any work at all.","operationId":"add_assignment_comment_organizations__org_id__groups__group_id__assignments__assignment_id__comments_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/{assignment_id}/comments/{comment_id}":{"patch":{"tags":["organizations","organizations"],"summary":"Edit Assignment Comment","description":"Author or org admin. A teacher may not rewrite a student's message.","operationId":"edit_assignment_comment_organizations__org_id__groups__group_id__assignments__assignment_id__comments__comment_id__patch","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["organizations","organizations"],"summary":"Remove Assignment Comment","description":"Soft delete — the row stays, the thread just stops showing it.","operationId":"remove_assignment_comment_organizations__org_id__groups__group_id__assignments__assignment_id__comments__comment_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"assignment_id","in":"path","required":true,"schema":{"type":"string","title":"Assignment Id"}},{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/plans":{"post":{"tags":["organizations","organizations"],"summary":"Create Class Plan","description":"Body: `{chatroom_id?, session_date?, title?, objective?, notes?, items?[],\ncopy_from_plan_id?, status?, visible_to_students?}`.\n\n`copy_from_plan_id` may name another section's plan or a school template —\ncopying across sections and across terms is the point — but the caller must\nhave staff access to the SOURCE section too, checked below, so a plan cannot\nbe read out of a class you do not teach by copying it into one you do.","operationId":"create_class_plan_organizations__org_id__groups__group_id__plans_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["organizations","organizations"],"summary":"List Class Plans","description":"A section's plans, oldest first. `?from=&to=` are calendar days.\n\nStudents get only published+shared plans, with every `notes` field gone.\nThat filtering is the serializer's, not this route's.","operationId":"list_class_plans_organizations__org_id__groups__group_id__plans_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"from","in":"query","required":false,"schema":{"type":"string","title":"From"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","title":"To"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/plan-slots":{"get":{"tags":["organizations","organizations"],"summary":"List Class Plan Slots","description":"The section's class slots a plan can be bound to. Staff only.\n\nStaff only, and not because a slot is a secret — it is the same timetable\nevery student can see — but because this list exists to be WRITTEN from.\nIts only reader is the picker that PATCHes `chatroom_id`, which is staff\nwork, and an endpoint whose whole purpose is to feed a control a student\ndoes not have should not answer them.\n\nEach row is an occurrence of the section's room series (never its\nfree-text schedule note) and says what plan already holds it, so the picker\ncan show \"taken by Class 8\" instead of letting the teacher discover it\nthrough a 409.","operationId":"list_class_plan_slots_organizations__org_id__groups__group_id__plan_slots_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"from","in":"query","required":false,"schema":{"type":"string","title":"From"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","title":"To"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/plans/templates":{"get":{"tags":["organizations","organizations"],"summary":"List Class Plan Templates","description":"The school's reusable plan templates. Staff only — a template is a\nteaching artifact, not student-facing material.","operationId":"list_class_plan_templates_organizations__org_id__plans_templates_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/plans/coverage":{"get":{"tags":["organizations","organizations"],"summary":"Class Plan Coverage","description":"Coordinator roll-up for one week: per section, how many of its scheduled\nsessions have a published plan with at least one item.\n\nAdmin/owner only — it is a management view of other people's work, and a\nteacher seeing their colleagues' completion rates is a different product\ndecision than anyone has made.\n\nRegistered BEFORE `/{org_id}/plans/{plan_id}` on purpose: FastAPI matches\nin declaration order and \"coverage\" would otherwise be read as a plan id.","operationId":"class_plan_coverage_organizations__org_id__plans_coverage_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"week_start","in":"query","required":false,"schema":{"type":"string","title":"Week Start"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/plans":{"get":{"tags":["organizations","organizations"],"summary":"Class Plan Grid","description":"The week/term grid: ONE ROW PER SCHEDULED SESSION in range, each with its\nplan or `plan: null`.\n\nThe nulls are the feature — they are the \"unplanned\" cells. Scoped with the\nsame `_resolve_class_scope` the attendance surfaces use, so a teacher sees\ntheir own sections by default and an admin sees the school.","operationId":"class_plan_grid_organizations__org_id__plans_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"from","in":"query","required":false,"schema":{"type":"string","title":"From"}},{"name":"to","in":"query","required":false,"schema":{"type":"string","title":"To"}},{"name":"group_id","in":"query","required":false,"schema":{"type":"string","title":"Group Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/plans/{plan_id}":{"get":{"tags":["organizations","organizations"],"summary":"Get Class Plan","description":"One plan with its items. A student asking for a draft (or a published\nplan the teacher has not shared) gets 404, not an empty plan — an unshared\nplan does not exist to them.","operationId":"get_class_plan_organizations__org_id__plans__plan_id__get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"plan_id","in":"path","required":true,"schema":{"type":"string","title":"Plan Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["organizations","organizations"],"summary":"Update Class Plan","description":"`{title?, objective?, notes?, status?, visible_to_students?, session_date?,\nchatroom_id?, occurrence_at?}`. Absent means \"leave it\"; an explicit null\nclears.\n\n`occurrence_at` attaches the plan to an upcoming class the generator has\nnot built a room for yet — the picker offers those so a teacher can plan\nforward. The room is created, then the plan binds to it normally.","operationId":"update_class_plan_organizations__org_id__plans__plan_id__patch","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"plan_id","in":"path","required":true,"schema":{"type":"string","title":"Plan Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["organizations","organizations"],"summary":"Delete Class Plan","operationId":"delete_class_plan_organizations__org_id__plans__plan_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"plan_id","in":"path","required":true,"schema":{"type":"string","title":"Plan Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/plans/{plan_id}/materials":{"get":{"tags":["organizations","organizations"],"summary":"List Class Plan Materials","description":"The handouts on this plan. Students see only the SHARED ones.\n\n`serialize_plan` omits materials unless links are being minted, so the plan\nendpoints never returned them — a teacher could not see what was attached\nwithout opening the room.","operationId":"list_class_plan_materials_organizations__org_id__plans__plan_id__materials_get","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"plan_id","in":"path","required":true,"schema":{"type":"string","title":"Plan Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["organizations","organizations"],"summary":"Add Class Plan Material","description":"Attach an uploaded file to this plan. Staff only.\n\n`{ref, name?, shared?}` — the client uploads to /api/upload/assignment-file\nfirst and posts the reference it got back, exactly as the in-room route does.","operationId":"add_class_plan_material_organizations__org_id__plans__plan_id__materials_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"plan_id","in":"path","required":true,"schema":{"type":"string","title":"Plan Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/plans/{plan_id}/materials/{material_id}":{"patch":{"tags":["organizations","organizations"],"summary":"Set Class Plan Material Shared From Plan","description":"Show a handout to the class, or take it back. Staff only. `{shared}`.","operationId":"set_class_plan_material_shared_from_plan_organizations__org_id__plans__plan_id__materials__material_id__patch","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"plan_id","in":"path","required":true,"schema":{"type":"string","title":"Plan Id"}},{"name":"material_id","in":"path","required":true,"schema":{"type":"string","title":"Material Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["organizations","organizations"],"summary":"Remove Class Plan Material From Plan","description":"Detach a handout. Staff only. The object in the bucket is left alone.","operationId":"remove_class_plan_material_from_plan_organizations__org_id__plans__plan_id__materials__material_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"plan_id","in":"path","required":true,"schema":{"type":"string","title":"Plan Id"}},{"name":"material_id","in":"path","required":true,"schema":{"type":"string","title":"Material Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/plans/{plan_id}/items":{"post":{"tags":["organizations","organizations"],"summary":"Add Class Plan Item","description":"One item, appended. `{title, stage?, stage_label?, notes?, minutes?,\nmaterial_kind?, lesson_passage_id?, assignment_id?, material_title?,\nmaterial_url?, material_ref?}`.\n\n`stage_label` is the teacher's own name for the stage when none of the\npresets fits; `stage` stays a closed set beneath it.","operationId":"add_class_plan_item_organizations__org_id__plans__plan_id__items_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"plan_id","in":"path","required":true,"schema":{"type":"string","title":"Plan Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/plans/{plan_id}/items/{item_id}":{"patch":{"tags":["organizations","organizations"],"summary":"Update Class Plan Item","operationId":"update_class_plan_item_organizations__org_id__plans__plan_id__items__item_id__patch","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"plan_id","in":"path","required":true,"schema":{"type":"string","title":"Plan Id"}},{"name":"item_id","in":"path","required":true,"schema":{"type":"string","title":"Item Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["organizations","organizations"],"summary":"Delete Class Plan Item","operationId":"delete_class_plan_item_organizations__org_id__plans__plan_id__items__item_id__delete","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"plan_id","in":"path","required":true,"schema":{"type":"string","title":"Plan Id"}},{"name":"item_id","in":"path","required":true,"schema":{"type":"string","title":"Item Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/plans/{plan_id}/items/order":{"put":{"tags":["organizations","organizations"],"summary":"Reorder Class Plan Items","description":"`{item_ids: [...]}` — a plain renumber, no delete-and-reinsert, because\n`(plan_id, position)` deliberately carries no unique constraint.","operationId":"reorder_class_plan_items_organizations__org_id__plans__plan_id__items_order_put","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"plan_id","in":"path","required":true,"schema":{"type":"string","title":"Plan Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/plans/{plan_id}/bump":{"post":{"tags":["organizations","organizations"],"summary":"Bump Class Plan","description":"Close out a class and roll what did not happen into the next one.\n`{covered_item_ids?: [...], target_plan_id?, insert_at?: 'top'|'bottom'}`.\n\nUncovered items are COPIED forward, not moved, so this plan keeps an honest\nrecord of what was planned versus what happened — that difference is what\nthe coordinator report reads. 409 when the section has no upcoming class:\nfiling the work under an invented date would lose it.\n\nBumping twice is safe; already-rolled items are skipped.","operationId":"bump_class_plan_organizations__org_id__plans__plan_id__bump_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"plan_id","in":"path","required":true,"schema":{"type":"string","title":"Plan Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"default":{},"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/plans/{plan_id}/save-as-template":{"post":{"tags":["organizations","organizations"],"summary":"Save Class Plan As Template","description":"`{name}` — freeze this plan as a school template. Org admin only: a\ntemplate is the school's shared material, not one teacher's.","operationId":"save_class_plan_as_template_organizations__org_id__plans__plan_id__save_as_template_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"plan_id","in":"path","required":true,"schema":{"type":"string","title":"Plan Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/organizations/{org_id}/groups/{group_id}/assignments/generate":{"post":{"tags":["organizations","organizations"],"summary":"Generate Group Assignment","description":"Propose a homework DRAFT from a class that already happened.\n\nBody (all optional): `{room_name?|chatroom_id?, due_at?, points_possible?}`.\nOmit the room and the section's most recent started class is used, which is\nthe case this exists for — the teacher has just finished teaching.\n\nBuilt from what actually happened: the plan items the class did not get\nthrough (including work already carried forward once), what it did cover,\nthe class transcript, the level of the students enrolled, and the target\nvocabulary this teacher has already set for this section. **422** when the\nclass left none of that behind — a class with no plan and no transcript\ngets an honest refusal rather than filler.\n\nThe result is always `status='draft'` and is never announced to students.\nEdit it with the ordinary PATCH / PUT-items endpoints and post it with\n`/publish`; nothing here can publish. It carries `ai_generated: true`,\nstored on the row, so the flag survives into the student's view of the\npublished assignment (EU AI Act Art. 50(2)).\n\nOne Gemini call per request.","operationId":"generate_group_assignment_organizations__org_id__groups__group_id__assignments_generate_post","parameters":[{"name":"org_id","in":"path","required":true,"schema":{"type":"string","title":"Org Id"}},{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"default":{},"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/llms.txt":{"get":{"tags":["blog","blog"],"summary":"Get Llms Txt","description":"Serve /llms.txt with a Blog Articles section generated live from published\nposts. Fronted by a Cloudflare Pages Function at fluentea.com/llms.txt.","operationId":"get_llms_txt_llms_txt_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/blog/posts":{"get":{"tags":["blog","blog"],"summary":"List Blog Posts","description":"Public listing. Admins can pass status=all to see drafts.","operationId":"list_blog_posts_blog_posts_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"default":"published","title":"Status"}},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lang"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/blog/posts/{slug}":{"get":{"tags":["blog","blog"],"summary":"Get Blog Post","operationId":"get_blog_post_blog_posts__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/blog/posts":{"post":{"tags":["blog","blog"],"summary":"Create Blog Post","operationId":"create_blog_post_admin_blog_posts_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPostCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/blog/posts/{post_id}":{"patch":{"tags":["blog","blog"],"summary":"Update Blog Post","operationId":"update_blog_post_admin_blog_posts__post_id__patch","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlogPostUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["blog","blog"],"summary":"Delete Blog Post","operationId":"delete_blog_post_admin_blog_posts__post_id__delete","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/blog/upload-image":{"post":{"tags":["blog","blog"],"summary":"Upload Blog Image","operationId":"upload_blog_image_admin_blog_upload_image_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_blog_image_admin_blog_upload_image_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/quick-call/my-invites":{"get":{"tags":["quick-call","quick-call"],"summary":"Get My Invites","description":"Get current user's quick call invites (for calendar display).","operationId":"get_my_invites_quick_call_my_invites_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/quick-call/create":{"post":{"tags":["quick-call","quick-call"],"summary":"Create Quick Call Invite","description":"Generate a shareable quick-call link (authenticated host only).","operationId":"create_quick_call_invite_quick_call_create_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInviteRequest","default":{}}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInviteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/quick-call/{token}":{"get":{"tags":["quick-call","quick-call"],"summary":"Get Invite Metadata","description":"Public endpoint — returns invite metadata for the join page.","operationId":"get_invite_metadata_quick_call__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteMetadataResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/quick-call/{token}/join":{"post":{"tags":["quick-call","quick-call"],"summary":"Join Quick Call","description":"Join a quick call — requires auth (anonymous JWT is fine).","operationId":"join_quick_call_quick_call__token__join_post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/quick-call/{token}/send-email":{"post":{"tags":["quick-call","quick-call"],"summary":"Send Quick Call Email","description":"Send an email invitation for a quick call link.","operationId":"send_quick_call_email_quick_call__token__send_email_post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendQuickCallEmailRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/revenuecat":{"post":{"tags":["play-billing"],"summary":"Revenuecat Webhook","description":"Primary entitlement webhook. RevenueCat normalises Play (and later\nApple) state changes into a single event shape:\n\n    { \"event\": { \"type\": \"INITIAL_PURCHASE\", \"app_user_id\": \"<uuid>\",\n                 \"product_id\": \"premium_monthly\", \"transaction_id\": \"...\",\n                 \"expiration_at_ms\": 123, \"auto_renewing\": true, ... } }\n\nAuth: the shared secret configured in the RC dashboard arrives in the\nAuthorization header (RC sends it verbatim, no Bearer prefix). We\ncompare with `hmac.compare_digest` to avoid timing leaks.","operationId":"revenuecat_webhook_webhooks_revenuecat_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Revenuecat Webhook Webhooks Revenuecat Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/play-billing":{"post":{"tags":["play-billing"],"summary":"Play Billing Rtdn","description":"Pub/Sub push subscription endpoint for Google's Real-Time Developer\nNotifications. We treat RC as the primary source of truth and use\nthis purely as a backstop, so the implementation is intentionally\nminimal — log + return 200 so Pub/Sub considers the message\ndelivered. A future ticket can wire up Android Publisher API\nverification once we have real RTDN traffic to test against.","operationId":"play_billing_rtdn_webhooks_play_billing_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Play Billing Rtdn Webhooks Play Billing Post"}}}}}}},"/mercadopago/subscribe":{"post":{"tags":["mercadopago"],"summary":"Mp Subscribe","description":"Start a recurring MP subscription; returns the MP hosted-checkout URL.","operationId":"mp_subscribe_mercadopago_subscribe_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MPCheckoutRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/mercadopago/pass":{"post":{"tags":["mercadopago"],"summary":"Mp Pass","description":"One-time pass (PSE/Nequi/cash) → 30-day entitlement on payment. Returns the\nMP hosted-checkout URL.","operationId":"mp_pass_mercadopago_pass_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MPCheckoutRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/mercadopago":{"post":{"tags":["mercadopago"],"summary":"Mercadopago Webhook","description":"MP notification → fetch the resource → update subscription. MP sends the\nevent type + resource id (in the body or query string); we fetch the resource\nto read its authoritative status.","operationId":"mercadopago_webhook_webhooks_mercadopago_post","parameters":[{"name":"secret","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Secret"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/onboarding/getting-started/":{"get":{"tags":["onboarding","onboarding"],"summary":"Get State","operationId":"get_state_api_onboarding_getting_started__get","parameters":[{"name":"sim_followup_d2","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Sim Followup D2"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get State Api Onboarding Getting Started  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/onboarding/getting-started/dismiss":{"post":{"tags":["onboarding","onboarding"],"summary":"Dismiss","operationId":"dismiss_api_onboarding_getting_started_dismiss_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Dismiss Api Onboarding Getting Started Dismiss Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/onboarding/getting-started/restore":{"post":{"tags":["onboarding","onboarding"],"summary":"Restore","description":"Bring the getting-started checklist back after it was dismissed or\nfinished. Keeps item progress; just un-hides it so show_modal is true.","operationId":"restore_api_onboarding_getting_started_restore_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Restore Api Onboarding Getting Started Restore Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/onboarding/getting-started/school-status":{"get":{"tags":["onboarding","onboarding"],"summary":"School Status","description":"The quiet-period answer on its own, without building the checklist.\n\nThe badge notifier and the invitation modal both need it, and neither\nneeds the 30-odd fields (or the activity reconciliation) that GET / does.","operationId":"school_status_api_onboarding_getting_started_school_status_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response School Status Api Onboarding Getting Started School Status Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/onboarding/getting-started/school-invite/{answer}":{"post":{"tags":["onboarding","onboarding"],"summary":"School Invite","description":"Answer the one invitation a school member gets.\n\n`accept` is what STARTS the getting-started checklist — it is opted into,\nnever sprung on somebody. `decline` is final: the invitation is never shown\nagain and every consumer prompt stays off. (`/restore` remains the door\nback in if they change their mind later.)","operationId":"school_invite_api_onboarding_getting_started_school_invite__answer__post","parameters":[{"name":"answer","in":"path","required":true,"schema":{"type":"string","title":"Answer"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response School Invite Api Onboarding Getting Started School Invite  Answer  Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/onboarding/getting-started/complete/{item_id}":{"post":{"tags":["onboarding","onboarding"],"summary":"Complete Item","operationId":"complete_item_api_onboarding_getting_started_complete__item_id__post","parameters":[{"name":"item_id","in":"path","required":true,"schema":{"type":"string","title":"Item Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Complete Item Api Onboarding Getting Started Complete  Item Id  Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/onboarding/getting-started/celebrate-seen":{"post":{"tags":["onboarding","onboarding"],"summary":"Celebrate Seen","operationId":"celebrate_seen_api_onboarding_getting_started_celebrate_seen_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Celebrate Seen Api Onboarding Getting Started Celebrate Seen Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/onboarding/getting-started/placement-rating":{"post":{"tags":["onboarding","onboarding"],"summary":"Placement Rating","description":"Record the user's feedback on ChaCha's placement estimate.\n\nBody: {rating: 'about_right' | 'too_high' | 'too_low'}\n\nStored in profile_data.chacha_placement_rating so we can later audit\nhow often the placement signal lands vs gets corrected. Stamps a\ntimestamp + the CEFR + source so the data is interpretable later.","operationId":"placement_rating_api_onboarding_getting_started_placement_rating_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Placement Rating Api Onboarding Getting Started Placement Rating Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/onboarding/getting-started/briefing-seen":{"post":{"tags":["onboarding","onboarding"],"summary":"Briefing Seen","description":"Mark the welcome-briefing pop-up as shown so it doesn't re-appear.","operationId":"briefing_seen_api_onboarding_getting_started_briefing_seen_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Briefing Seen Api Onboarding Getting Started Briefing Seen Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/onboarding/getting-started/post-checkin":{"get":{"tags":["onboarding","onboarding"],"summary":"Post Checkin","description":"Return placement + recommended lessons for the post-ChaCha-checkin\nscreen. CEFR comes from ChaCha's placement signal when present\n(chacha_intent_summary.placement_signal or chacha_returning_summary.\nintent_summary.placement), otherwise falls back to the user's\nself-reported language_proficiency.","operationId":"post_checkin_api_onboarding_getting_started_post_checkin_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Post Checkin Api Onboarding Getting Started Post Checkin Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/onboarding/getting-started/intro-call-seen":{"post":{"tags":["onboarding","onboarding"],"summary":"Intro Call Seen","description":"Mark the first-time-sign-in 'ChaCha is calling' modal as shown so it\ndoesn't appear again. Called from all three Voice / Text / Maybe-later\npaths — once they've seen the offer, it shouldn't pop up a second time.\nThe Getting Started checklist modal/card still appears on next load.","operationId":"intro_call_seen_api_onboarding_getting_started_intro_call_seen_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Intro Call Seen Api Onboarding Getting Started Intro Call Seen Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/onboarding/getting-started/followup-d2-seen":{"post":{"tags":["onboarding","onboarding"],"summary":"Followup D2 Seen","description":"Mark the second-visit ChaCha call as seen so it doesn't fire again.","operationId":"followup_d2_seen_api_onboarding_getting_started_followup_d2_seen_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Followup D2 Seen Api Onboarding Getting Started Followup D2 Seen Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/onboarding/getting-started/reset":{"post":{"tags":["onboarding","onboarding"],"summary":"Reset","description":"QA / debug only — clears flag + progress.\n\nGated by GETTING_STARTED_DEBUG_RESET env flag in production.","operationId":"reset_api_onboarding_getting_started_reset_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Reset Api Onboarding Getting Started Reset Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/onboarding/getting-started/pick-language":{"post":{"tags":["onboarding","onboarding"],"summary":"Pick Language","description":"When a user is learning multiple languages, let them pick which one\nto focus on for the Getting Started checklist. Locks the snapshot.","operationId":"pick_language_api_onboarding_getting_started_pick_language_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Pick Language Api Onboarding Getting Started Pick Language Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/onboarding/getting-started/text-chat":{"post":{"tags":["onboarding","onboarding"],"summary":"Text Chat","description":"Stateless text-chat endpoint for ChaCha check-in calls.\n\nThe frontend posts the full conversation history each turn (along with\nmode = a1_intent | a2plus_orient | wrapup | followup_d2) and gets back\nChaCha's next message. When ChaCha emits a final JSON summary, the\nendpoint sets done=true and the frontend marks the matching checklist\nitem complete (only for a1_intent / a2plus_orient — wrapup and\nfollowup_d2 use celebrate-seen / followup-d2-seen instead).\n\nThis intentionally reuses the same prompt builders the voice path\nuses, so the conversation goal is identical: placement signal +\nintent capture for orient, congrats+feedback for wrapup, etc.","operationId":"text_chat_api_onboarding_getting_started_text_chat_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Text Chat Api Onboarding Getting Started Text Chat Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/help/search":{"get":{"tags":["help","help"],"summary":"Search Guides","description":"Top help-guide sections for a query. Embedding-ranked with a lexical\nfallback; scoped to the caller's role.","operationId":"search_guides_help_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":500,"title":"Q"}},{"name":"lang","in":"query","required":false,"schema":{"type":"string","default":"en","title":"Lang"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/try-chacha/save-interview":{"post":{"tags":["try-chacha","try-chacha"],"summary":"Save Interview","description":"Persist the TryChaCha interview summary to the user's profile_data.\n\nCalled by the frontend the moment ChaCha emits her final summary JSON in\nthe live transcript (same GS_SUMMARY_MARKER pattern the dashboard\nintro-call already uses). Idempotent — calling twice just overwrites\nwith the newer payload.\n\nDesigned to work for anonymous users (`anon_*` usernames). The data\nrides the existing auth_routes anon→authed migration unchanged.","operationId":"save_interview_api_try_chacha_save_interview_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveInterviewRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Save Interview Api Try Chacha Save Interview Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/try-chacha/analyze":{"post":{"tags":["try-chacha","try-chacha"],"summary":"Analyze Interview","description":"Run the existing quick_call_summary on the TryChaCha transcript +\nextract structured intent. Returns the post-call card payload AND\npersists the interview to profile_data so signup carries it forward.","operationId":"analyze_interview_api_try_chacha_analyze_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Analyze Interview Api Try Chacha Analyze Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/try-chacha/text-message":{"post":{"tags":["try-chacha","try-chacha"],"summary":"Text Message","description":"Stateless text-chat turn for the anon TryChaCha preview.\n\nFrontend posts the full history each turn (along with the latest\nuser message) and gets ChaCha's next message back. Calls Gemini\ndirectly so we can use the custom try_chacha text prompt — the\nshared generate_ai_response helper builds its own system prompt and\ndoesn't take an override.","operationId":"text_message_api_try_chacha_text_message_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextMessageRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Text Message Api Try Chacha Text Message Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/chacha-matches/":{"get":{"tags":["chacha-matches","chacha-matches"],"summary":"Get Matches","operationId":"get_matches_api_chacha_matches__get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Matches Api Chacha Matches  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/chacha-matches/refresh":{"post":{"tags":["chacha-matches","chacha-matches"],"summary":"Refresh Matches","operationId":"refresh_matches_api_chacha_matches_refresh_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Refresh Matches Api Chacha Matches Refresh Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/chacha-matches/{match_id}/introduce":{"post":{"tags":["chacha-matches","chacha-matches"],"summary":"Introduce","operationId":"introduce_api_chacha_matches__match_id__introduce_post","parameters":[{"name":"match_id","in":"path","required":true,"schema":{"type":"string","title":"Match Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Introduce Api Chacha Matches  Match Id  Introduce Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/chacha-matches/{match_id}/decline":{"post":{"tags":["chacha-matches","chacha-matches"],"summary":"Decline","operationId":"decline_api_chacha_matches__match_id__decline_post","parameters":[{"name":"match_id","in":"path","required":true,"schema":{"type":"string","title":"Match Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Decline Api Chacha Matches  Match Id  Decline Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/marketing/human-check":{"post":{"tags":["marketing","marketing"],"summary":"Human Check","description":"Score how human a piece of writing sounds and coach the author to improve it.","operationId":"human_check_marketing_human_check_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HumanCheckRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/marketing/human-check/feedback":{"post":{"tags":["marketing","marketing"],"summary":"Human Check Feedback","description":"Attach a thumbs up/down (training label) to a prior submission.","operationId":"human_check_feedback_marketing_human_check_feedback_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HumanCheckFeedbackRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/addon/oauth/code":{"post":{"tags":["gcal-addon"],"summary":"Mint Auth Code","description":"Mint a one-time authorization code for the Apps Script callback.\n\nCalled by the /app/connect/addon page after the logged-in user clicks\nApprove. The page then redirects to redirect_uri?code=...&state=... (state\nround-trips client-side; the Apps Script OAuth2 library validates it).","operationId":"mint_auth_code_api_addon_oauth_code_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MintCodeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/addon/oauth/token":{"post":{"tags":["gcal-addon"],"summary":"Exchange Token","description":"Exchange a one-time code for a long-lived add-on token (OAuth-shaped,\nform-encoded, as sent by the Apps Script OAuth2 library).","operationId":"exchange_token_api_addon_oauth_token_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_exchange_token_api_addon_oauth_token_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/addon/me":{"get":{"tags":["gcal-addon"],"summary":"Addon Me","description":"Identity + capabilities for the add-on sidebar.","operationId":"addon_me_api_addon_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/addon/rooms":{"post":{"tags":["gcal-addon"],"summary":"Create Event Room","description":"Create (or return) the FluenTea room for a calendar event. Idempotent by\ngcal_event_id: Calendar may call onCreateFunction more than once.","operationId":"create_event_room_api_addon_rooms_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomSettings"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/addon/rooms/{gcal_event_id}":{"get":{"tags":["gcal-addon"],"summary":"Get Event Room","operationId":"get_event_room_api_addon_rooms__gcal_event_id__get","parameters":[{"name":"gcal_event_id","in":"path","required":true,"schema":{"type":"string","title":"Gcal Event Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["gcal-addon"],"summary":"Update Event Room","description":"Update room settings / reschedule after the calendar event changed.","operationId":"update_event_room_api_addon_rooms__gcal_event_id__patch","parameters":[{"name":"gcal_event_id","in":"path","required":true,"schema":{"type":"string","title":"Gcal Event Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomPatch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["gcal-addon"],"summary":"Cancel Event Room","description":"Cancel the room when the calendar event (or its conference) is removed.","operationId":"cancel_event_room_api_addon_rooms__gcal_event_id__delete","parameters":[{"name":"gcal_event_id","in":"path","required":true,"schema":{"type":"string","title":"Gcal Event Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/addon/resolve/{public_token}":{"get":{"tags":["gcal-addon"],"summary":"Resolve Join Link","description":"Public: turn a calendar-link token into the current room + join code.\n\nPossession of the link is the authorization (same model as a Zoom link) —\nthe join code for private rooms is returned here so /app/join/gcal can\nforward guests to /chatrooms/<room>?code=<code>. If the mapped room has\nalready ended (previous occurrence of a recurring event), a fresh room\nwith the same settings is created on the spot.","operationId":"resolve_join_link_api_addon_resolve__public_token__get","parameters":[{"name":"public_token","in":"path","required":true,"schema":{"type":"string","title":"Public Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"summary":"Health Check","description":"Health check endpoint for Kubernetes liveness/readiness probes.\n\nIMPORTANT: This endpoint must be extremely fast and lightweight.\n- Uses regular def (not async) to avoid event loop blocking\n- No database operations\n- No external service calls\n- Just returns a static response\n\nThis ensures K8s probes succeed even when the event loop is busy.","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health/ready":{"get":{"summary":"Readiness Check","description":"Readiness probe for Kubernetes (`readinessProbe`). NOT the liveness probe.\n\n`/health` above stays the LIVENESS probe: static, no I/O, so a merely busy\nevent loop never gets the pod killed. This endpoint answers the different\nquestion k8s asks for readiness - \"should this pod receive traffic?\" - and\nit can answer no. Before it existed, a pod with a dead database or an\nunapplied migration passed readiness, joined the Service endpoints, took\nlive class traffic, and made `kubectl rollout status` report success.\n\nChecks (see app/health/readiness.py for the full rationale):\n  - database: a real `SELECT 1` over its own short-lived connection, hard-\n    bounded by libpq `connect_timeout` + `statement_timeout` (~2s), and\n    deliberately NOT taken from the main pool so a load spike cannot fail\n    readiness on every pod at once.\n  - migrations: the DB's alembic revision vs the migration head shipped in\n    this image. DB BEHIND code fails (the code will read columns that do\n    not exist). DB AHEAD of code does NOT fail - during an expand/migrate\n    deploy, and during a rollback, the old pods are intentionally running\n    against a newer schema, and failing them would cause the outage the\n    zero-downtime rollout is meant to avoid.\n\nContract: 200 when servable, 503 when not. k8s only reads the status code;\nthe JSON body names the failing check for humans. Never raises - an\nunexpected exception is turned into a 503 with a reason, because a 500\nstack trace from the probe looks like an app bug and hides the cause.\n\nPlain `def` on purpose: the DB layer here is synchronous SQLAlchemy +\npsycopg2, so Starlette runs this in the threadpool. Do not make it\n`async def` without an async driver - blocking the single uvicorn worker's\nevent loop under a full class of students is worse than having no probe.","operationId":"readiness_check_health_ready_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health/detailed":{"get":{"summary":"Detailed Health Check","description":"Detailed health check endpoint for monitoring and debugging.\n\nDO NOT use this for Kubernetes probes - use /health instead.\nThis endpoint includes service status checks and may be slower.","operationId":"detailed_health_check_health_detailed_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/version":{"get":{"summary":"Get Version","description":"API version endpoint for service worker and monitoring","operationId":"get_version_api_version_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/":{"get":{"summary":"Root","description":"Root endpoint","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/welcome-audio/{language}/{filename}":{"get":{"summary":"Serve Welcome Audio","description":"Serve welcome audio files for AI voice chat from GCS or local storage\n\nArgs:\n    language: Language code (english, spanish, french, german, mandarin)\n    filename: Audio filename (chacha_welcome.wav, chacha_waiting.wav)","operationId":"serve_welcome_audio_welcome_audio__language___filename__get","parameters":[{"name":"language","in":"path","required":true,"schema":{"type":"string","title":"Language"}},{"name":"filename","in":"path","required":true,"schema":{"type":"string","title":"Filename"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/translate":{"post":{"summary":"Translate Text","description":"Legacy translation endpoint - redirects to chat translation","operationId":"translate_text_translate_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/chat/ai/{user_id}":{"post":{"summary":"Chat With Ai","description":"Send a message to ChaCha AI and get a response","operationId":"chat_with_ai_chat_ai__user_id__post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"ActionRequest":{"properties":{"action":{"type":"string","maxLength":40,"minLength":1,"title":"Action"},"payload":{"additionalProperties":true,"type":"object","title":"Payload"}},"type":"object","required":["action"],"title":"ActionRequest"},"ActivateAccountRequest":{"properties":{"token":{"type":"string","title":"Token"},"new_password":{"type":"string","title":"New Password"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"acceptTerms":{"type":"boolean","title":"Acceptterms","default":true},"acceptPrivacy":{"type":"boolean","title":"Acceptprivacy","default":true},"acceptDataProcessing":{"type":"boolean","title":"Acceptdataprocessing","default":true}},"type":"object","required":["token","new_password"],"title":"ActivateAccountRequest"},"ActiveUsersByLanguageResponse":{"properties":{"languages":{"additionalProperties":{"$ref":"#/components/schemas/LanguageCounts"},"type":"object","title":"Languages"},"total_users":{"type":"integer","title":"Total Users"},"timestamp":{"type":"string","title":"Timestamp"}},"type":"object","required":["languages","total_users","timestamp"],"title":"ActiveUsersByLanguageResponse"},"ActivityFeedItem":{"properties":{"id":{"type":"string","title":"Id"},"type":{"type":"string","title":"Type"},"title":{"type":"string","title":"Title"},"description":{"type":"string","title":"Description"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"},"student_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Id"},"unread":{"type":"boolean","title":"Unread","default":false}},"type":"object","required":["id","type","title","description","timestamp"],"title":"ActivityFeedItem","description":"Activity feed item for tutor dashboard."},"ActivityTrackRequest":{"properties":{"session_id":{"type":"string","title":"Session Id"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"},"activity_type":{"type":"string","title":"Activity Type"},"activity_name":{"type":"string","title":"Activity Name"},"activity_category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Activity Category"},"page_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Page Url"},"page_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Page Title"},"referrer_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referrer Url"},"target_element":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Element"},"target_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Text"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata"},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Ms"},"interaction_time_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Interaction Time Ms"},"anonymous_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Anonymous Id"}},"type":"object","required":["session_id","activity_type","activity_name"],"title":"ActivityTrackRequest"},"ActivityTrackingRequest":{"properties":{"activity_type":{"type":"string","title":"Activity Type"},"page_path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Page Path"}},"type":"object","required":["activity_type"],"title":"ActivityTrackingRequest"},"AddStudentRequest":{"properties":{"student_id":{"type":"string","title":"Student Id"}},"type":"object","required":["student_id"],"title":"AddStudentRequest","description":"Request to add a student to tutor's roster."},"AdoptDirectRequest":{"properties":{"room_name":{"type":"string","title":"Room Name"},"peer_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Peer User Id"},"call_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Call Type"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"}},"type":"object","required":["room_name"],"title":"AdoptDirectRequest"},"AiFeedbackIn":{"properties":{"surface":{"type":"string","title":"Surface"},"ref_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ref Id"},"context":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Context"},"rating":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rating"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"}},"type":"object","required":["surface"],"title":"AiFeedbackIn"},"AnalyticsReportResponse":{"properties":{"report_id":{"type":"string","title":"Report Id"},"report_type":{"type":"string","title":"Report Type"},"focus_area":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Focus Area"},"insights":{"additionalProperties":true,"type":"object","title":"Insights"},"student_id":{"type":"string","title":"Student Id"},"student_name":{"type":"string","title":"Student Name"},"generated_at":{"type":"string","title":"Generated At"},"generation_duration_ms":{"type":"integer","title":"Generation Duration Ms"},"tokens_used":{"type":"integer","title":"Tokens Used"},"cost":{"type":"number","title":"Cost"}},"type":"object","required":["report_id","report_type","focus_area","insights","student_id","student_name","generated_at","generation_duration_ms","tokens_used","cost"],"title":"AnalyticsReportResponse","description":"Analytics report response"},"AnalyticsSummaryResponse":{"properties":{"total_visitors":{"type":"integer","title":"Total Visitors"},"total_page_views":{"type":"integer","title":"Total Page Views"},"total_conversions":{"type":"integer","title":"Total Conversions"},"conversion_rate":{"type":"number","title":"Conversion Rate"},"onboarding_signups":{"type":"integer","title":"Onboarding Signups","default":0},"direct_signups":{"type":"integer","title":"Direct Signups","default":0},"top_pages":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Top Pages"},"conversion_funnel":{"additionalProperties":{"type":"integer"},"type":"object","title":"Conversion Funnel"},"signup_sources":{"additionalProperties":{"type":"integer"},"type":"object","title":"Signup Sources"},"top_first_activities":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Top First Activities"},"preview_to_signup_correlation":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Preview To Signup Correlation"}},"type":"object","required":["total_visitors","total_page_views","total_conversions","conversion_rate","top_pages","conversion_funnel","signup_sources","top_first_activities","preview_to_signup_correlation"],"title":"AnalyticsSummaryResponse"},"AnalyzeRequest":{"properties":{"transcript":{"items":{"$ref":"#/components/schemas/TranscriptTurn"},"type":"array","title":"Transcript"},"target_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Language"},"native_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Native Language"}},"type":"object","required":["transcript"],"title":"AnalyzeRequest"},"AnswerBody":{"properties":{"assessment_id":{"type":"string","title":"Assessment Id"},"redis_session_id":{"type":"string","title":"Redis Session Id","description":"from /start"},"item_id":{"type":"string","title":"Item Id"},"answer_index":{"anyOf":[{"type":"integer","maximum":3.0,"minimum":0.0},{"type":"null"}],"title":"Answer Index"},"response_text":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Response Text"}},"type":"object","required":["assessment_id","redis_session_id","item_id"],"title":"AnswerBody"},"AssignLatecomerRequest":{"properties":{"user_id":{"type":"string","title":"User Id"},"breakout_room_name":{"type":"string","title":"Breakout Room Name"}},"type":"object","required":["user_id","breakout_room_name"],"title":"AssignLatecomerRequest"},"AudioCallAssistanceRequest":{"properties":{"audio_data":{"type":"string","title":"Audio Data"},"context":{"type":"string","title":"Context"},"scenario":{"type":"string","title":"Scenario"},"target_language":{"type":"string","title":"Target Language"}},"type":"object","required":["audio_data","context","scenario","target_language"],"title":"AudioCallAssistanceRequest"},"AudioProcessingRequest":{"properties":{"user_id":{"type":"string","title":"User Id"},"audio_data":{"type":"string","title":"Audio Data"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","default":"auto"}},"type":"object","required":["user_id","audio_data"],"title":"AudioProcessingRequest"},"AuthorityNotificationRequest":{"properties":{"measures_taken":{"items":{"type":"string"},"type":"array","title":"Measures Taken"},"contact_details":{"additionalProperties":{"type":"string"},"type":"object","title":"Contact Details"},"additional_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Additional Notes"}},"type":"object","required":["measures_taken","contact_details"],"title":"AuthorityNotificationRequest"},"BadgeResponse":{"properties":{"id":{"type":"string","title":"Id"},"badge_type":{"type":"string","title":"Badge Type"},"criteria_met":{"additionalProperties":true,"type":"object","title":"Criteria Met"},"is_active":{"type":"boolean","title":"Is Active"},"earned_at":{"type":"string","format":"date-time","title":"Earned At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"type":"object","required":["id","badge_type","criteria_met","is_active","earned_at","expires_at"],"title":"BadgeResponse","description":"Response for a tutor badge."},"BecomeTutorRequest":{"properties":{"tutor_type":{"type":"string","maxLength":50,"title":"Tutor Type","default":"community"},"bio":{"type":"string","maxLength":2000,"minLength":50,"title":"Bio"},"tagline":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Tagline"},"gender":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Gender"},"country":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Country"},"intro_video_url":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Intro Video Url"},"languages_taught":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Languages Taught"},"language_proficiency":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Language Proficiency","default":{}},"years_experience":{"anyOf":[{"type":"integer","maximum":50.0,"minimum":0.0},{"type":"null"}],"title":"Years Experience"},"teaching_style":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Teaching Style"},"certifications":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Certifications","default":[]},"specializations":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Specializations","default":[]},"timezone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timezone"},"availability_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Availability Notes"},"availability_schedule":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Availability Schedule"},"external_payment_info":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Payment Info"},"packages":{"anyOf":[{"items":{"$ref":"#/components/schemas/PackageRequest"},"type":"array"},{"type":"null"}],"title":"Packages","default":[]},"additional_notes":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Additional Notes"},"custom_starting_hourly_rate":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Custom Starting Hourly Rate"},"custom_starting_currency":{"anyOf":[{"type":"string","maxLength":3},{"type":"null"}],"title":"Custom Starting Currency"}},"type":"object","required":["bio","languages_taught"],"title":"BecomeTutorRequest","description":"Request to become a tutor."},"BilingualSuggestionsResponse":{"properties":{"target_language_suggestions":{"items":{"type":"string"},"type":"array","title":"Target Language Suggestions"},"native_language_suggestions":{"items":{"type":"string"},"type":"array","title":"Native Language Suggestions"},"target_language":{"type":"string","title":"Target Language"},"native_language":{"type":"string","title":"Native Language"}},"type":"object","required":["target_language_suggestions","native_language_suggestions","target_language","native_language"],"title":"BilingualSuggestionsResponse"},"BlockedTimeRequest":{"properties":{"start_time":{"type":"string","format":"date-time","title":"Start Time"},"end_time":{"type":"string","format":"date-time","title":"End Time"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","required":["start_time","end_time"],"title":"BlockedTimeRequest","description":"Request to create or update a blocked time slot."},"BlockedTimeResponse":{"properties":{"id":{"type":"string","title":"Id"},"tutor_profile_id":{"type":"string","title":"Tutor Profile Id"},"start_time":{"type":"string","format":"date-time","title":"Start Time"},"end_time":{"type":"string","format":"date-time","title":"End Time"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","tutor_profile_id","start_time","end_time","title","description","created_at","updated_at"],"title":"BlockedTimeResponse","description":"Response for a blocked time slot."},"BlogPostCreate":{"properties":{"title":{"type":"string","title":"Title"},"subtitle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subtitle"},"body_html":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Html","default":""},"cover_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cover Image Url"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","default":"draft"}},"type":"object","required":["title"],"title":"BlogPostCreate"},"BlogPostUpdate":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"subtitle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subtitle"},"body_html":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Html"},"cover_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cover Image Url"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},"type":"object","title":"BlogPostUpdate"},"Body_add_ai_message_to_history_ai_voice_add_ai_message_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"ai_message":{"type":"string","title":"Ai Message"}},"type":"object","required":["session_id","ai_message"],"title":"Body_add_ai_message_to_history_ai_voice_add_ai_message_post"},"Body_add_caption_contest_comment_api_word_of_day_caption_contest_caption__caption_id__comment_post":{"properties":{"comment_text":{"type":"string","title":"Comment Text"},"parent_comment_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parent Comment Id"}},"type":"object","required":["comment_text"],"title":"Body_add_caption_contest_comment_api_word_of_day_caption_contest_caption__caption_id__comment_post"},"Body_add_comment_to_caption_api_word_of_day_caption__caption_id__comment_post":{"properties":{"comment_text":{"type":"string","title":"Comment Text"},"parent_comment_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parent Comment Id"}},"type":"object","required":["comment_text"],"title":"Body_add_comment_to_caption_api_word_of_day_caption__caption_id__comment_post"},"Body_analyze_conversation_quality_learning_conversation_quality_analysis__user_id__post":{"properties":{"target_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Language"},"partner_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Partner Id"},"time_period_hours":{"type":"integer","title":"Time Period Hours","default":24}},"type":"object","title":"Body_analyze_conversation_quality_learning_conversation_quality_analysis__user_id__post"},"Body_analyze_cultural_response_api_word_of_day_cultural_response_post":{"properties":{"audio":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio"},"word_id":{"type":"integer","title":"Word Id"},"language":{"type":"string","title":"Language"},"cultural_answer":{"type":"string","title":"Cultural Answer"},"region":{"type":"string","title":"Region"}},"type":"object","required":["audio","word_id","language"],"title":"Body_analyze_cultural_response_api_word_of_day_cultural_response_post"},"Body_analyze_pronunciation_api_word_of_day_pronounce_post":{"properties":{"word_id":{"type":"integer","title":"Word Id"},"language":{"type":"string","title":"Language"},"audio":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio"},"native_language":{"type":"string","title":"Native Language"},"displayed_word":{"type":"string","title":"Displayed Word"}},"type":"object","required":["word_id","language","audio"],"title":"Body_analyze_pronunciation_api_word_of_day_pronounce_post"},"Body_analyze_pronunciation_preview_api_word_of_day_pronounce_preview_post":{"properties":{"word_id":{"type":"integer","title":"Word Id"},"language":{"type":"string","title":"Language"},"audio":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio"},"user_id":{"type":"string","title":"User Id"},"displayed_word":{"type":"string","title":"Displayed Word"}},"type":"object","required":["word_id","language","audio"],"title":"Body_analyze_pronunciation_preview_api_word_of_day_pronounce_preview_post"},"Body_apple_callback_auth_oauth_apple_callback_post":{"properties":{"code":{"type":"string","title":"Code"},"state":{"type":"string","title":"State"},"user":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User"}},"type":"object","required":["code","state"],"title":"Body_apple_callback_auth_oauth_apple_callback_post"},"Body_apple_signup_callback_auth_oauth_apple_signup_callback_post":{"properties":{"code":{"type":"string","title":"Code"},"state":{"type":"string","title":"State"},"user":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User"}},"type":"object","required":["code","state"],"title":"Body_apple_signup_callback_auth_oauth_apple_signup_callback_post"},"Body_chatroom_chacha_transcribe_chatrooms__room_name__chacha_transcribe_post":{"properties":{"audio_file":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio File"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"}},"type":"object","required":["audio_file"],"title":"Body_chatroom_chacha_transcribe_chatrooms__room_name__chacha_transcribe_post"},"Body_complete_word_of_day_activity_api_word_of_day_complete_activity_post":{"properties":{"activity_name":{"type":"string","title":"Activity Name"},"word_id":{"type":"integer","title":"Word Id"}},"type":"object","required":["activity_name","word_id"],"title":"Body_complete_word_of_day_activity_api_word_of_day_complete_activity_post"},"Body_complete_word_of_day_session_api_word_of_day_complete_session_post":{"properties":{"word_id":{"type":"integer","title":"Word Id"},"completed_activities":{"type":"string","title":"Completed Activities"}},"type":"object","required":["word_id","completed_activities"],"title":"Body_complete_word_of_day_session_api_word_of_day_complete_session_post"},"Body_create_call_assistance_session_learning_call_assistance_create_session_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"scenario":{"type":"string","title":"Scenario"},"language":{"type":"string","title":"Language","default":"English"}},"type":"object","required":["session_id","scenario"],"title":"Body_create_call_assistance_session_learning_call_assistance_create_session_post"},"Body_create_deepgram_session_deepgram_create_session_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"user_id":{"type":"string","title":"User Id"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","default":"en"}},"type":"object","required":["session_id"],"title":"Body_create_deepgram_session_deepgram_create_session_post"},"Body_create_realtime_session_realtime_create_session_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"user_id":{"type":"string","title":"User Id"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","default":"auto"}},"type":"object","required":["session_id"],"title":"Body_create_realtime_session_realtime_create_session_post"},"Body_debug_vocabulary_analysis_learning_debug_vocabulary_post":{"properties":{"user_id":{"type":"string","title":"User Id"},"call_session_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Call Session Id"},"target_language":{"type":"string","title":"Target Language"}},"type":"object","required":["user_id","target_language"],"title":"Body_debug_vocabulary_analysis_learning_debug_vocabulary_post"},"Body_destroy_deepgram_session_deepgram_destroy_session_post":{"properties":{"session_id":{"type":"string","title":"Session Id"}},"type":"object","required":["session_id"],"title":"Body_destroy_deepgram_session_deepgram_destroy_session_post"},"Body_destroy_realtime_session_realtime_destroy_session_post":{"properties":{"session_id":{"type":"string","title":"Session Id"}},"type":"object","required":["session_id"],"title":"Body_destroy_realtime_session_realtime_destroy_session_post"},"Body_end_ai_voice_session_ai_voice_end_session_post":{"properties":{"session_id":{"type":"string","title":"Session Id"}},"type":"object","required":["session_id"],"title":"Body_end_ai_voice_session_ai_voice_end_session_post"},"Body_end_ai_voice_session_preview_ai_voice_end_session_preview_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"user_id":{"type":"string","title":"User Id"}},"type":"object","required":["session_id","user_id"],"title":"Body_end_ai_voice_session_preview_ai_voice_end_session_preview_post"},"Body_end_ai_voice_session_voice_ai_voice_end_session_post":{"properties":{"session_id":{"type":"string","title":"Session Id"}},"type":"object","required":["session_id"],"title":"Body_end_ai_voice_session_voice_ai_voice_end_session_post"},"Body_end_assistance_session_learning_call_assistance_end_session_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"speaker_filter":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Speaker Filter"}},"type":"object","required":["session_id"],"title":"Body_end_assistance_session_learning_call_assistance_end_session_post"},"Body_evaluate_vocab_attempt_lessons_situations__passage_id__evaluate_attempt_post":{"properties":{"word":{"type":"string","title":"Word"},"language":{"type":"string","title":"Language","default":"English"},"progress_id":{"type":"string","title":"Progress Id"},"text_attempt":{"type":"string","title":"Text Attempt"},"audio":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio"}},"type":"object","required":["word"],"title":"Body_evaluate_vocab_attempt_lessons_situations__passage_id__evaluate_attempt_post"},"Body_exchange_token_api_addon_oauth_token_post":{"properties":{"code":{"type":"string","title":"Code"},"grant_type":{"type":"string","title":"Grant Type","default":"authorization_code"},"redirect_uri":{"type":"string","title":"Redirect Uri"}},"type":"object","title":"Body_exchange_token_api_addon_oauth_token_post"},"Body_flush_audio_deepgram_flush_audio_post":{"properties":{"session_id":{"type":"string","title":"Session Id"}},"type":"object","required":["session_id"],"title":"Body_flush_audio_deepgram_flush_audio_post"},"Body_flush_audio_realtime_flush_audio_post":{"properties":{"session_id":{"type":"string","title":"Session Id"}},"type":"object","required":["session_id"],"title":"Body_flush_audio_realtime_flush_audio_post"},"Body_generate_flashcard_audio_learning_enhanced_flashcards_generate_audio_post":{"properties":{"text":{"type":"string","title":"Text"},"language":{"type":"string","title":"Language"},"session_id":{"type":"string","title":"Session Id","default":"flashcard"}},"type":"object","required":["text","language"],"title":"Body_generate_flashcard_audio_learning_enhanced_flashcards_generate_audio_post"},"Body_get_assistance_hint_learning_call_assistance_get_hint_post":{"properties":{"session_id":{"type":"string","title":"Session Id"}},"type":"object","required":["session_id"],"title":"Body_get_assistance_hint_learning_call_assistance_get_hint_post"},"Body_improve_text_api_word_of_day_improve_text_post":{"properties":{"text":{"type":"string","title":"Text"},"target_language":{"type":"string","title":"Target Language"},"context":{"type":"string","title":"Context","default":"general"},"native_language":{"type":"string","title":"Native Language","default":"English"}},"type":"object","required":["text","target_language"],"title":"Body_improve_text_api_word_of_day_improve_text_post"},"Body_initiate_matched_chat_api_word_of_day_initiate_matched_chat_post":{"properties":{"partner_user_id":{"type":"string","title":"Partner User Id"},"word_id":{"type":"integer","title":"Word Id"},"user_response":{"type":"string","title":"User Response"},"partner_response":{"type":"string","title":"Partner Response"},"learning_language":{"type":"string","title":"Learning Language"}},"type":"object","required":["partner_user_id","word_id","user_response","partner_response","learning_language"],"title":"Body_initiate_matched_chat_api_word_of_day_initiate_matched_chat_post"},"Body_live_transcribe_audio_voice_live_transcribe_post":{"properties":{"audio_file":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio File"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"use_enhanced":{"type":"boolean","title":"Use Enhanced","default":false}},"type":"object","required":["audio_file"],"title":"Body_live_transcribe_audio_voice_live_transcribe_post"},"Body_live_translate_text_voice_live_translate_post":{"properties":{"text":{"type":"string","title":"Text"},"source_language":{"type":"string","title":"Source Language"},"target_language":{"type":"string","title":"Target Language"}},"type":"object","required":["text","source_language","target_language"],"title":"Body_live_translate_text_voice_live_translate_post"},"Body_login_auth_login_post":{"properties":{"user_id":{"type":"string","title":"User Id"},"password":{"type":"string","title":"Password"},"device_fingerprint":{"type":"string","title":"Device Fingerprint"}},"type":"object","required":["user_id","password"],"title":"Body_login_auth_login_post"},"Body_process_ai_voice_message_ai_voice_process_message_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"audio_file":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio File"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"lesson_context":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lesson Context"}},"type":"object","required":["session_id","audio_file"],"title":"Body_process_ai_voice_message_ai_voice_process_message_post"},"Body_process_ai_voice_message_preview_ai_voice_process_message_preview_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"audio_file":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio File"},"user_id":{"type":"string","title":"User Id"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"lesson_context":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lesson Context"}},"type":"object","required":["session_id","audio_file","user_id"],"title":"Body_process_ai_voice_message_preview_ai_voice_process_message_preview_post"},"Body_process_ai_voice_message_voice_ai_voice_process_message_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"audio_file":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio File"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"}},"type":"object","required":["session_id","audio_file"],"title":"Body_process_ai_voice_message_voice_ai_voice_process_message_post"},"Body_process_assistance_audio_learning_call_assistance_process_audio_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"audio_data":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio Data"}},"type":"object","required":["session_id","audio_data"],"title":"Body_process_assistance_audio_learning_call_assistance_process_audio_post"},"Body_process_deepgram_audio_deepgram_process_audio_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"audio_data":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio Data"},"sample_rate":{"type":"integer","title":"Sample Rate","default":48000},"channels":{"type":"integer","title":"Channels","default":1}},"type":"object","required":["session_id","audio_data"],"title":"Body_process_deepgram_audio_deepgram_process_audio_post"},"Body_process_realtime_audio_realtime_process_audio_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"audio_data":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio Data"},"sample_rate":{"type":"integer","title":"Sample Rate","default":48000},"channels":{"type":"integer","title":"Channels","default":1}},"type":"object","required":["session_id","audio_data"],"title":"Body_process_realtime_audio_realtime_process_audio_post"},"Body_rename_enhanced_flashcard_deck_learning_enhanced_flashcards_deck__user_id___deck_id__rename_put":{"properties":{"new_name":{"type":"string","title":"New Name"}},"type":"object","required":["new_name"],"title":"Body_rename_enhanced_flashcard_deck_learning_enhanced_flashcards_deck__user_id___deck_id__rename_put"},"Body_rename_flashcard_deck_learning_flashcards_deck__user_id___deck_id__rename_put":{"properties":{"new_name":{"type":"string","title":"New Name"}},"type":"object","required":["new_name"],"title":"Body_rename_flashcard_deck_learning_flashcards_deck__user_id___deck_id__rename_put"},"Body_resend_verification_email_auth_resend_verification_post":{"properties":{"user_id":{"type":"string","title":"User Id"},"language":{"type":"string","title":"Language","default":"en"}},"type":"object","required":["user_id"],"title":"Body_resend_verification_email_auth_resend_verification_post"},"Body_resolve_chat_image_url_api_upload_chat_image_url_post":{"properties":{"ref":{"type":"string","title":"Ref"}},"type":"object","required":["ref"],"title":"Body_resolve_chat_image_url_api_upload_chat_image_url_post"},"Body_resolve_chat_image_urls_api_upload_chat_image_urls_post":{"properties":{"refs":{"items":{},"type":"array","title":"Refs"}},"type":"object","required":["refs"],"title":"Body_resolve_chat_image_urls_api_upload_chat_image_urls_post"},"Body_start_ai_voice_session_ai_voice_start_session_post":{"properties":{"user_id":{"type":"string","title":"User Id"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"}},"type":"object","required":["user_id"],"title":"Body_start_ai_voice_session_ai_voice_start_session_post"},"Body_start_ai_voice_session_preview_ai_voice_start_session_preview_post":{"properties":{"user_id":{"type":"string","title":"User Id"}},"type":"object","required":["user_id"],"title":"Body_start_ai_voice_session_preview_ai_voice_start_session_preview_post"},"Body_start_ai_voice_session_voice_ai_voice_start_session_post":{"properties":{"user_id":{"type":"string","title":"User Id"},"target_language":{"type":"string","title":"Target Language"},"native_language":{"type":"string","title":"Native Language"}},"type":"object","required":["user_id"],"title":"Body_start_ai_voice_session_voice_ai_voice_start_session_post"},"Body_store_progress_snapshot_learning_progress_tracking_store_snapshot__user_id__post":{"properties":{"target_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Language"},"snapshot_type":{"type":"string","title":"Snapshot Type","default":"on_demand"},"analysis_period_hours":{"type":"integer","title":"Analysis Period Hours","default":24}},"type":"object","title":"Body_store_progress_snapshot_learning_progress_tracking_store_snapshot__user_id__post"},"Body_submit_caption_api_word_of_day_caption__image_id__post":{"properties":{"caption_text":{"type":"string","title":"Caption Text"}},"type":"object","required":["caption_text"],"title":"Body_submit_caption_api_word_of_day_caption__image_id__post"},"Body_submit_caption_contest_caption_api_word_of_day_caption_contest__image_id__caption_post":{"properties":{"caption_text":{"type":"string","title":"Caption Text"}},"type":"object","required":["caption_text"],"title":"Body_submit_caption_contest_caption_api_word_of_day_caption_contest__image_id__caption_post"},"Body_submit_native_pronunciation_api_word_of_day_native_pronunciation__word_id__post":{"properties":{"audio":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio"},"dialect_info":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dialect Info"},"native_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Native Language"},"level":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Level"}},"type":"object","required":["audio"],"title":"Body_submit_native_pronunciation_api_word_of_day_native_pronunciation__word_id__post"},"Body_submit_quiz_answer_api_word_of_day_quiz__question_id__post":{"properties":{"selected_answer":{"type":"string","title":"Selected Answer"},"time_taken_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Time Taken Seconds"}},"type":"object","required":["selected_answer"],"title":"Body_submit_quiz_answer_api_word_of_day_quiz__question_id__post"},"Body_submit_reading_audio_api_word_of_day_input_exercise_submit_reading_audio_post":{"properties":{"content_id":{"type":"integer","title":"Content Id"},"audio_file":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio File"}},"type":"object","required":["content_id","audio_file"],"title":"Body_submit_reading_audio_api_word_of_day_input_exercise_submit_reading_audio_post"},"Body_submit_recording_response_api_word_of_day_recording_response__prompt_id__post":{"properties":{"audio":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"Audio"},"text_response":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text Response"},"learning_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Learning Language"},"native_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Native Language"}},"type":"object","title":"Body_submit_recording_response_api_word_of_day_recording_response__prompt_id__post"},"Body_submit_slang_answer_api_word_of_day_slang__question_id__post":{"properties":{"selected_answer":{"type":"string","title":"Selected Answer"},"time_taken_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Time Taken Seconds"}},"type":"object","required":["selected_answer"],"title":"Body_submit_slang_answer_api_word_of_day_slang__question_id__post"},"Body_submit_slang_contribution_api_word_of_day_slang__word_id__contribute_post":{"properties":{"slang_term":{"type":"string","title":"Slang Term"},"region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Region"},"native_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Native Language"},"usage_example":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usage Example"},"formal_equivalent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Formal Equivalent"},"formality_level":{"type":"string","title":"Formality Level","default":"informal"},"cultural_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cultural Notes"}},"type":"object","required":["slang_term"],"title":"Body_submit_slang_contribution_api_word_of_day_slang__word_id__contribute_post"},"Body_synthesize_ai_voice_ai_voice_synthesize_post":{"properties":{"text":{"type":"string","title":"Text"},"session_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session Id"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","default":"English"}},"type":"object","required":["text"],"title":"Body_synthesize_ai_voice_ai_voice_synthesize_post"},"Body_synthesize_ai_voice_preview_ai_voice_synthesize_preview_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"text":{"type":"string","title":"Text"},"language":{"type":"string","title":"Language"},"user_id":{"type":"string","title":"User Id"},"lesson_context":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lesson Context"}},"type":"object","required":["session_id","text","language","user_id"],"title":"Body_synthesize_ai_voice_preview_ai_voice_synthesize_preview_post"},"Body_transcribe_audio_file_voice_transcribe_post":{"properties":{"audio_file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"Audio File"},"audio":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"Audio"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"use_deepgram":{"type":"boolean","title":"Use Deepgram","default":false}},"type":"object","title":"Body_transcribe_audio_file_voice_transcribe_post"},"Body_update_deepgram_language_deepgram_update_language_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"language":{"type":"string","title":"Language"}},"type":"object","required":["session_id","language"],"title":"Body_update_deepgram_language_deepgram_update_language_post"},"Body_update_realtime_language_realtime_update_language_post":{"properties":{"session_id":{"type":"string","title":"Session Id"},"language":{"type":"string","title":"Language"}},"type":"object","required":["session_id","language"],"title":"Body_update_realtime_language_realtime_update_language_post"},"Body_update_session_language_ai_voice_sessions__session_id__language_put":{"properties":{"language":{"type":"string","title":"Language"}},"type":"object","required":["language"],"title":"Body_update_session_language_ai_voice_sessions__session_id__language_put"},"Body_upload_assignment_file_api_upload_assignment_file_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_assignment_file_api_upload_assignment_file_post"},"Body_upload_blog_image_admin_blog_upload_image_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_blog_image_admin_blog_upload_image_post"},"Body_upload_chat_image_api_upload_chat_image_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_chat_image_api_upload_chat_image_post"},"Body_upload_chat_image_ephemeral_api_upload_chat_image_ephemeral_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_chat_image_ephemeral_api_upload_chat_image_ephemeral_post"},"Body_upload_cover_organizations__org_id__cover_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_cover_organizations__org_id__cover_post"},"Body_upload_intro_video_tutor_upload_intro_video_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_intro_video_tutor_upload_intro_video_post"},"Body_upload_logo_organizations__org_id__logo_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_logo_organizations__org_id__logo_post"},"Body_upload_profile_picture_api_upload_profile_picture_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_profile_picture_api_upload_profile_picture_post"},"Body_upload_video_api_upload_video_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_video_api_upload_video_post"},"Body_upload_voice_note_voice_upload_post":{"properties":{"audio_file":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio File"},"from_user_id":{"type":"string","title":"From User Id"},"to_user_id":{"type":"string","title":"To User Id"},"duration":{"type":"string","title":"Duration"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"}},"type":"object","required":["audio_file","from_user_id","to_user_id","duration"],"title":"Body_upload_voice_note_voice_upload_post"},"Body_vote_on_caption_api_word_of_day_caption__caption_id__vote_post":{"properties":{"vote_type":{"type":"string","title":"Vote Type"}},"type":"object","required":["vote_type"],"title":"Body_vote_on_caption_api_word_of_day_caption__caption_id__vote_post"},"Body_vote_on_caption_contest_caption_api_word_of_day_caption_contest_caption__caption_id__vote_post":{"properties":{"vote_type":{"type":"string","title":"Vote Type"}},"type":"object","required":["vote_type"],"title":"Body_vote_on_caption_contest_caption_api_word_of_day_caption_contest_caption__caption_id__vote_post"},"Body_vote_on_caption_contest_comment_api_word_of_day_caption_contest_comment__comment_id__vote_post":{"properties":{"vote_type":{"type":"string","title":"Vote Type"}},"type":"object","required":["vote_type"],"title":"Body_vote_on_caption_contest_comment_api_word_of_day_caption_contest_comment__comment_id__vote_post"},"Body_vote_on_comment_api_word_of_day_caption_comment__comment_id__vote_post":{"properties":{"vote_type":{"type":"string","title":"Vote Type"}},"type":"object","required":["vote_type"],"title":"Body_vote_on_comment_api_word_of_day_caption_comment__comment_id__vote_post"},"BookingResponse":{"properties":{"id":{"type":"string","title":"Id"},"tutor_id":{"type":"string","title":"Tutor Id"},"tutor_username":{"type":"string","title":"Tutor Username"},"tutor_full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tutor Full Name"},"tutor_profile_picture":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tutor Profile Picture"},"student_id":{"type":"string","title":"Student Id"},"student_username":{"type":"string","title":"Student Username"},"student_full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Full Name"},"student_profile_picture":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Profile Picture"},"scheduled_time":{"type":"string","format":"date-time","title":"Scheduled Time"},"duration_minutes":{"type":"integer","title":"Duration Minutes"},"status":{"type":"string","title":"Status"},"is_trial_session":{"type":"boolean","title":"Is Trial Session"},"package_reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Package Reference"},"cancelled_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Cancelled At"},"cancelled_by_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cancelled By User Id"},"cancellation_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cancellation Reason"},"is_late_cancellation":{"type":"boolean","title":"Is Late Cancellation"},"tutor_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tutor Notes"},"student_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Notes"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"is_tutor":{"type":"boolean","title":"Is Tutor"},"language_for_practice":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language For Practice"},"scheduled_room_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scheduled Room Name"},"recurrence_rule":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Recurrence Rule"},"recurrence_parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Recurrence Parent Id"},"tutor_cancellation_policy_hours":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Tutor Cancellation Policy Hours"},"tutor_cancellation_policy_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tutor Cancellation Policy Text"}},"type":"object","required":["id","tutor_id","tutor_username","tutor_full_name","tutor_profile_picture","student_id","student_username","student_full_name","student_profile_picture","scheduled_time","duration_minutes","status","is_trial_session","package_reference","cancelled_at","cancelled_by_user_id","cancellation_reason","is_late_cancellation","tutor_notes","student_notes","created_at","updated_at","is_tutor"],"title":"BookingResponse","description":"Response for a class booking."},"BotMessageRequest":{"properties":{"message":{"type":"string","title":"Message"}},"type":"object","required":["message"],"title":"BotMessageRequest"},"BotSessionEndRequest":{"properties":{"sessionId":{"type":"string","title":"Sessionid"}},"type":"object","required":["sessionId"],"title":"BotSessionEndRequest"},"BotSessionStartRequest":{"properties":{"sessionId":{"type":"string","title":"Sessionid"},"language":{"type":"string","title":"Language"}},"type":"object","required":["sessionId","language"],"title":"BotSessionStartRequest"},"BreachIncidentDetailResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"breach_type":{"type":"string","title":"Breach Type"},"description":{"type":"string","title":"Description"},"detected_at":{"type":"string","format":"date-time","title":"Detected At"},"severity":{"type":"string","title":"Severity"},"risk_level":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Risk Level"},"status":{"type":"string","title":"Status"},"estimated_users_affected":{"type":"integer","title":"Estimated Users Affected"},"authority_notified_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Authority Notified At"},"users_notified_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Users Notified At"},"occurred_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Occurred At"},"data_types_affected":{"items":{"type":"string"},"type":"array","title":"Data Types Affected","default":[]},"systems_affected":{"items":{"type":"string"},"type":"array","title":"Systems Affected","default":[]},"risk_assessment":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Risk Assessment"},"containment_measures":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Containment Measures"},"remediation_actions":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Remediation Actions"},"breach_metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Breach Metadata"}},"type":"object","required":["id","breach_type","description","detected_at","severity","status","estimated_users_affected"],"title":"BreachIncidentDetailResponse"},"BreachIncidentResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"breach_type":{"type":"string","title":"Breach Type"},"description":{"type":"string","title":"Description"},"detected_at":{"type":"string","format":"date-time","title":"Detected At"},"severity":{"type":"string","title":"Severity"},"risk_level":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Risk Level"},"status":{"type":"string","title":"Status"},"estimated_users_affected":{"type":"integer","title":"Estimated Users Affected"},"authority_notified_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Authority Notified At"},"users_notified_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Users Notified At"}},"type":"object","required":["id","breach_type","description","detected_at","severity","status","estimated_users_affected"],"title":"BreachIncidentResponse"},"BreachReportRequest":{"properties":{"breach_type":{"type":"string","title":"Breach Type"},"description":{"type":"string","title":"Description"},"detected_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Detected At"},"data_types_affected":{"items":{"type":"string"},"type":"array","title":"Data Types Affected","default":[]},"estimated_users_affected":{"type":"integer","title":"Estimated Users Affected","default":0},"severity":{"type":"string","title":"Severity","default":"medium"},"initial_assessment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Initial Assessment"},"potential_impact":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Potential Impact"},"reporter_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reporter Id"}},"type":"object","required":["breach_type","description"],"title":"BreachReportRequest"},"BreachStatusUpdate":{"properties":{"status":{"type":"string","title":"Status"},"containment_measures":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Containment Measures"},"remediation_actions":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Remediation Actions"}},"type":"object","required":["status"],"title":"BreachStatusUpdate"},"BreakoutBroadcastRequest":{"properties":{"kind":{"type":"string","title":"Kind"},"text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text"},"sound":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sound"}},"type":"object","required":["kind"],"title":"BreakoutBroadcastRequest"},"CallAssistanceRequest":{"properties":{"assistance_type":{"type":"string","title":"Assistance Type"},"current_transcription":{"type":"string","title":"Current Transcription"},"conversation_context":{"items":{"type":"string"},"type":"array","title":"Conversation Context"},"scenario":{"type":"string","title":"Scenario"},"target_language":{"type":"string","title":"Target Language"},"native_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Native Language"},"explanation_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Explanation Language"}},"type":"object","required":["assistance_type","current_transcription","conversation_context","scenario","target_language"],"title":"CallAssistanceRequest"},"CallAssistanceResponse":{"properties":{"suggestion":{"type":"string","title":"Suggestion"},"explanation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Explanation"}},"type":"object","required":["suggestion"],"title":"CallAssistanceResponse"},"CallCancelRequest":{"properties":{"call_id":{"type":"string","title":"Call Id"},"caller_id":{"type":"string","title":"Caller Id"},"callee_id":{"type":"string","title":"Callee Id"}},"type":"object","required":["call_id","caller_id","callee_id"],"title":"CallCancelRequest"},"CallChaChaRequest":{"properties":{"message":{"type":"string","title":"Message"},"call_language":{"type":"string","title":"Call Language","default":"English"},"native_language":{"type":"string","title":"Native Language","default":"English"},"partner_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Partner Name"}},"type":"object","required":["message"],"title":"CallChaChaRequest"},"CallChaChasilenceSuggestionsRequest":{"properties":{"call_language":{"type":"string","title":"Call Language","default":"English"},"native_language":{"type":"string","title":"Native Language","default":"English"},"partner_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Partner Name"},"transcription_context":{"items":{"type":"string"},"type":"array","title":"Transcription Context","default":[]},"last_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Message"}},"type":"object","title":"CallChaChasilenceSuggestionsRequest"},"CallInitiateRequest":{"properties":{"caller_id":{"type":"string","title":"Caller Id"},"callee_id":{"type":"string","title":"Callee Id"},"call_type":{"type":"string","title":"Call Type","default":"video"},"conversation_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Language"},"transcription_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transcription Language"},"translation_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Translation Language"},"conversation_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Id"},"transcription_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Transcription Enabled","default":true},"scheduled_duration_minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Scheduled Duration Minutes"},"call_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Call Mode"},"exchange_period_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Exchange Period Seconds"},"exchange_language_b":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exchange Language B"},"language_for_practice":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language For Practice"},"learner_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Learner Id"},"teacher_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Teacher Id"},"lesson_context":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Lesson Context"}},"type":"object","required":["caller_id","callee_id"],"title":"CallInitiateRequest"},"CallRatingRequest":{"properties":{"rating":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rating"},"technical_rating":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Technical Rating"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"is_ai_call":{"type":"boolean","title":"Is Ai Call","default":false}},"type":"object","title":"CallRatingRequest"},"CallResponseRequest":{"properties":{"call_id":{"type":"string","title":"Call Id"},"caller_id":{"type":"string","title":"Caller Id"},"callee_id":{"type":"string","title":"Callee Id"},"action":{"type":"string","title":"Action"},"conversation_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Language"},"translation_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Translation Language"},"transcription_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Transcription Enabled","default":true}},"type":"object","required":["call_id","caller_id","callee_id","action"],"title":"CallResponseRequest"},"CallSessionParticipant":{"properties":{"id":{"type":"string","title":"Id"},"username":{"type":"string","title":"Username"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"display_name":{"type":"string","title":"Display Name"}},"type":"object","required":["id","username","display_name"],"title":"CallSessionParticipant"},"CallSessionResponse":{"properties":{"id":{"type":"string","title":"Id"},"conversation_id":{"type":"string","title":"Conversation Id"},"start_time":{"type":"string","format":"date-time","title":"Start Time"},"end_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"End Time"},"duration_seconds":{"type":"integer","title":"Duration Seconds","default":0},"call_type":{"type":"string","title":"Call Type","default":"video"},"status":{"type":"string","title":"Status","default":"active"},"participants":{"items":{"type":"string"},"type":"array","title":"Participants","default":[]},"participant_info":{"anyOf":[{"items":{"$ref":"#/components/schemas/CallSessionParticipant"},"type":"array"},{"type":"null"}],"title":"Participant Info","default":[]}},"type":"object","required":["id","conversation_id","start_time"],"title":"CallSessionResponse"},"CallTranscriptResponse":{"properties":{"call_session_id":{"type":"string","title":"Call Session Id"},"conversation_id":{"type":"string","title":"Conversation Id"},"start_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Start Time"},"end_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"End Time"},"transcript_data":{"additionalProperties":{"items":{"additionalProperties":true,"type":"object"},"type":"array"},"type":"object","title":"Transcript Data","default":{}},"duration_seconds":{"type":"integer","title":"Duration Seconds","default":0},"participants":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Participants","default":[]},"participant_info":{"anyOf":[{"additionalProperties":{"additionalProperties":{"type":"string"},"type":"object"},"type":"object"},{"type":"null"}],"title":"Participant Info","default":{}},"call_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Call Type","default":"video"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","default":"ended"},"disabled_by_user":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Disabled By User","default":false},"user_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Message"},"quota_exhausted_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Quota Exhausted At"}},"type":"object","required":["call_session_id","conversation_id"],"title":"CallTranscriptResponse"},"CancelBookingRequest":{"properties":{"cancellation_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cancellation Reason"}},"type":"object","title":"CancelBookingRequest","description":"Request to cancel a booking."},"CancelSubscriptionRequest":{"properties":{"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"},"cancel_immediately":{"type":"boolean","title":"Cancel Immediately","default":false}},"type":"object","title":"CancelSubscriptionRequest"},"ChaChaSpeakRequest":{"properties":{"text":{"type":"string","maxLength":1500,"minLength":1,"title":"Text"}},"type":"object","required":["text"],"title":"ChaChaSpeakRequest"},"ChangePasswordRequest":{"properties":{"current_password":{"type":"string","title":"Current Password"},"new_password":{"type":"string","title":"New Password"}},"type":"object","required":["current_password","new_password"],"title":"ChangePasswordRequest"},"ChatPreviewRequest":{"properties":{"message":{"type":"string","title":"Message"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"},"target_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Language","default":"English"},"proficiency_level":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Proficiency Level","default":"A1"},"is_preview":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Preview","default":true},"enhanced":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enhanced","default":false},"conversation_history":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Conversation History"}},"type":"object","required":["message"],"title":"ChatPreviewRequest"},"ChatReactionRequest":{"properties":{"emoji":{"type":"string","maxLength":16,"minLength":1,"title":"Emoji"}},"type":"object","required":["emoji"],"title":"ChatReactionRequest"},"ChatroomChaChaRequest":{"properties":{"message":{"type":"string","maxLength":2000,"minLength":1,"title":"Message"},"mode":{"type":"string","pattern":"^(chat|translate)$","title":"Mode","default":"chat"},"source_language":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Source Language"},"target_language":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Target Language"},"transcript_context":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Transcript Context"}},"type":"object","required":["message"],"title":"ChatroomChaChaRequest"},"ClassChatBroadcastRequest":{"properties":{"text":{"type":"string","maxLength":500,"minLength":1,"title":"Text"}},"type":"object","required":["text"],"title":"ClassChatBroadcastRequest"},"CommentBody":{"properties":{"body":{"type":"string","title":"Body"}},"type":"object","required":["body"],"title":"CommentBody"},"CommunityPollVoteRequest":{"properties":{"word_id":{"type":"integer","title":"Word Id"},"selected_level":{"type":"string","title":"Selected Level"},"display_language":{"type":"string","title":"Display Language"}},"type":"object","required":["word_id","selected_level","display_language"],"title":"CommunityPollVoteRequest"},"CommunityRSVPRequest":{"properties":{"event_key":{"type":"string","title":"Event Key"}},"type":"object","required":["event_key"],"title":"CommunityRSVPRequest"},"ComprehensionAnswerIn":{"properties":{"index":{"type":"integer","title":"Index"},"answer":{"type":"string","title":"Answer"}},"type":"object","required":["index","answer"],"title":"ComprehensionAnswerIn"},"ComprehensionSubmission":{"properties":{"progress_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Progress Id"},"answers":{"items":{"$ref":"#/components/schemas/ComprehensionAnswerIn"},"type":"array","title":"Answers"}},"type":"object","required":["answers"],"title":"ComprehensionSubmission"},"ComprehensionSubmitRequest":{"properties":{"content_id":{"type":"integer","title":"Content Id"},"answers":{"items":{},"type":"array","title":"Answers"}},"type":"object","required":["content_id","answers"],"title":"ComprehensionSubmitRequest"},"ConfirmBookingRequest":{"properties":{"tutor_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tutor Notes"}},"type":"object","title":"ConfirmBookingRequest","description":"Request to confirm a booking."},"ConfirmLevelBody":{"properties":{"accepted":{"type":"boolean","title":"Accepted"},"override_level":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Override Level"}},"type":"object","required":["accepted"],"title":"ConfirmLevelBody"},"ConsentUpdate":{"properties":{"consent_type":{"type":"string","title":"Consent Type"},"consent_given":{"type":"boolean","title":"Consent Given"},"consent_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Consent Version"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata"}},"type":"object","required":["consent_type","consent_given"],"title":"ConsentUpdate"},"ConversionEventRequest":{"properties":{"session_id":{"type":"string","title":"Session Id"},"event_type":{"type":"string","title":"Event Type"},"event_data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Event Data"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"}},"type":"object","required":["session_id","event_type"],"title":"ConversionEventRequest"},"CoverPlanItemRequest":{"properties":{"covered":{"type":"boolean","title":"Covered","default":true},"actual_minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Actual Minutes"}},"type":"object","title":"CoverPlanItemRequest"},"CreateBookingRequest":{"properties":{"tutor_id":{"type":"string","title":"Tutor Id"},"student_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Id"},"scheduled_time":{"type":"string","format":"date-time","title":"Scheduled Time"},"duration_minutes":{"type":"integer","maximum":180.0,"minimum":15.0,"title":"Duration Minutes"},"is_trial_session":{"type":"boolean","title":"Is Trial Session","default":false},"package_reference":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Package Reference"},"student_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Notes"},"language_for_practice":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language For Practice"},"recurrence_rule":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Recurrence Rule"},"recurrence_end_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Recurrence End Date"}},"type":"object","required":["tutor_id","scheduled_time","duration_minutes"],"title":"CreateBookingRequest","description":"Request to create a class booking."},"CreateBreakoutsRequest":{"properties":{"num_rooms":{"anyOf":[{"type":"integer","maximum":15.0,"minimum":1.0},{"type":"null"}],"title":"Num Rooms"},"group_size":{"anyOf":[{"type":"integer","maximum":15.0,"minimum":2.0},{"type":"null"}],"title":"Group Size"},"assignments":{"anyOf":[{"items":{"items":{"type":"string"},"type":"array"},"type":"array"},{"type":"null"}],"title":"Assignments"},"half_minutes":{"anyOf":[{"type":"integer","maximum":60.0,"minimum":1.0},{"type":"null"}],"title":"Half Minutes","default":20},"duration_minutes":{"anyOf":[{"type":"integer","maximum":180.0,"minimum":1.0},{"type":"null"}],"title":"Duration Minutes"},"language_a":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language A"},"language_b":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language B"}},"type":"object","title":"CreateBreakoutsRequest"},"CreateCallInviteRequest":{"properties":{"scheduled_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Scheduled Time"},"duration_minutes":{"type":"integer","title":"Duration Minutes","default":30},"inviter_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inviter Note"},"invitee_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invitee Email"},"language_for_practice":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language For Practice"}},"type":"object","title":"CreateCallInviteRequest"},"CreateCallSessionRequest":{"properties":{"conversation_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Id"},"call_type":{"type":"string","title":"Call Type","default":"video"},"user1_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User1 Id"},"user2_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User2 Id"},"call_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Call Id"},"call_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Call Mode"},"language_for_practice":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language For Practice"},"exchange_switch_at_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Exchange Switch At Seconds"}},"type":"object","title":"CreateCallSessionRequest"},"CreateChatroomRequest":{"properties":{"language":{"type":"string","title":"Language"},"topic":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Topic"},"max_participants":{"type":"integer","maximum":100.0,"minimum":2.0,"title":"Max Participants","default":10},"scheduled_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scheduled At"},"activity_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Activity Type"},"activity_config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Activity Config"},"is_featured":{"type":"boolean","title":"Is Featured","default":false},"visibility":{"type":"string","title":"Visibility","default":"public"},"invitee_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Invitee Ids"},"video_enabled":{"type":"boolean","title":"Video Enabled","default":false},"scheduled_duration_minutes":{"anyOf":[{"type":"integer","maximum":480.0,"minimum":5.0},{"type":"null"}],"title":"Scheduled Duration Minutes"}},"type":"object","required":["language"],"title":"CreateChatroomRequest"},"CreateCheckoutSessionRequest":{"properties":{"tier":{"type":"string","title":"Tier"},"billing_cycle":{"type":"string","title":"Billing Cycle"},"success_url":{"type":"string","title":"Success Url"},"cancel_url":{"type":"string","title":"Cancel Url"},"trial_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Trial Days"},"promotional_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Promotional Code"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"}},"type":"object","required":["tier","billing_cycle","success_url","cancel_url"],"title":"CreateCheckoutSessionRequest"},"CreateCheckoutSessionResponse":{"properties":{"session_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Session Id"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"pay_in_app":{"type":"boolean","title":"Pay In App","default":false},"app_store_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"App Store Url"}},"type":"object","title":"CreateCheckoutSessionResponse"},"CreateClassRequest":{"properties":{"organization_id":{"type":"string","title":"Organization Id"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"topic":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Topic"},"max_participants":{"type":"integer","maximum":100.0,"minimum":2.0,"title":"Max Participants","default":100},"video_enabled":{"type":"boolean","title":"Video Enabled","default":false},"activity_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Activity Type"},"language_a":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language A"},"language_b":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language B"},"half_minutes":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Half Minutes"},"group_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Group Id"}},"type":"object","required":["organization_id"],"title":"CreateClassRequest"},"CreateDeckRequest":{"properties":{"title":{"type":"string","title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"target_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Language"}},"type":"object","required":["title"],"title":"CreateDeckRequest","description":"Request to create an empty flashcard deck."},"CreateInviteRequest":{"properties":{"learning_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Learning Language"},"scheduled_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scheduled Time"},"call_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Call Mode"},"exchange_period_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Exchange Period Seconds"},"exchange_language_b":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exchange Language B"}},"type":"object","title":"CreateInviteRequest"},"CreateInviteResponse":{"properties":{"token":{"type":"string","title":"Token"},"url":{"type":"string","title":"Url"}},"type":"object","required":["token","url"],"title":"CreateInviteResponse"},"CreatePackageRequest":{"properties":{"title":{"type":"string","maxLength":200,"minLength":1,"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"session_count":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"Session Count"},"session_duration_minutes":{"type":"integer","maximum":180.0,"minimum":15.0,"title":"Session Duration Minutes"},"price_amount":{"type":"number","minimum":0.0,"title":"Price Amount"},"price_currency":{"type":"string","maxLength":3,"title":"Price Currency","default":"USD"}},"type":"object","required":["title","session_count","session_duration_minutes","price_amount"],"title":"CreatePackageRequest","description":"Request to create a pricing package."},"CreatePeerCallRequest":{"properties":{"invitee_id":{"type":"string","title":"Invitee Id"},"scheduled_time":{"type":"string","format":"date-time","title":"Scheduled Time"},"duration_minutes":{"type":"integer","maximum":60.0,"minimum":15.0,"title":"Duration Minutes","default":30},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"},"language_for_practice":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language For Practice"},"call_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Call Mode"},"exchange_period_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Exchange Period Seconds"},"exchange_language_b":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exchange Language B"},"timezone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timezone"},"recurrence_rule":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Recurrence Rule"},"recurrence_end_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Recurrence End Date"}},"type":"object","required":["invitee_id","scheduled_time"],"title":"CreatePeerCallRequest"},"CreatePostBody":{"properties":{"body":{"type":"string","title":"Body"}},"type":"object","required":["body"],"title":"CreatePostBody"},"CreateStripeCustomerRequest":{"properties":{"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"}},"type":"object","title":"CreateStripeCustomerRequest","description":"Optional body. `country` is the frontend's ISO-2 guess, forwarded so this\nendpoint can apply the SAME app-rail check /subscriptions/checkout applies.\nOptional because older cached frontends POST here with an empty body."},"CulturalAnswerRequest":{"properties":{"word_id":{"type":"integer","title":"Word Id"},"language":{"type":"string","title":"Language"},"answer":{"type":"string","title":"Answer"},"region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Region"}},"type":"object","required":["word_id","language","answer"],"title":"CulturalAnswerRequest"},"EarlyAccessPaymentRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"interest_types":{"items":{"type":"string"},"type":"array","title":"Interest Types"},"use_case":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Use Case"},"payment_method_id":{"type":"string","title":"Payment Method Id"}},"type":"object","required":["email","interest_types","payment_method_id"],"title":"EarlyAccessPaymentRequest","description":"Request schema for priority early access with payment"},"EarlyAccessSignupRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"interest_types":{"items":{"type":"string"},"type":"array","title":"Interest Types"},"use_case":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Use Case"},"paid_priority":{"type":"boolean","title":"Paid Priority","default":false}},"type":"object","required":["email","interest_types"],"title":"EarlyAccessSignupRequest","description":"Request schema for early access waiting list signup"},"EarlyAccessSignupResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"email":{"type":"string","title":"Email"},"interest_types":{"items":{"type":"string"},"type":"array","title":"Interest Types"},"use_case":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Use Case"},"paid_priority":{"type":"boolean","title":"Paid Priority"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"message":{"type":"string","title":"Message"}},"type":"object","required":["id","email","interest_types","paid_priority","created_at","message"],"title":"EarlyAccessSignupResponse","description":"Response schema for early access signup"},"EarlyAccessStatsResponse":{"properties":{"total_signups":{"type":"integer","title":"Total Signups"},"free_signups":{"type":"integer","title":"Free Signups"},"paid_signups":{"type":"integer","title":"Paid Signups"},"dating_interest":{"type":"integer","title":"Dating Interest"},"business_interest":{"type":"integer","title":"Business Interest"},"both_interests":{"type":"integer","title":"Both Interests"},"total_revenue_cents":{"type":"integer","title":"Total Revenue Cents"}},"type":"object","required":["total_signups","free_signups","paid_signups","dating_interest","business_interest","both_interests","total_revenue_cents"],"title":"EarlyAccessStatsResponse","description":"Admin endpoint for early access statistics"},"EditMessageRequest":{"properties":{"content":{"type":"string","title":"Content"},"user_id":{"type":"string","title":"User Id"}},"type":"object","required":["content","user_id"],"title":"EditMessageRequest"},"EmailPreferencesResponse":{"properties":{"marketing_emails":{"type":"boolean","title":"Marketing Emails"},"transactional_emails":{"type":"boolean","title":"Transactional Emails"},"newsletter":{"type":"boolean","title":"Newsletter"}},"type":"object","required":["marketing_emails","transactional_emails","newsletter"],"title":"EmailPreferencesResponse"},"EventRSVPCountResponse":{"properties":{"event_id":{"type":"string","title":"Event Id"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["event_id","count"],"title":"EventRSVPCountResponse"},"EventRSVPRequest":{"properties":{"event_id":{"type":"string","title":"Event Id"},"event_type":{"type":"string","title":"Event Type"},"event_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Event Date"},"event_metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Event Metadata","default":{}}},"type":"object","required":["event_id","event_type"],"title":"EventRSVPRequest"},"EventRSVPResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"message":{"type":"string","title":"Message"},"rsvp_id":{"type":"string","title":"Rsvp Id"},"event_id":{"type":"string","title":"Event Id"},"status":{"type":"string","title":"Status"}},"type":"object","required":["success","message","rsvp_id","event_id","status"],"title":"EventRSVPResponse"},"EventRSVPStatusResponse":{"properties":{"has_rsvp":{"type":"boolean","title":"Has Rsvp"},"event_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Event Id"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"}},"type":"object","required":["has_rsvp"],"title":"EventRSVPStatusResponse"},"FeedbackGenerateRequest":{"properties":{"lesson_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lesson Summary"},"student_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Notes"},"conversation_transcript":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Transcript"}},"type":"object","title":"FeedbackGenerateRequest","description":"Request schema for generating teaching feedback"},"FeedbackRequest":{"properties":{"category":{"type":"string","title":"Category"},"text":{"type":"string","title":"Text"}},"type":"object","required":["category","text"],"title":"FeedbackRequest"},"FeedbackResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"message":{"type":"string","title":"Message"}},"type":"object","required":["success","message"],"title":"FeedbackResponse"},"FinalizeBody":{"properties":{"redis_session_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Redis Session Id"},"voice_signal":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Voice Signal"}},"type":"object","title":"FinalizeBody"},"FlashcardSuggestionRequest":{"properties":{"suggestion_type":{"type":"string","pattern":"^(edit|add|remove)$","title":"Suggestion Type"},"card_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Card Id"},"suggested_data":{"additionalProperties":true,"type":"object","title":"Suggested Data"},"tutor_note":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Tutor Note"}},"type":"object","required":["suggestion_type","suggested_data"],"title":"FlashcardSuggestionRequest","description":"Request to suggest a flashcard edit/add/remove"},"FlashcardSuggestionResponse":{"properties":{"id":{"type":"string","title":"Id"},"tutor_id":{"type":"string","title":"Tutor Id"},"student_id":{"type":"string","title":"Student Id"},"deck_id":{"type":"string","title":"Deck Id"},"card_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Card Id"},"suggestion_type":{"type":"string","title":"Suggestion Type"},"status":{"type":"string","title":"Status"},"original_data":{"additionalProperties":true,"type":"object","title":"Original Data"},"suggested_data":{"additionalProperties":true,"type":"object","title":"Suggested Data"},"tutor_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tutor Note"},"student_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Note"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"resolved_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Resolved At"}},"type":"object","required":["id","tutor_id","student_id","deck_id","card_id","suggestion_type","status","original_data","suggested_data","tutor_note","student_note","created_at","resolved_at"],"title":"FlashcardSuggestionResponse","description":"Flashcard suggestion response"},"ForgotPasswordRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"language":{"type":"string","title":"Language","default":"en"}},"type":"object","required":["email"],"title":"ForgotPasswordRequest"},"FriendRequestCreate":{"properties":{"receiver_id":{"type":"string","title":"Receiver Id"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"}},"type":"object","required":["receiver_id"],"title":"FriendRequestCreate"},"FriendRequestResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"sender_id":{"type":"string","format":"uuid","title":"Sender Id"},"receiver_id":{"type":"string","format":"uuid","title":"Receiver Id"},"status":{"type":"string","title":"Status"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"sender_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sender Name"},"receiver_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Receiver Name"},"sender_picture":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sender Picture"},"receiver_picture":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Receiver Picture"}},"type":"object","required":["id","sender_id","receiver_id","status","message","created_at","updated_at"],"title":"FriendRequestResponse"},"FriendRequestUpdate":{"properties":{"status":{"type":"string","title":"Status"}},"type":"object","required":["status"],"title":"FriendRequestUpdate"},"GPUInfo":{"properties":{"cuda_available":{"type":"boolean","title":"Cuda Available"},"device_count":{"type":"integer","title":"Device Count"},"devices":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Devices"},"current_memory_usage":{"additionalProperties":true,"type":"object","title":"Current Memory Usage"},"driver_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Driver Version"},"cuda_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cuda Version"}},"type":"object","required":["cuda_available","device_count","devices","current_memory_usage","driver_version","cuda_version"],"title":"GPUInfo"},"GenerateAnalyticsRequest":{"properties":{"report_type":{"type":"string","pattern":"^(comprehensive|focused)$","title":"Report Type","default":"comprehensive"},"focus_area":{"anyOf":[{"type":"string","pattern":"^(vocabulary|speaking|lessons|flashcards)$"},{"type":"null"}],"title":"Focus Area"}},"type":"object","title":"GenerateAnalyticsRequest","description":"Request to generate analytics report"},"GrammarCheckRequest":{"properties":{"text":{"type":"string","title":"Text"},"language":{"type":"string","title":"Language"},"native_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Native Language","default":"English"},"context":{"anyOf":[{"items":{"additionalProperties":{"type":"string"},"type":"object"},"type":"array"},{"type":"null"}],"title":"Context"},"is_spoken":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Spoken","default":false}},"type":"object","required":["text","language"],"title":"GrammarCheckRequest"},"GrammarItem":{"properties":{"point":{"type":"string","title":"Point"},"explanation":{"type":"string","title":"Explanation"},"example":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Example"}},"type":"object","required":["point","explanation"],"title":"GrammarItem"},"GrammaticalInfoRequest":{"properties":{"word":{"type":"string","title":"Word"},"language":{"type":"string","title":"Language"}},"type":"object","required":["word","language"],"title":"GrammaticalInfoRequest"},"GrammaticalInfoResponse":{"properties":{"word":{"type":"string","title":"Word"},"language":{"type":"string","title":"Language"},"gender":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gender"},"pos":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pos"},"info":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Info"}},"type":"object","required":["word","language"],"title":"GrammaticalInfoResponse"},"GreetingBody":{"properties":{"assessment_id":{"type":"string","title":"Assessment Id"},"redis_session_id":{"type":"string","title":"Redis Session Id","description":"from /start"},"response_text":{"type":"string","maxLength":2000,"title":"Response Text"}},"type":"object","required":["assessment_id","redis_session_id","response_text"],"title":"GreetingBody"},"GuessRequest":{"properties":{"guess":{"type":"string","maxLength":200,"minLength":1,"title":"Guess"},"time_remaining":{"type":"integer","maximum":60.0,"minimum":0.0,"title":"Time Remaining","default":0}},"type":"object","required":["guess"],"title":"GuessRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HomeworkFeedback":{"properties":{"feedback_text":{"type":"string","title":"Feedback Text"}},"type":"object","required":["feedback_text"],"title":"HomeworkFeedback","description":"Request schema for providing tutor feedback"},"HomeworkGenerateRequest":{"properties":{"language":{"type":"string","title":"Language"},"student_level":{"type":"string","title":"Student Level"},"lesson_topic":{"type":"string","title":"Lesson Topic"},"lesson_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lesson Notes"},"focus_areas":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Focus Areas"},"duration_minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Minutes","default":30},"student_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Id"}},"type":"object","required":["language","student_level","lesson_topic"],"title":"HomeworkGenerateRequest","description":"Request schema for generating AI homework"},"HomeworkResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tutor_id":{"type":"string","format":"uuid","title":"Tutor Id"},"student_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Student Id"},"title":{"type":"string","title":"Title"},"description":{"type":"string","title":"Description"},"language":{"type":"string","title":"Language"},"student_level":{"type":"string","title":"Student Level"},"lesson_topic":{"type":"string","title":"Lesson Topic"},"exercises":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Exercises"},"learning_objectives":{"items":{"type":"string"},"type":"array","title":"Learning Objectives"},"estimated_time":{"type":"integer","title":"Estimated Time"},"status":{"type":"string","title":"Status"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"submitted_answers":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Submitted Answers"},"tutor_feedback":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tutor Feedback"}},"type":"object","required":["id","tutor_id","title","description","language","student_level","lesson_topic","exercises","learning_objectives","estimated_time","status"],"title":"HomeworkResponse","description":"Response schema for homework assignment"},"HomeworkSubmission":{"properties":{"answers":{"additionalProperties":true,"type":"object","title":"Answers"}},"type":"object","required":["answers"],"title":"HomeworkSubmission","description":"Request schema for submitting homework"},"HumanCheckFeedbackRequest":{"properties":{"submission_id":{"type":"string","title":"Submission Id"},"rating":{"type":"string","title":"Rating"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"}},"type":"object","required":["submission_id","rating"],"title":"HumanCheckFeedbackRequest"},"HumanCheckRequest":{"properties":{"text":{"type":"string","title":"Text"}},"type":"object","required":["text"],"title":"HumanCheckRequest"},"IcebreakerAnswerRequest":{"properties":{"target_user_id":{"type":"string","title":"Target User Id"},"icebreaker_question":{"type":"string","title":"Icebreaker Question"},"user_answer":{"type":"string","title":"User Answer"},"language":{"type":"string","title":"Language"}},"type":"object","required":["target_user_id","icebreaker_question","user_answer","language"],"title":"IcebreakerAnswerRequest","description":"Request model for sending icebreaker answer"},"InstantMatchBackgroundRequest":{"properties":{"background":{"type":"boolean","title":"Background"}},"type":"object","required":["background"],"title":"InstantMatchBackgroundRequest"},"InstantMatchJoinRequest":{"properties":{"userId":{"type":"string","title":"Userid"},"languages":{"items":{"type":"string"},"type":"array","title":"Languages"},"preferences":{"items":{"type":"string"},"type":"array","title":"Preferences"},"nativeLanguagesForTutoring":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Nativelanguagesfortutoring"},"transcriptionEnabled":{"type":"boolean","title":"Transcriptionenabled","default":true},"lessonContext":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Lessoncontext"},"lessonId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lessonid"},"wodContext":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Wodcontext"}},"type":"object","required":["userId","languages","preferences"],"title":"InstantMatchJoinRequest"},"InstantMatchLeaveRequest":{"properties":{"userId":{"type":"string","title":"Userid"}},"type":"object","required":["userId"],"title":"InstantMatchLeaveRequest"},"InstantMatchRegistrationRequest":{"properties":{"call_id":{"type":"string","title":"Call Id"},"user1_id":{"type":"string","title":"User1 Id"},"user2_id":{"type":"string","title":"User2 Id"},"room_name":{"type":"string","title":"Room Name"}},"type":"object","required":["call_id","user1_id","user2_id","room_name"],"title":"InstantMatchRegistrationRequest"},"InviteMetadataResponse":{"properties":{"host_display_name":{"type":"string","title":"Host Display Name"},"host_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Host User Id"},"learning_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Learning Language"},"scheduled_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scheduled Time"},"expired":{"type":"boolean","title":"Expired"},"room_opens_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Room Opens At"},"call_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Call Mode"},"exchange_period_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Exchange Period Seconds"},"exchange_language_b":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exchange Language B"}},"type":"object","required":["host_display_name","learning_language","expired"],"title":"InviteMetadataResponse"},"InviteRequest":{"properties":{"invitee_ids":{"items":{"type":"string"},"type":"array","title":"Invitee Ids"}},"type":"object","required":["invitee_ids"],"title":"InviteRequest"},"JoinResponse":{"properties":{"livekit_token":{"type":"string","title":"Livekit Token"},"server_url":{"type":"string","title":"Server Url"},"room_name":{"type":"string","title":"Room Name"},"transcription_budget_seconds":{"type":"integer","title":"Transcription Budget Seconds"},"learning_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Learning Language"},"host_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Host User Id"},"call_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Call Mode"},"exchange_period_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Exchange Period Seconds"},"exchange_language_b":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exchange Language B"}},"type":"object","required":["livekit_token","server_url","room_name","transcription_budget_seconds"],"title":"JoinResponse"},"LanguageCounts":{"properties":{"learners":{"type":"integer","title":"Learners"},"natives":{"type":"integer","title":"Natives"},"teababes":{"type":"integer","title":"Teababes","default":0}},"type":"object","required":["learners","natives"],"title":"LanguageCounts"},"LanguageDetectionRequest":{"properties":{"text":{"type":"string","title":"Text"}},"type":"object","required":["text"],"title":"LanguageDetectionRequest"},"LanguageDetectionResponse":{"properties":{"detected_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detected Language"}},"type":"object","title":"LanguageDetectionResponse"},"LearningGoal":{"properties":{"type":{"type":"string","title":"Type"},"description":{"type":"string","title":"Description"},"target_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Target Date"},"progress_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Progress Notes"},"completed":{"type":"boolean","title":"Completed","default":false}},"type":"object","required":["type","description"],"title":"LearningGoal","description":"Individual learning goal for a student"},"LearningGoalsResponse":{"properties":{"student_id":{"type":"string","format":"uuid","title":"Student Id"},"tutor_id":{"type":"string","format":"uuid","title":"Tutor Id"},"goals":{"items":{"$ref":"#/components/schemas/LearningGoal"},"type":"array","title":"Goals"},"last_updated":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Updated"}},"type":"object","required":["student_id","tutor_id","goals"],"title":"LearningGoalsResponse","description":"Response schema for learning goals"},"LearningGoalsUpdate":{"properties":{"goals":{"items":{"$ref":"#/components/schemas/LearningGoal"},"type":"array","title":"Goals"}},"type":"object","required":["goals"],"title":"LearningGoalsUpdate","description":"Request schema for updating learning goals"},"LessonFeedbackIn":{"properties":{"progress_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Progress Id"},"stage":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stage"},"rating":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rating"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"}},"type":"object","title":"LessonFeedbackIn"},"LessonPassageResponse":{"properties":{"topic_id":{"type":"string","format":"uuid","title":"Topic Id"},"title":{"type":"string","title":"Title"},"content":{"type":"string","title":"Content"},"word_count":{"type":"integer","title":"Word Count"},"estimated_read_time":{"type":"integer","title":"Estimated Read Time"},"key_vocabulary":{"items":{"$ref":"#/components/schemas/VocabularyItem"},"type":"array","title":"Key Vocabulary","default":[]},"key_grammar":{"items":{"$ref":"#/components/schemas/GrammarItem"},"type":"array","title":"Key Grammar","default":[]},"discussion_prompts":{"items":{"type":"string"},"type":"array","title":"Discussion Prompts","default":[]},"difficulty_level":{"type":"string","title":"Difficulty Level"},"audio_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audio Url"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source","default":"AI-Generated by FluenTea"},"id":{"type":"string","format":"uuid","title":"Id"},"is_active":{"type":"boolean","title":"Is Active"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"topic":{"anyOf":[{"$ref":"#/components/schemas/LessonTopicResponse"},{"type":"null"}]},"user_progress":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Progress"},"title_translations":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Title Translations"},"description_translations":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Description Translations"}},"type":"object","required":["topic_id","title","content","word_count","estimated_read_time","difficulty_level","id","is_active","created_at","updated_at"],"title":"LessonPassageResponse"},"LessonProgressCheckpoint":{"properties":{"current_phase":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current Phase"},"vocab_drill_index":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Vocab Drill Index"},"roleplay_dialogue_index":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Roleplay Dialogue Index"},"grammar_index":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Grammar Index"}},"type":"object","title":"LessonProgressCheckpoint"},"LessonProgressCreate":{"properties":{"passage_id":{"type":"string","format":"uuid","title":"Passage Id"},"status":{"type":"string","title":"Status"}},"type":"object","required":["passage_id","status"],"title":"LessonProgressCreate"},"LessonProgressResponse":{"properties":{"passage_id":{"type":"string","format":"uuid","title":"Passage Id"},"status":{"type":"string","title":"Status"},"id":{"type":"string","format":"uuid","title":"Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"started_at":{"type":"string","format":"date-time","title":"Started At"},"reading_completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Reading Completed At"},"chacha_chat_started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Chacha Chat Started At"},"chacha_chat_completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Chacha Chat Completed At"},"human_practice_started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Human Practice Started At"},"human_practice_completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Human Practice Completed At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"reading_time_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reading Time Seconds"},"chacha_time_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Chacha Time Seconds"},"human_practice_time_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Human Practice Time Seconds"},"chacha_conversation_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Chacha Conversation Id"},"human_practice_call_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Human Practice Call Id"},"partner_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Partner User Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"passage":{"anyOf":[{"$ref":"#/components/schemas/LessonPassageResponse"},{"type":"null"}]}},"type":"object","required":["passage_id","status","id","user_id","started_at","created_at","updated_at"],"title":"LessonProgressResponse"},"LessonProgressUpdate":{"properties":{"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"reading_completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Reading Completed At"},"chacha_chat_started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Chacha Chat Started At"},"chacha_chat_completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Chacha Chat Completed At"},"human_practice_started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Human Practice Started At"},"human_practice_completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Human Practice Completed At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"reading_time_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reading Time Seconds"},"chacha_time_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Chacha Time Seconds"},"human_practice_time_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Human Practice Time Seconds"},"chacha_conversation_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Chacha Conversation Id"},"human_practice_call_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Human Practice Call Id"},"partner_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Partner User Id"}},"type":"object","title":"LessonProgressUpdate"},"LessonTopicResponse":{"properties":{"topic_name":{"type":"string","title":"Topic Name"},"category":{"type":"string","title":"Category"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"difficulty_level":{"type":"string","title":"Difficulty Level"},"target_language":{"type":"string","title":"Target Language"},"id":{"type":"string","format":"uuid","title":"Id"},"is_active":{"type":"boolean","title":"Is Active"},"display_order":{"type":"integer","title":"Display Order"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"passage_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Passage Count"}},"type":"object","required":["topic_name","category","difficulty_level","target_language","id","is_active","display_order","created_at","updated_at"],"title":"LessonTopicResponse"},"LiveSnapshotResponse":{"properties":{"languages":{"additionalProperties":{"$ref":"#/components/schemas/LanguageCounts"},"type":"object","title":"Languages"},"total_learners":{"type":"integer","title":"Total Learners"},"total_natives":{"type":"integer","title":"Total Natives"},"total_teababes":{"type":"integer","title":"Total Teababes"},"timestamp":{"type":"string","title":"Timestamp"}},"type":"object","required":["languages","total_learners","total_natives","total_teababes","timestamp"],"title":"LiveSnapshotResponse"},"MPCheckoutRequest":{"properties":{"tier":{"type":"string","title":"Tier","default":"lite"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"}},"type":"object","title":"MPCheckoutRequest"},"MadlibFillRequest":{"properties":{"word_id":{"type":"integer","title":"Word Id"},"language":{"type":"string","title":"Language"},"blank_index":{"type":"integer","title":"Blank Index"},"word_filled":{"type":"string","title":"Word Filled"}},"type":"object","required":["word_id","language","blank_index","word_filled"],"title":"MadlibFillRequest"},"MessageResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"conversation_id":{"type":"string","format":"uuid","title":"Conversation Id"},"sender_id":{"type":"string","format":"uuid","title":"Sender Id"},"content":{"type":"string","title":"Content"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"},"detected_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Detected Language"},"from_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From User Id"},"to_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To User Id"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"},"type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type"},"voice_note_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Voice Note Id"},"duration":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration"},"transcription":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transcription"},"voice_note_data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Voice Note Data"},"message_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message Type"},"message_metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Message Metadata"},"reply_to_message_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reply To Message Id"},"edited_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Edited At"},"reactions":{"anyOf":[{"additionalProperties":{"items":{},"type":"array"},"type":"object"},{"type":"null"}],"title":"Reactions"},"reply_to_preview":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Reply To Preview"},"ai_reply":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Ai Reply"},"metadata":{"anyOf":[{},{"type":"null"}],"title":"Metadata"}},"type":"object","required":["id","conversation_id","sender_id","content","timestamp"],"title":"MessageResponse"},"MessageSuggestionsRequest":{"properties":{"message":{"type":"string","title":"Message"},"message_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message Id"},"requesting_user_id":{"type":"string","title":"Requesting User Id"},"user_context":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"User Context"},"partner_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Partner User Id"},"is_live_call":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Live Call","default":false},"call_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Call Id"}},"type":"object","required":["message","requesting_user_id"],"title":"MessageSuggestionsRequest"},"MessageTranslateRequest":{"properties":{"message":{"type":"string","title":"Message"},"requesting_user_id":{"type":"string","title":"Requesting User Id"},"target_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Language"},"user_context":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"User Context"}},"type":"object","required":["message","requesting_user_id"],"title":"MessageTranslateRequest"},"MintCodeRequest":{"properties":{"redirect_uri":{"type":"string","title":"Redirect Uri"}},"type":"object","required":["redirect_uri"],"title":"MintCodeRequest"},"MoveBreakoutRequest":{"properties":{"user_id":{"type":"string","title":"User Id"},"breakout_room_name":{"type":"string","title":"Breakout Room Name"}},"type":"object","required":["user_id","breakout_room_name"],"title":"MoveBreakoutRequest"},"NextRoundRequest":{"properties":{"describer_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Describer User Id"}},"type":"object","title":"NextRoundRequest"},"NotificationResponse":{"properties":{"id":{"type":"string","title":"Id"},"notification_type":{"type":"string","title":"Notification Type"},"title":{"type":"string","title":"Title"},"message":{"type":"string","title":"Message"},"action_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action Url"},"action_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action Label"},"related_booking_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Related Booking Id"},"related_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Related User Id"},"is_read":{"type":"boolean","title":"Is Read"},"read_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Read At"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","notification_type","title","message","action_url","action_label","related_booking_id","related_user_id","is_read","read_at","created_at"],"title":"NotificationResponse","description":"Response schema for a notification"},"OAuthSignupComplete":{"properties":{"state":{"type":"string","title":"State"},"username":{"type":"string","title":"Username"},"acceptTerms":{"type":"boolean","title":"Acceptterms","default":false},"acceptPrivacy":{"type":"boolean","title":"Acceptprivacy","default":false},"acceptDataProcessing":{"type":"boolean","title":"Acceptdataprocessing","default":false},"confirmAge":{"type":"boolean","title":"Confirmage","default":false},"acceptBehaviorGuidelines":{"type":"boolean","title":"Acceptbehaviorguidelines","default":false},"marketingEmailsEnabled":{"type":"boolean","title":"Marketingemailsenabled","default":false},"productEmailsEnabled":{"type":"boolean","title":"Productemailsenabled","default":false},"signup_utm_source":{"type":"string","title":"Signup Utm Source"},"signup_utm_medium":{"type":"string","title":"Signup Utm Medium"},"signup_utm_campaign":{"type":"string","title":"Signup Utm Campaign"},"signup_utm_content":{"type":"string","title":"Signup Utm Content"},"signup_utm_term":{"type":"string","title":"Signup Utm Term"},"signup_gclid":{"type":"string","title":"Signup Gclid"},"signup_referrer":{"type":"string","title":"Signup Referrer"}},"type":"object","required":["state","username"],"title":"OAuthSignupComplete"},"OnboardingFunnelTrackingRequest":{"properties":{"eventType":{"type":"string","title":"Eventtype"},"timestamp":{"type":"string","title":"Timestamp"},"intent":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Intent"},"step":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Step"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"activity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Activity"},"anonymous_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Anonymous User Id"},"referrer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referrer"},"currentUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currenturl"},"utm":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Utm"}},"type":"object","required":["eventType","timestamp"],"title":"OnboardingFunnelTrackingRequest"},"PackageRequest":{"properties":{"title":{"type":"string","maxLength":200,"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"session_count":{"type":"integer","minimum":1.0,"title":"Session Count"},"session_duration_minutes":{"type":"integer","minimum":15.0,"title":"Session Duration Minutes"},"price_amount":{"type":"number","minimum":0.0,"title":"Price Amount"},"price_currency":{"type":"string","maxLength":3,"title":"Price Currency","default":"USD"}},"type":"object","required":["title","session_count","session_duration_minutes","price_amount"],"title":"PackageRequest","description":"Package data for tutor applications."},"PageViewRequest":{"properties":{"session_id":{"type":"string","title":"Session Id"},"page_url":{"type":"string","title":"Page Url"},"page_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Page Title"},"page_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Page Type"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"referrer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referrer"},"time_on_previous_page":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Time On Previous Page"},"utm_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Utm Source"},"utm_medium":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Utm Medium"},"utm_campaign":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Utm Campaign"},"utm_content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Utm Content"},"utm_term":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Utm Term"},"gclid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gclid"}},"type":"object","required":["session_id","page_url"],"title":"PageViewRequest"},"PaginatedUserDiscoveryResponse":{"properties":{"users":{"items":{"$ref":"#/components/schemas/UserDiscoveryResponse"},"type":"array","title":"Users"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"},"has_more":{"type":"boolean","title":"Has More"}},"type":"object","required":["users","total","limit","offset","has_more"],"title":"PaginatedUserDiscoveryResponse"},"PartnerMatchResponse":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"username":{"type":"string","title":"Username"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"profile_picture":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Picture"},"native_languages":{"items":{"type":"string"},"type":"array","title":"Native Languages"},"target_languages":{"items":{"type":"string"},"type":"array","title":"Target Languages"},"match_score":{"type":"number","title":"Match Score"},"match_reasons":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Match Reasons"}},"type":"object","required":["user_id","username","native_languages","target_languages","match_score","match_reasons"],"title":"PartnerMatchResponse"},"PassageResponse":{"properties":{"id":{"type":"string","title":"Id"},"language":{"type":"string","title":"Language"},"title":{"type":"string","title":"Title"},"content":{"type":"string","title":"Content"},"level":{"type":"string","title":"Level"},"source":{"type":"string","title":"Source"},"word_count":{"type":"integer","title":"Word Count"}},"type":"object","required":["id","language","title","content","level","source","word_count"],"title":"PassageResponse"},"PaymentHistoryResponse":{"properties":{"payments":{"items":{"$ref":"#/components/schemas/PaymentResponse"},"type":"array","title":"Payments"},"total_count":{"type":"integer","title":"Total Count"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"}},"type":"object","required":["payments","total_count","limit","offset"],"title":"PaymentHistoryResponse"},"PaymentResponse":{"properties":{"id":{"type":"string","title":"Id"},"amount":{"type":"integer","title":"Amount"},"currency":{"type":"string","title":"Currency"},"status":{"type":"string","title":"Status"},"payment_type":{"type":"string","title":"Payment Type"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","amount","currency","status","payment_type","created_at"],"title":"PaymentResponse"},"PhraseSubmitRequest":{"properties":{"content_id":{"type":"integer","title":"Content Id"},"answers":{"items":{},"type":"array","title":"Answers"},"native_lang":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Native Lang"}},"type":"object","required":["content_id","answers"],"title":"PhraseSubmitRequest"},"PollVoteRequest":{"properties":{"word_id":{"type":"integer","title":"Word Id"},"language":{"type":"string","title":"Language"},"poll_index":{"type":"integer","title":"Poll Index"},"poll_type":{"type":"string","title":"Poll Type"},"selected_option":{"type":"string","title":"Selected Option"}},"type":"object","required":["word_id","language","poll_index","poll_type","selected_option"],"title":"PollVoteRequest"},"PrivateAskRequest":{"properties":{"text":{"type":"string","maxLength":1000,"minLength":1,"title":"Text"},"client_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Client Id"},"to_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To User Id"}},"type":"object","required":["text"],"title":"PrivateAskRequest"},"PurchaseTokensRequest":{"properties":{"amount":{"type":"integer","title":"Amount"},"token_amount":{"type":"integer","title":"Token Amount"},"success_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Success Url"},"cancel_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cancel Url"}},"type":"object","required":["amount","token_amount"],"title":"PurchaseTokensRequest"},"PushPreferencesUpdate":{"properties":{"practice_reminders":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Practice Reminders"},"messages":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Messages"},"calls":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Calls"},"community":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Community"},"school":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"School"},"school_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"School Email"}},"type":"object","title":"PushPreferencesUpdate","description":"Notification preferences update request (partial).\n\nThe booleans are PUSH categories; `school_email` is the school EMAIL\nchannel and is a mode, not a boolean — \"off\" and \"batch it into one a day\"\nare different answers and collapsing them loses the middle one."},"QuickFeedbackResponse":{"properties":{"focus_area":{"type":"string","title":"Focus Area"},"rating":{"type":"number","title":"Rating"},"highlights":{"items":{"type":"string"},"type":"array","title":"Highlights"},"improvements":{"items":{"type":"string"},"type":"array","title":"Improvements"},"quick_tip":{"type":"string","title":"Quick Tip"},"booking_id":{"type":"string","title":"Booking Id"},"generated_at":{"type":"string","title":"Generated At"}},"type":"object","required":["focus_area","rating","highlights","improvements","quick_tip","booking_id","generated_at"],"title":"QuickFeedbackResponse","description":"Response schema for quick focused feedback"},"RankingSubmitRequest":{"properties":{"word_id":{"type":"integer","title":"Word Id"},"language":{"type":"string","title":"Language"},"ranking":{"items":{},"type":"array","title":"Ranking"}},"type":"object","required":["word_id","language","ranking"],"title":"RankingSubmitRequest"},"ReactionBody":{"properties":{"reaction_type":{"type":"string","title":"Reaction Type"}},"type":"object","required":["reaction_type"],"title":"ReactionBody"},"ReactionTapSubmitRequest":{"properties":{"content_id":{"type":"integer","title":"Content Id"},"taps":{"items":{},"type":"array","title":"Taps"}},"type":"object","required":["content_id","taps"],"title":"ReactionTapSubmitRequest"},"RecordAdViewRequest":{"properties":{"ad_provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ad Provider"},"ad_unit_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ad Unit Id"},"reward_type":{"type":"string","title":"Reward Type"},"reward_amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Reward Amount"},"verification_token":{"type":"string","title":"Verification Token"}},"type":"object","required":["reward_type","verification_token"],"title":"RecordAdViewRequest"},"RecordingSubmission":{"properties":{"passage_id":{"type":"string","title":"Passage Id"},"audio_data":{"type":"string","title":"Audio Data"},"duration_seconds":{"type":"integer","title":"Duration Seconds"},"section_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Section Id"}},"type":"object","required":["passage_id","audio_data","duration_seconds"],"title":"RecordingSubmission"},"RedeemInviteCodeRequest":{"properties":{"code":{"type":"string","title":"Code"}},"type":"object","required":["code"],"title":"RedeemInviteCodeRequest"},"RegisterDeviceRequest":{"properties":{"fcm_token":{"type":"string","title":"Fcm Token"},"platform":{"type":"string","title":"Platform"}},"type":"object","required":["fcm_token","platform"],"title":"RegisterDeviceRequest"},"RegistrationRequest":{"properties":{"username":{"type":"string","title":"Username"},"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","title":"Password"},"inviteCode":{"type":"string","title":"Invitecode"},"acceptTerms":{"type":"boolean","title":"Acceptterms","default":true},"acceptPrivacy":{"type":"boolean","title":"Acceptprivacy","default":true},"acceptDataProcessing":{"type":"boolean","title":"Acceptdataprocessing","default":true},"confirmAge":{"type":"boolean","title":"Confirmage","default":true},"acceptBehaviorGuidelines":{"type":"boolean","title":"Acceptbehaviorguidelines","default":true},"marketingEmailsEnabled":{"type":"boolean","title":"Marketingemailsenabled","default":false},"productEmailsEnabled":{"type":"boolean","title":"Productemailsenabled","default":false},"device_fingerprint":{"type":"string","title":"Device Fingerprint"},"ui_language":{"type":"string","title":"Ui Language","default":"en"},"anonymous_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Anonymous User Id"},"target_languages":{"items":{},"type":"array","title":"Target Languages","default":[]},"language_proficiency":{"additionalProperties":true,"type":"object","title":"Language Proficiency","default":{}},"native_languages":{"items":{},"type":"array","title":"Native Languages","default":[]},"native_language_proficiency":{"additionalProperties":true,"type":"object","title":"Native Language Proficiency","default":{}},"learning_reason":{"title":"Learning Reason"},"practice_method":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Practice Method"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"signup_utm_source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signup Utm Source"},"signup_utm_medium":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signup Utm Medium"},"signup_utm_campaign":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signup Utm Campaign"},"signup_utm_content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signup Utm Content"},"signup_utm_term":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signup Utm Term"},"signup_gclid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signup Gclid"},"signup_referrer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Signup Referrer"},"referral_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referral Code"},"invite_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invite Token"}},"type":"object","required":["email","password"],"title":"RegistrationRequest"},"RejectBookingRequest":{"properties":{"rejection_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rejection Reason"}},"type":"object","title":"RejectBookingRequest","description":"Request to reject a booking."},"ReportRequest":{"properties":{"reason":{"type":"string","maxLength":500,"title":"Reason","default":"inappropriate behavior"}},"type":"object","title":"ReportRequest"},"ReportTutorRequest":{"properties":{"reason":{"type":"string","maxLength":100,"minLength":1,"title":"Reason"},"description":{"type":"string","maxLength":2000,"minLength":10,"title":"Description"},"evidence_urls":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Evidence Urls"},"related_booking_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Related Booking Id"}},"type":"object","required":["reason","description"],"title":"ReportTutorRequest","description":"Request to report a tutor."},"RequestTutorRequest":{"properties":{"tutor_id":{"type":"string","title":"Tutor Id","description":"ID of the tutor to connect with"}},"type":"object","required":["tutor_id"],"title":"RequestTutorRequest","description":"Request to connect with a tutor."},"ReschedulePeerCallRequest":{"properties":{"scheduled_time":{"type":"string","format":"date-time","title":"Scheduled Time"},"duration_minutes":{"anyOf":[{"type":"integer","maximum":60.0,"minimum":15.0},{"type":"null"}],"title":"Duration Minutes"}},"type":"object","required":["scheduled_time"],"title":"ReschedulePeerCallRequest"},"ResendVerificationEmailRequest":{"properties":{"email":{"type":"string","title":"Email"},"language":{"type":"string","title":"Language","default":"en"}},"type":"object","required":["email"],"title":"ResendVerificationEmailRequest"},"ResetPasswordRequest":{"properties":{"token":{"type":"string","title":"Token"},"new_password":{"type":"string","title":"New Password"}},"type":"object","required":["token","new_password"],"title":"ResetPasswordRequest"},"RetentionMetricsResponse":{"properties":{"cohort_size":{"type":"integer","title":"Cohort Size"},"total_active_users":{"type":"integer","title":"Total Active Users"},"users_with_2plus_days":{"type":"integer","title":"Users With 2Plus Days"},"users_with_3plus_days":{"type":"integer","title":"Users With 3Plus Days"},"retention_rate_2day":{"type":"number","title":"Retention Rate 2Day"},"retention_rate_3day":{"type":"number","title":"Retention Rate 3Day"},"daily_cohorts":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Daily Cohorts"}},"type":"object","required":["cohort_size","total_active_users","users_with_2plus_days","users_with_3plus_days","retention_rate_2day","retention_rate_3day","daily_cohorts"],"title":"RetentionMetricsResponse"},"RoomPatch":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"start_iso":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Iso"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"video_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Video Enabled"},"multilingual":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Multilingual"},"max_participants":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Participants"},"visibility":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Visibility"},"activity_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Activity Type"},"exchange_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exchange Language"}},"type":"object","title":"RoomPatch"},"RoomSettings":{"properties":{"gcal_event_id":{"type":"string","maxLength":1024,"minLength":1,"title":"Gcal Event Id"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"start_iso":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Iso"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"video_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Video Enabled"},"max_participants":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Participants"},"visibility":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Visibility"},"activity_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Activity Type"},"exchange_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exchange Language"},"multilingual":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Multilingual"}},"type":"object","required":["gcal_event_id"],"title":"RoomSettings"},"SaveBreakoutLayoutRequest":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"rooms":{"items":{"items":{"type":"string"},"type":"array"},"type":"array","title":"Rooms"},"id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"}},"type":"object","required":["rooms"],"title":"SaveBreakoutLayoutRequest"},"SaveInterviewRequest":{"properties":{"why_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Why Language"},"real_situation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Real Situation"},"mini_lesson_topic":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mini Lesson Topic"},"target_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Language"},"native_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Native Language"}},"type":"object","title":"SaveInterviewRequest"},"SchoolSeat":{"properties":{"organization_id":{"type":"string","title":"Organization Id"},"name":{"type":"string","title":"Name"},"slug":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Slug"},"role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role"},"member_tier":{"type":"string","title":"Member Tier"},"premium_until":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Premium Until"},"entitles_now":{"type":"boolean","title":"Entitles Now","default":true}},"type":"object","required":["organization_id","name","member_tier"],"title":"SchoolSeat","description":"One School Space seat, as the account page needs to describe it.\n\nReported even when it entitles nothing — a self-serve school grants the free\ntier and an ended term grants nothing at all, and in both cases the person\nis still in a school. See usage_limiter.org_memberships."},"SendInviteEmailRequest":{"properties":{"invite_id":{"type":"string","title":"Invite Id"},"email":{"type":"string","title":"Email"}},"type":"object","required":["invite_id","email"],"title":"SendInviteEmailRequest"},"SendMessageRequest":{"properties":{"message":{"type":"string","maxLength":10000,"title":"Message"},"from_user_id":{"type":"string","title":"From User Id"},"to_user_id":{"type":"string","title":"To User Id"},"recipient_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Recipient Id"},"message_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message Type","default":"text"},"metadata":{"anyOf":[{},{"type":"null"}],"title":"Metadata"},"conversation_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Id"},"user_context":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"User Context"},"reply_to_message_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reply To Message Id"},"is_auto_greeting":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Auto Greeting","default":false}},"type":"object","required":["message","from_user_id","to_user_id"],"title":"SendMessageRequest"},"SendQuickCallEmailRequest":{"properties":{"email":{"type":"string","title":"Email"},"timezone":{"type":"string","title":"Timezone","default":"UTC"}},"type":"object","required":["email"],"title":"SendQuickCallEmailRequest"},"SessionCreateRequest":{"properties":{"session_id":{"type":"string","title":"Session Id"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"},"device_fingerprint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Device Fingerprint"}},"type":"object","required":["session_id"],"title":"SessionCreateRequest"},"SessionEndRequest":{"properties":{"session_id":{"type":"string","title":"Session Id"}},"type":"object","required":["session_id"],"title":"SessionEndRequest"},"SetClassMaterialSharedRequest":{"properties":{"shared":{"type":"boolean","title":"Shared"}},"type":"object","required":["shared"],"title":"SetClassMaterialSharedRequest"},"ShareClassMaterialRequest":{"properties":{"ref":{"type":"string","title":"Ref"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"shared":{"type":"boolean","title":"Shared","default":false}},"type":"object","required":["ref"],"title":"ShareClassMaterialRequest"},"ShareFlashcardDeckRequest":{"properties":{"deck_id":{"type":"string","title":"Deck Id"},"student_ids":{"items":{"type":"string"},"type":"array","title":"Student Ids"}},"type":"object","required":["deck_id","student_ids"],"title":"ShareFlashcardDeckRequest","description":"Request to share a flashcard deck with students."},"SharedDeckResponse":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"target_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Language"},"card_count":{"type":"integer","title":"Card Count"},"shared_with_count":{"type":"integer","title":"Shared With Count"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","title","description","target_language","card_count","shared_with_count","created_at"],"title":"SharedDeckResponse","description":"Response for a shared flashcard deck."},"SignupClickTrackingRequest":{"properties":{"timestamp":{"type":"string","title":"Timestamp"},"referrer":{"type":"string","title":"Referrer"},"currentUrl":{"type":"string","title":"Currenturl"},"userAgent":{"type":"string","title":"Useragent"},"utm":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Utm"},"sourceName":{"type":"string","title":"Sourcename"},"buttonLocation":{"type":"string","title":"Buttonlocation"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","default":"English"}},"type":"object","required":["timestamp","referrer","currentUrl","userAgent","sourceName","buttonLocation"],"title":"SignupClickTrackingRequest"},"SignupFunnelTrackingRequest":{"properties":{"eventType":{"type":"string","title":"Eventtype"},"timestamp":{"type":"string","title":"Timestamp"},"referrer":{"type":"string","title":"Referrer"},"currentUrl":{"type":"string","title":"Currenturl"},"userAgent":{"type":"string","title":"Useragent"},"utm":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Utm"},"sourceName":{"type":"string","title":"Sourcename"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","default":"English"},"page":{"type":"string","title":"Page"},"deviceType":{"type":"string","title":"Devicetype"},"screenWidth":{"type":"integer","title":"Screenwidth"},"screenHeight":{"type":"integer","title":"Screenheight"},"field":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Field"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider"},"userId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Userid"},"emailVerified":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Emailverified"},"statusCode":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Statuscode"},"errorType":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Errortype"}},"type":"object","required":["eventType","timestamp","referrer","currentUrl","userAgent","sourceName","page","deviceType","screenWidth","screenHeight"],"title":"SignupFunnelTrackingRequest"},"SkipBeginnerBody":{"properties":{"target_language":{"type":"string","maxLength":50,"minLength":1,"title":"Target Language"},"complete_beginner":{"type":"boolean","title":"Complete Beginner","default":false},"override_cefr":{"anyOf":[{"type":"string","pattern":"^(A1|A2|B1|B2|C1|C2)$"},{"type":"null"}],"title":"Override Cefr"}},"type":"object","required":["target_language"],"title":"SkipBeginnerBody"},"SoloRecordingSaveRequest":{"properties":{"language":{"type":"string","title":"Language"},"transcription":{"type":"string","title":"Transcription"},"analysis":{"additionalProperties":true,"type":"object","title":"Analysis"},"duration_seconds":{"type":"integer","title":"Duration Seconds"},"audio_file_path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audio File Path"}},"type":"object","required":["language","transcription","analysis","duration_seconds"],"title":"SoloRecordingSaveRequest"},"SparkleAssistRequest":{"properties":{"word_id":{"type":"integer","title":"Word Id"},"language":{"type":"string","title":"Language"},"native_language":{"type":"string","title":"Native Language"},"draft_text":{"type":"string","title":"Draft Text"},"question_context":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Question Context"}},"type":"object","required":["word_id","language","native_language","draft_text"],"title":"SparkleAssistRequest"},"SpeechRequest":{"properties":{"entry_id":{"type":"string","maxLength":200,"minLength":1,"title":"Entry Id"},"text":{"type":"string","maxLength":2000,"minLength":1,"title":"Text"}},"type":"object","required":["entry_id","text"],"title":"SpeechRequest"},"StartAssessmentBody":{"properties":{"target_language":{"type":"string","maxLength":50,"minLength":1,"title":"Target Language"},"modality":{"type":"string","pattern":"^(text|voice)$","title":"Modality","default":"text"},"intro_mode":{"anyOf":[{"type":"string","pattern":"^(a1_intent|a2plus_orient|returning_user|post_try_chacha)$"},{"type":"null"}],"title":"Intro Mode"}},"type":"object","required":["target_language"],"title":"StartAssessmentBody"},"StartGameEverywhereRequest":{"properties":{"game_type":{"type":"string","title":"Game Type"},"config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Config"}},"type":"object","required":["game_type"],"title":"StartGameEverywhereRequest"},"StartGameRequest":{"properties":{"game_type":{"type":"string","maxLength":30,"minLength":1,"title":"Game Type"},"config":{"additionalProperties":true,"type":"object","title":"Config"},"player_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Player Ids"}},"type":"object","required":["game_type"],"title":"StartGameRequest"},"StreamChatRequest":{"properties":{"message":{"type":"string","title":"Message"},"from_user_id":{"type":"string","title":"From User Id"},"to_user_id":{"type":"string","title":"To User Id"},"user_context":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"User Context"}},"type":"object","required":["message","from_user_id","to_user_id"],"title":"StreamChatRequest"},"StudentProgressResponse":{"properties":{"id":{"type":"string","title":"Id"},"student_id":{"type":"string","title":"Student Id"},"student_username":{"type":"string","title":"Student Username"},"student_full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Full Name"},"student_profile_picture":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Profile Picture"},"total_sessions":{"type":"integer","title":"Total Sessions"},"total_minutes":{"type":"integer","title":"Total Minutes"},"last_session_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Session At"},"tutor_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tutor Notes"},"learning_goals":{"items":{"type":"string"},"type":"array","title":"Learning Goals"},"flashcards_shared":{"type":"integer","title":"Flashcards Shared"},"flashcards_completed_by_student":{"type":"integer","title":"Flashcards Completed By Student"},"primary_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Primary Language"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"languages_learning":{"items":{"type":"string"},"type":"array","title":"Languages Learning"},"last_contact_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Contact Date"}},"type":"object","required":["id","student_id","student_username","student_full_name","student_profile_picture","total_sessions","total_minutes","last_session_at","tutor_notes","learning_goals","flashcards_shared","flashcards_completed_by_student","primary_language","created_at","updated_at","languages_learning","last_contact_date"],"title":"StudentProgressResponse","description":"Response for student progress."},"SubmitReviewRequest":{"properties":{"tutor_id":{"type":"string","title":"Tutor Id"},"rating":{"type":"integer","maximum":5.0,"minimum":1.0,"title":"Rating"},"review_text":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Review Text"},"language_taught":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Language Taught"},"booking_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Booking Id"}},"type":"object","required":["tutor_id","rating"],"title":"SubmitReviewRequest","description":"Request to submit a review for a tutor."},"SubscriptionResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"tier":{"type":"string","title":"Tier"},"effective_tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Effective Tier"},"own_tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Own Tier"},"is_premium":{"type":"boolean","title":"Is Premium","default":false},"school_premium":{"type":"boolean","title":"School Premium","default":false},"organization_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Organization Name"},"schools":{"items":{"$ref":"#/components/schemas/SchoolSeat"},"type":"array","title":"Schools","default":[]},"school_grants_extra":{"type":"boolean","title":"School Grants Extra","default":false},"has_billing":{"type":"boolean","title":"Has Billing","default":false},"limits":{"anyOf":[{"additionalProperties":{"anyOf":[{"type":"number"},{"type":"null"}]},"type":"object"},{"type":"null"}],"title":"Limits"},"own_limits":{"anyOf":[{"additionalProperties":{"anyOf":[{"type":"number"},{"type":"null"}]},"type":"object"},{"type":"null"}],"title":"Own Limits"},"status":{"type":"string","title":"Status"},"billing_cycle":{"type":"string","title":"Billing Cycle"},"billing_provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Billing Provider"},"play_product_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Play Product Id"},"play_auto_renewing":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Play Auto Renewing"},"stripe_customer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe Customer Id"},"stripe_subscription_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe Subscription Id"},"current_period_start":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Current Period Start"},"current_period_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Current Period End"},"trial_start":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Trial Start"},"trial_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Trial End"},"is_trial_active":{"type":"boolean","title":"Is Trial Active","default":false},"trial_tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trial Tier"},"post_trial_tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Post Trial Tier"},"cancel_at_period_end":{"type":"boolean","title":"Cancel At Period End","default":false},"canceled_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Canceled At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","user_id","tier","status","billing_cycle","created_at","updated_at"],"title":"SubscriptionResponse"},"SynonymRequest":{"properties":{"word":{"type":"string","title":"Word"},"language":{"type":"string","title":"Language"},"context":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context"}},"type":"object","required":["word","language"],"title":"SynonymRequest"},"SynonymResponse":{"properties":{"word":{"type":"string","title":"Word"},"language":{"type":"string","title":"Language"},"synonyms":{"items":{"type":"string"},"type":"array","title":"Synonyms"},"senses":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Senses","default":[]}},"type":"object","required":["word","language","synonyms"],"title":"SynonymResponse"},"SystemInfo":{"properties":{"gpu":{"$ref":"#/components/schemas/GPUInfo"},"models":{"additionalProperties":true,"type":"object","title":"Models"},"cpu":{"additionalProperties":true,"type":"object","title":"Cpu"},"memory":{"additionalProperties":true,"type":"object","title":"Memory"},"python_version":{"type":"string","title":"Python Version"},"platform":{"type":"string","title":"Platform"}},"type":"object","required":["gpu","models","cpu","memory","python_version","platform"],"title":"SystemInfo"},"TTSRequest":{"properties":{"text":{"type":"string","title":"Text"},"language":{"type":"string","title":"Language"}},"type":"object","required":["text","language"],"title":"TTSRequest"},"TalkNowClickTrackingRequest":{"properties":{"timestamp":{"type":"string","title":"Timestamp"},"referrer":{"type":"string","title":"Referrer"},"currentUrl":{"type":"string","title":"Currenturl"},"userAgent":{"type":"string","title":"Useragent"},"utm":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Utm"},"sourceName":{"type":"string","title":"Sourcename"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language","default":"English"},"preferences":{"items":{"type":"string"},"type":"array","title":"Preferences"},"languages":{"items":{"type":"string"},"type":"array","title":"Languages"},"transcriptionEnabled":{"type":"boolean","title":"Transcriptionenabled"}},"type":"object","required":["timestamp","referrer","currentUrl","userAgent","sourceName","preferences","languages","transcriptionEnabled"],"title":"TalkNowClickTrackingRequest"},"TeachingFeedbackResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"booking_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Booking Id"},"call_session_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Call Session Id"},"tutor_id":{"type":"string","format":"uuid","title":"Tutor Id"},"student_id":{"type":"string","format":"uuid","title":"Student Id"},"student_username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Username"},"student_full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Full Name"},"overall_rating":{"type":"number","title":"Overall Rating"},"teaching_style":{"additionalProperties":true,"type":"object","title":"Teaching Style"},"student_engagement":{"additionalProperties":true,"type":"object","title":"Student Engagement"},"areas_for_improvement":{"additionalProperties":true,"type":"object","title":"Areas For Improvement"},"encouragement":{"additionalProperties":true,"type":"object","title":"Encouragement"},"actionable_tips":{"items":{"type":"string"},"type":"array","title":"Actionable Tips"},"next_lesson_suggestions":{"items":{"type":"string"},"type":"array","title":"Next Lesson Suggestions"},"lesson_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lesson Summary"},"student_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Notes"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"}},"type":"object","required":["id","tutor_id","student_id","overall_rating","teaching_style","student_engagement","areas_for_improvement","encouragement","actionable_tips","next_lesson_suggestions","created_at"],"title":"TeachingFeedbackResponse","description":"Response schema for teaching feedback"},"TextMessageRequest":{"properties":{"message":{"type":"string","title":"Message"},"history":{"items":{"$ref":"#/components/schemas/TranscriptTurn"},"type":"array","title":"History","default":[]},"target_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Language"},"native_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Native Language"},"proficiency_level":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Proficiency Level"}},"type":"object","required":["message"],"title":"TextMessageRequest"},"TextTranslationRequest":{"properties":{"text":{"type":"string","title":"Text"},"source_language":{"type":"string","title":"Source Language","default":"auto"},"target_language":{"type":"string","title":"Target Language"}},"type":"object","required":["text","target_language"],"title":"TextTranslationRequest"},"ThinkingRoutineRequest":{"properties":{"word_id":{"type":"integer","title":"Word Id"},"language":{"type":"string","title":"Language"},"routine_type":{"type":"string","title":"Routine Type"},"responses":{"items":{},"type":"array","title":"Responses"}},"type":"object","required":["word_id","language","routine_type","responses"],"title":"ThinkingRoutineRequest"},"ToggleReactionRequest":{"properties":{"emoji":{"type":"string","title":"Emoji"},"user_id":{"type":"string","title":"User Id"}},"type":"object","required":["emoji","user_id"],"title":"ToggleReactionRequest"},"TokenBalanceResponse":{"properties":{"user_id":{"type":"string","title":"User Id"},"balance":{"type":"integer","title":"Balance"},"total_earned":{"type":"integer","title":"Total Earned"},"total_spent":{"type":"integer","title":"Total Spent"},"total_purchased":{"type":"integer","title":"Total Purchased"},"last_updated":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Updated"}},"type":"object","required":["user_id","balance","total_earned","total_spent","total_purchased"],"title":"TokenBalanceResponse"},"TokenRequest":{"properties":{"user_id":{"type":"string","title":"User Id"},"room_name":{"type":"string","title":"Room Name"}},"type":"object","required":["user_id","room_name"],"title":"TokenRequest"},"TranscriptEntryRequest":{"properties":{"content":{"type":"string","title":"Content"},"confidence":{"type":"number","title":"Confidence","default":0.0},"speaker_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Speaker Id"},"speaker_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Speaker Name"},"is_remote":{"type":"boolean","title":"Is Remote","default":false}},"type":"object","required":["content"],"title":"TranscriptEntryRequest"},"TranscriptTurn":{"properties":{"role":{"type":"string","title":"Role"},"text":{"type":"string","title":"Text"}},"type":"object","required":["role","text"],"title":"TranscriptTurn"},"TranscriptionLanguageRequest":{"properties":{"user_id":{"type":"string","title":"User Id"},"language":{"type":"string","title":"Language"},"call_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Call Id"}},"type":"object","required":["user_id","language"],"title":"TranscriptionLanguageRequest"},"TranslateAndExplainRequest":{"properties":{"text":{"type":"string","title":"Text"},"language":{"type":"string","title":"Language"},"native_language":{"type":"string","title":"Native Language"},"source_language":{"type":"string","title":"Source Language"},"context":{"anyOf":[{"items":{"additionalProperties":{"type":"string"},"type":"object"},"type":"array"},{"type":"null"}],"title":"Context"},"mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mode","default":"translation"},"proficiency_level":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Proficiency Level"}},"type":"object","required":["text","language","native_language","source_language"],"title":"TranslateAndExplainRequest"},"TranslationRequest":{"properties":{"message":{"type":"string","title":"Message"},"source_lang":{"type":"string","title":"Source Lang","default":"English"},"target_lang":{"type":"string","title":"Target Lang","default":"English"}},"type":"object","required":["message"],"title":"TranslationRequest"},"TranslationResponse":{"properties":{"translated_text":{"type":"string","title":"Translated Text"},"target_language":{"type":"string","title":"Target Language"}},"type":"object","required":["translated_text","target_language"],"title":"TranslationResponse"},"TrialChoiceRequest":{"properties":{"choice":{"type":"string","title":"Choice"},"invite_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invite Code"},"stripe_customer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe Customer Id"}},"type":"object","required":["choice"],"title":"TrialChoiceRequest","description":"Request for trial choice selection."},"TrialChoiceResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"choice":{"type":"string","title":"Choice"},"trial_active":{"type":"boolean","title":"Trial Active"},"trial_tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trial Tier"},"trial_end":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trial End"},"requires_payment":{"type":"boolean","title":"Requires Payment"},"message":{"type":"string","title":"Message"}},"type":"object","required":["success","choice","trial_active","requires_payment","message"],"title":"TrialChoiceResponse","description":"Response for trial choice."},"TutorAvailabilityResponse":{"properties":{"tutor_id":{"type":"string","title":"Tutor Id"},"blocked_times":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Blocked Times"},"booked_slots":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Booked Slots"},"available_slots":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Available Slots"}},"type":"object","required":["tutor_id","blocked_times","booked_slots","available_slots"],"title":"TutorAvailabilityResponse","description":"Tutor availability calendar response"},"TutorDirectoryResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"username":{"type":"string","title":"Username"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"profile_picture":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Picture"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"tagline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tagline"},"languages_taught":{"items":{"type":"string"},"type":"array","title":"Languages Taught"},"tutor_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tutor Type"},"is_verified":{"type":"boolean","title":"Is Verified"},"is_featured":{"type":"boolean","title":"Is Featured"},"average_rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Average Rating"},"total_reviews":{"type":"integer","title":"Total Reviews"},"user_reviews_count":{"type":"integer","title":"User Reviews Count"},"total_students":{"type":"integer","title":"Total Students"},"years_experience":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Years Experience"},"last_login_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Login At"},"intro_video_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Intro Video Url"},"country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Country"},"starting_hourly_rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Starting Hourly Rate"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"relationship_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Relationship Status"},"relationship_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Relationship Id"},"is_saved":{"type":"boolean","title":"Is Saved","default":false}},"type":"object","required":["id","user_id","username","full_name","profile_picture","display_name","tagline","languages_taught","is_verified","is_featured","average_rating","total_reviews","user_reviews_count","total_students","years_experience"],"title":"TutorDirectoryResponse","description":"Response for tutor directory listing."},"TutorInvitationResponse":{"properties":{"id":{"type":"string","title":"Id"},"tutor_id":{"type":"string","title":"Tutor Id"},"tutor_username":{"type":"string","title":"Tutor Username"},"tutor_full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tutor Full Name"},"tutor_profile_picture":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tutor Profile Picture"},"tutor_tagline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tutor Tagline"},"languages_taught":{"items":{"type":"string"},"type":"array","title":"Languages Taught"},"invited_at":{"type":"string","format":"date-time","title":"Invited At"},"status":{"type":"string","title":"Status"}},"type":"object","required":["id","tutor_id","tutor_username","tutor_full_name","tutor_profile_picture","tutor_tagline","languages_taught","invited_at","status"],"title":"TutorInvitationResponse","description":"Response for a tutor invitation."},"TutorPackageResponse":{"properties":{"id":{"type":"string","title":"Id"},"tutor_profile_id":{"type":"string","title":"Tutor Profile Id"},"title":{"type":"string","title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"session_count":{"type":"integer","title":"Session Count"},"session_duration_minutes":{"type":"integer","title":"Session Duration Minutes"},"price_amount":{"type":"number","title":"Price Amount"},"price_currency":{"type":"string","title":"Price Currency"},"is_active":{"type":"boolean","title":"Is Active"},"times_purchased":{"type":"integer","title":"Times Purchased"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","tutor_profile_id","title","description","session_count","session_duration_minutes","price_amount","price_currency","is_active","times_purchased","created_at"],"title":"TutorPackageResponse","description":"Response for tutor package."},"TutorReviewResponse":{"properties":{"id":{"type":"string","title":"Id"},"reviewer_id":{"type":"string","title":"Reviewer Id"},"reviewer_username":{"type":"string","title":"Reviewer Username"},"reviewer_full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reviewer Full Name"},"reviewer_profile_picture":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reviewer Profile Picture"},"rating":{"type":"integer","title":"Rating"},"review_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Review Text"},"language_experience":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language Experience"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","reviewer_id","reviewer_username","reviewer_full_name","reviewer_profile_picture","rating","review_text","language_experience","created_at"],"title":"TutorReviewResponse","description":"Response for a tutor review."},"TutorStatsResponse":{"properties":{"total_sessions":{"type":"integer","title":"Total Sessions"},"total_call_minutes":{"type":"integer","title":"Total Call Minutes"},"total_students":{"type":"integer","title":"Total Students"},"new_students_this_period":{"type":"integer","title":"New Students This Period"},"messages_received":{"type":"integer","title":"Messages Received"},"average_session_duration":{"type":"number","title":"Average Session Duration"}},"type":"object","required":["total_sessions","total_call_minutes","total_students","new_students_this_period","messages_received","average_session_duration"],"title":"TutorStatsResponse","description":"Tutor statistics for a specific period."},"TutorWaitlistRequest":{"properties":{"email":{"type":"string","title":"Email"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"languages":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Languages"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"}},"type":"object","required":["email"],"title":"TutorWaitlistRequest"},"UpdateBookingRequest":{"properties":{"scheduled_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Scheduled Time"},"duration_minutes":{"anyOf":[{"type":"integer","maximum":180.0,"minimum":15.0},{"type":"null"}],"title":"Duration Minutes"},"tutor_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tutor Notes"},"student_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Student Notes"}},"type":"object","title":"UpdateBookingRequest","description":"Request to update a booking."},"UpdatePackageRequest":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"session_count":{"anyOf":[{"type":"integer","maximum":100.0,"minimum":1.0},{"type":"null"}],"title":"Session Count"},"session_duration_minutes":{"anyOf":[{"type":"integer","maximum":180.0,"minimum":15.0},{"type":"null"}],"title":"Session Duration Minutes"},"price_amount":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Price Amount"},"price_currency":{"anyOf":[{"type":"string","maxLength":3},{"type":"null"}],"title":"Price Currency"},"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"}},"type":"object","title":"UpdatePackageRequest","description":"Request to update a pricing package."},"UpdateStudentNotesRequest":{"properties":{"tutor_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tutor Notes"},"learning_goals":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Learning Goals"},"primary_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Primary Language"}},"type":"object","title":"UpdateStudentNotesRequest","description":"Request to update tutor notes for a student."},"UpdateTutorProfileRequest":{"properties":{"display_name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Display Name"},"bio":{"anyOf":[{"type":"string","maxLength":2000,"minLength":50},{"type":"null"}],"title":"Bio"},"tagline":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Tagline"},"gender":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Gender"},"country":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Country"},"intro_video_url":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Intro Video Url"},"languages_taught":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Languages Taught"},"years_experience":{"anyOf":[{"type":"integer","maximum":50.0,"minimum":0.0},{"type":"null"}],"title":"Years Experience"},"certifications":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Certifications"},"teaching_style":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Teaching Style"},"specializations":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Specializations"},"timezone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timezone"},"availability_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Availability Notes"},"availability_schedule":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Availability Schedule"},"external_payment_info":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Payment Info"},"custom_starting_hourly_rate":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Custom Starting Hourly Rate"},"custom_starting_currency":{"anyOf":[{"type":"string","maxLength":3},{"type":"null"}],"title":"Custom Starting Currency"},"is_visible":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Visible"}},"type":"object","title":"UpdateTutorProfileRequest","description":"Request to update tutor profile."},"UpgradeSubscriptionRequest":{"properties":{"tier":{"type":"string","title":"Tier"},"billing_cycle":{"type":"string","title":"Billing Cycle"}},"type":"object","required":["tier","billing_cycle"],"title":"UpgradeSubscriptionRequest"},"UsageBreakdown":{"properties":{"percentage_used":{"type":"number","title":"Percentage Used"},"used":{"type":"number","title":"Used"},"limit":{"type":"number","title":"Limit"},"window":{"type":"string","title":"Window","default":"day"},"resets_on":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resets On"},"resets_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resets At"}},"type":"object","required":["percentage_used","used","limit"],"title":"UsageBreakdown","description":"Usage breakdown with percentage, used, and limit.\n\n`window` says what period `used` and `limit` are measured over. It defaults\nto \"day\" because every consumer tier is rationed daily; a school seat is\nrationed by the WEEK, and a screen that prints a weekly figure under the\nword \"today\" is simply wrong. `resets_on` is the last date of that window.\n\n`resets_at` is the exact INSTANT it next rolls over, in UTC, so the client\ncan print it on the reader's own clock. The daily counters turn over at\nmidnight New York and a school week turns over at midnight in the school's\nzone — neither is the reader's midnight, so \"resets at midnight\" was\ntelling a learner in Berlin something untrue by six hours."},"UsageStatsResponse":{"properties":{"tier":{"type":"string","title":"Tier"},"chacha_messages":{"$ref":"#/components/schemas/UsageBreakdown"},"daily_transcription":{"$ref":"#/components/schemas/UsageBreakdown"},"daily_voice":{"anyOf":[{"$ref":"#/components/schemas/UsageBreakdown"},{"type":"null"}]},"call_minutes_per_call":{"type":"number","title":"Call Minutes Per Call"},"ai_usage":{"$ref":"#/components/schemas/UsageBreakdown"},"live_transcriptions":{"$ref":"#/components/schemas/UsageBreakdown"},"call_minutes":{"$ref":"#/components/schemas/UsageBreakdown"},"detailed_reports":{"anyOf":[{"$ref":"#/components/schemas/UsageBreakdown"},{"type":"null"}]},"is_trial_active":{"type":"boolean","title":"Is Trial Active"},"trial_end_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Trial End Date"}},"type":"object","required":["tier","chacha_messages","daily_transcription","call_minutes_per_call","ai_usage","live_transcriptions","call_minutes","is_trial_active"],"title":"UsageStatsResponse","description":"Response for usage statistics (2-tier daily limits)"},"UserBlockResponse":{"properties":{"id":{"type":"string","title":"Id"},"blocked_user_id":{"type":"string","title":"Blocked User Id"},"is_active":{"type":"boolean","title":"Is Active"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","blocked_user_id","is_active","created_at"],"title":"UserBlockResponse"},"UserBreachNotificationRequest":{"properties":{"affected_user_ids":{"items":{"type":"string"},"type":"array","title":"Affected User Ids"}},"type":"object","required":["affected_user_ids"],"title":"UserBreachNotificationRequest"},"UserConsentResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"consent_type":{"type":"string","title":"Consent Type"},"consent_given":{"type":"boolean","title":"Consent Given"},"consent_version":{"type":"string","title":"Consent Version"},"consent_timestamp":{"type":"string","format":"date-time","title":"Consent Timestamp"},"ip_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ip Address"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata"}},"type":"object","required":["id","user_id","consent_type","consent_given","consent_version","consent_timestamp"],"title":"UserConsentResponse"},"UserDiscoveryResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"username":{"type":"string","title":"Username"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"profile_picture":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Picture"},"native_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Native Language"},"native_languages":{"items":{"type":"string"},"type":"array","title":"Native Languages","default":[]},"target_languages":{"items":{"type":"string"},"type":"array","title":"Target Languages"},"profile_data":{"additionalProperties":true,"type":"object","title":"Profile Data"},"interests":{"items":{"type":"string"},"type":"array","title":"Interests","default":[]},"last_login_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Login At"},"average_rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Average Rating"},"review_count":{"type":"integer","title":"Review Count","default":0},"is_friend":{"type":"boolean","title":"Is Friend","default":false},"friend_request_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Friend Request Status"},"account_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Account Type","default":"student"},"is_staff":{"type":"boolean","title":"Is Staff","default":false},"is_ai_persona":{"type":"boolean","title":"Is Ai Persona","default":false},"school_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"School Name"},"relationship_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Relationship Status"},"has_booking_today":{"type":"boolean","title":"Has Booking Today","default":false},"starting_hourly_rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Starting Hourly Rate"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Currency"},"badge_count":{"type":"integer","title":"Badge Count","default":0}},"type":"object","required":["id","username","full_name","profile_picture","native_language","target_languages","profile_data"],"title":"UserDiscoveryResponse"},"UserProfileEditUpdate":{"properties":{"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"profile_picture":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Picture"},"native_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Native Language"},"native_languages":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Native Languages"},"target_languages":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Target Languages"},"language_proficiency":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Language Proficiency"},"show_pinyin":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Show Pinyin"},"profile_data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Profile Data"},"user_gender":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Gender"},"contact_gender_preference":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Contact Gender Preference"},"show_gender_publicly":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Show Gender Publicly"},"platform_goals":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Platform Goals"},"conversation_native_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Native Language"},"conversation_target_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Target Language"},"save_my_transcripts":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Save My Transcripts"},"allow_others_to_save_my_transcripts":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Allow Others To Save My Transcripts"},"marketing_emails_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Marketing Emails Enabled"},"product_emails_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Product Emails Enabled"},"unread_message_emails_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Unread Message Emails Enabled"},"has_seen_welcome_tour":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Seen Welcome Tour"},"timezone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timezone"},"interests":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Interests"},"learning_reason":{"anyOf":[{},{"type":"null"}],"title":"Learning Reason"},"practice_method":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Practice Method"}},"type":"object","title":"UserProfileEditUpdate"},"UserProfileResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"username":{"type":"string","title":"Username"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"profile_picture":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Picture"},"native_language":{"type":"string","title":"Native Language"},"native_languages":{"items":{"type":"string"},"type":"array","title":"Native Languages","default":["English"]},"target_languages":{"items":{"type":"string"},"type":"array","title":"Target Languages"},"language_proficiency":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Language Proficiency"},"profile_data":{"additionalProperties":true,"type":"object","title":"Profile Data"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"last_login_at":{"type":"string","format":"date-time","title":"Last Login At"},"user_gender":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Gender"},"contact_gender_preference":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Contact Gender Preference"},"show_gender_publicly":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Show Gender Publicly","default":true},"conversation_native_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Native Language"},"conversation_target_language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Target Language"},"save_my_transcripts":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Save My Transcripts"},"allow_others_to_save_my_transcripts":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Allow Others To Save My Transcripts"},"marketing_emails_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Marketing Emails Enabled","default":true},"product_emails_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Product Emails Enabled","default":true},"unread_message_emails_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Unread Message Emails Enabled","default":true},"unread_notification_frequency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unread Notification Frequency","default":"progressive"},"last_unread_notification_sent_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Unread Notification Sent At"},"show_pinyin":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Show Pinyin","default":true},"profile_complete":{"type":"boolean","title":"Profile Complete","default":false},"has_seen_welcome_tour":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Seen Welcome Tour"},"timezone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timezone"},"interests":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Interests"},"learning_reason":{"anyOf":[{},{"type":"null"}],"title":"Learning Reason"},"practice_method":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Practice Method"},"average_rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Average Rating"},"review_count":{"type":"integer","title":"Review Count","default":0},"is_friend":{"type":"boolean","title":"Is Friend","default":false},"friend_request_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Friend Request Status"},"friend_request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Friend Request Id"},"account_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Account Type","default":"student"},"is_staff":{"type":"boolean","title":"Is Staff","default":false},"is_ai_persona":{"type":"boolean","title":"Is Ai Persona","default":false},"tutor_profile":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Tutor Profile"},"relationship_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Relationship Status"},"has_trial_lesson_today":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has Trial Lesson Today","default":false}},"type":"object","required":["id","username","full_name","profile_picture","native_language","target_languages","profile_data","created_at","last_login_at"],"title":"UserProfileResponse"},"UserReportCreate":{"properties":{"reported_user_id":{"type":"string","title":"Reported User Id"},"reason":{"type":"string","title":"Reason"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"evidence":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Evidence"}},"type":"object","required":["reported_user_id","reason"],"title":"UserReportCreate"},"UserReportRequest":{"properties":{"reported_user_id":{"type":"string","format":"uuid4","title":"Reported User Id"},"reason":{"type":"string","title":"Reason"},"description":{"type":"string","title":"Description"},"evidence":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Evidence"}},"type":"object","required":["reported_user_id","reason","description"],"title":"UserReportRequest"},"UserReviewCreate":{"properties":{"reviewed_user_id":{"type":"string","title":"Reviewed User Id"},"rating":{"type":"integer","title":"Rating"},"review_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Review Text"},"language_experience":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language Experience"},"technical_rating":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Technical Rating"}},"type":"object","required":["reviewed_user_id","rating"],"title":"UserReviewCreate"},"UserReviewResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"reviewer_id":{"type":"string","format":"uuid","title":"Reviewer Id"},"reviewed_user_id":{"type":"string","format":"uuid","title":"Reviewed User Id"},"rating":{"type":"integer","title":"Rating"},"review_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Review Text"},"language_experience":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language Experience"},"technical_rating":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Technical Rating"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"reviewer_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reviewer Name"},"reviewer_avatar":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reviewer Avatar"}},"type":"object","required":["id","reviewer_id","reviewed_user_id","rating","review_text","language_experience","created_at"],"title":"UserReviewResponse"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VocabularyItem":{"properties":{"word":{"type":"string","title":"Word"},"definition":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Definition"},"translation":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Translation"},"example":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Example"},"definitions":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Definitions"}},"type":"object","required":["word"],"title":"VocabularyItem"},"VoiceCallRequest":{"properties":{"caller_id":{"type":"string","title":"Caller Id"},"callee_id":{"type":"string","title":"Callee Id"}},"type":"object","required":["caller_id","callee_id"],"title":"VoiceCallRequest"},"WordAssociationRequest":{"properties":{"word_id":{"type":"integer","title":"Word Id"},"language":{"type":"string","title":"Language"},"associated_words":{"items":{},"type":"array","title":"Associated Words"}},"type":"object","required":["word_id","language","associated_words"],"title":"WordAssociationRequest"},"WordTranslationRequest":{"properties":{"word":{"type":"string","title":"Word"},"source_language":{"type":"string","title":"Source Language"},"target_language":{"type":"string","title":"Target Language"},"context":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context"}},"type":"object","required":["word","source_language","target_language"],"title":"WordTranslationRequest"},"app__models__schemas__EmailPreferencesUpdate":{"properties":{"marketing_emails":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Marketing Emails"},"transactional_emails":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Transactional Emails"},"newsletter":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Newsletter"}},"type":"object","title":"EmailPreferencesUpdate"},"app__models__schemas__UserReportResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"reporter_id":{"type":"string","format":"uuid","title":"Reporter Id"},"reported_user_id":{"type":"string","format":"uuid","title":"Reported User Id"},"reason":{"type":"string","title":"Reason"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"evidence":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Evidence"},"status":{"type":"string","title":"Status"},"admin_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Admin Notes"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"resolved_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Resolved At"}},"type":"object","required":["id","reporter_id","reported_user_id","reason","description","evidence","status","admin_notes","created_at","resolved_at"],"title":"UserReportResponse"},"app__routes__notification_routes__EmailPreferencesUpdate":{"properties":{"marketing_emails_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Marketing Emails Enabled"},"product_emails_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Product Emails Enabled"},"unread_message_emails_enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Unread Message Emails Enabled"},"unread_notification_frequency":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unread Notification Frequency"}},"type":"object","title":"EmailPreferencesUpdate","description":"Email notification preferences update request"},"app__schemas__users__UserReportResponse":{"properties":{"id":{"type":"string","title":"Id"},"reported_user_id":{"type":"string","title":"Reported User Id"},"reason":{"type":"string","title":"Reason"},"status":{"type":"string","title":"Status"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"email_sent":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Email Sent"}},"type":"object","required":["id","reported_user_id","reason","status","created_at"],"title":"UserReportResponse"}}}}