{"openapi":"3.1.0","info":{"title":"Chaldea App","version":"0.1.0"},"paths":{"/api/v1/auth/register":{"post":{"tags":["auth"],"summary":"Register","description":"Step 1: Register - send verification code to email","operationId":"auth-register","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/verify-register":{"post":{"tags":["auth"],"summary":"Verify Register","description":"Step 2: Verify registration code and create account","operationId":"auth-verify_register","parameters":[{"name":"x-chaldea-uuid","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Uuid"}},{"name":"x-chaldea-ver","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Ver"}},{"name":"x-chaldea-build","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Build"}},{"name":"x-chaldea-lang","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Lang"}},{"name":"x-chaldea-platform","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Platform"}},{"name":"x-chaldea-client","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Client"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyRegisterRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/login":{"post":{"tags":["auth"],"summary":"Login","description":"User login with username or email","operationId":"auth-login","parameters":[{"name":"x-chaldea-uuid","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Uuid"}},{"name":"x-chaldea-ver","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Ver"}},{"name":"x-chaldea-build","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Build"}},{"name":"x-chaldea-lang","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Lang"}},{"name":"x-chaldea-platform","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Platform"}},{"name":"x-chaldea-client","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Client"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/token":{"post":{"tags":["auth"],"summary":"Login For Swagger","description":"OAuth2 compatible token endpoint for Swagger UI authorization.","operationId":"auth-login_for_swagger","parameters":[{"name":"x-chaldea-uuid","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Uuid"}},{"name":"x-chaldea-ver","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Ver"}},{"name":"x-chaldea-build","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Build"}},{"name":"x-chaldea-lang","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Lang"}},{"name":"x-chaldea-platform","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Platform"}},{"name":"x-chaldea-client","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Client"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_auth-login_for_swagger"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/logout":{"post":{"tags":["auth"],"summary":"Logout","description":"Logout. Delete current device session or all sessions.\n\nBest-effort cleanup of legacy migration-source rows in user_sessions.\nNew logins no longer write session rows, so for new users this is a\nno-op. JWT revocation is handled by token_version, not by session\ndeletion.","operationId":"auth-logout","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"all_devices","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"All Devices"}},{"name":"x-chaldea-uuid","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Uuid"}},{"name":"x-chaldea-ver","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Ver"}},{"name":"x-chaldea-build","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Build"}},{"name":"x-chaldea-lang","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Lang"}},{"name":"x-chaldea-platform","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Platform"}},{"name":"x-chaldea-client","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Client"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/refresh":{"post":{"tags":["auth"],"summary":"Refresh Token","description":"Refresh JWT token.\n\nThe new token's expiry follows the current request's client type, not the\noriginal login's.","operationId":"auth-refresh_token","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"x-chaldea-uuid","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Uuid"}},{"name":"x-chaldea-ver","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Ver"}},{"name":"x-chaldea-build","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Build"}},{"name":"x-chaldea-lang","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Lang"}},{"name":"x-chaldea-platform","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Platform"}},{"name":"x-chaldea-client","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Client"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/migrate-token":{"post":{"tags":["auth"],"summary":"Migrate Token","description":"Exchange a legacy Worker `secret` for a JWT.\n\nThe client sends the exact `secret` value it stored at login time, which is\n`base64(\"userId:sessionId\")` (see api-worker `generateSecret`). The server\ndecodes it, looks up `user_sessions` by the (user_id, session_id) pair, and\nissues a JWT for the matched user.\n\nIdempotent: presenting the same `secret` multiple times returns a fresh JWT\neach time. The endpoint does NOT delete the `user_sessions` row on success —\ncleanup relies on existing `login` (overwrites same-device row) and `logout`\n(deletes row) flows, so the user is never locked out by a crash between\nreceiving the JWT and persisting it locally.\n\nThe 36-char uuid `session_id` is computationally unguessable, so a lingering\nrow after migration poses negligible auth risk (and the row has no auth\npower on this server anyway).","operationId":"auth-migrate_token","parameters":[{"name":"x-chaldea-uuid","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Uuid"}},{"name":"x-chaldea-ver","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Ver"}},{"name":"x-chaldea-build","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Build"}},{"name":"x-chaldea-lang","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Lang"}},{"name":"x-chaldea-platform","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Platform"}},{"name":"x-chaldea-client","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Client"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MigrateTokenRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/forgot-password":{"post":{"tags":["auth"],"summary":"Forgot Password","description":"Forgot password - send verification code to email","operationId":"auth-forgot_password","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForgotPasswordRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/recover-by-device":{"post":{"tags":["auth"],"summary":"Recover By Device","description":"Recover account via username + latest device_id + new email (Method 2).\n\nFor migrated users whose `users.email` is NULL. If the username exists, has\nno bound email, and the device_id matches the latest entry in `user_logins`\n(by created_at OR updated_at), a verification code is sent to `new_email`.\n\n- Username not found: explicit 404 (registration page already reveals username existence)\n- User already has email bound: 409 + security warning to real owner\n- Device mismatch: 400 with a generic \"info invalid\" error (anti-brute-force\n  is handled by the per-username lockout counter; silent success only left\n  legitimate users waiting for an email that never arrives)\n- New email taken: 409 (same enumeration exposure as /register)","operationId":"auth-recover_by_device","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecoverByDeviceRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/reset-password":{"post":{"tags":["auth"],"summary":"Reset Password","description":"Reset password using verification code (Method 1: email-based)","operationId":"auth-reset_password","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPasswordRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/reset-password-by-device":{"post":{"tags":["auth"],"summary":"Reset Password By Device","description":"Reset password using device recovery verification code (Method 2)","operationId":"auth-reset_password_by_device","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPasswordByDeviceRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/reset-password-by-token":{"post":{"tags":["auth"],"summary":"Reset Password By Token","description":"Reset password using admin-generated JWT token (Method 3).\n\nThe token is generated by admin's /admin/recover-user endpoint and sent to the user's\nemail. It contains:\n- sub: user_id\n- ver: token_version (must match user's current token_version)\n- bind_email: optional email to bind after password reset","operationId":"auth-reset_password_by_token","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPasswordByTokenRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me":{"get":{"tags":["users"],"summary":"Get Current User Info","description":"Get current user info","operationId":"users-get_current_user_info","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInfo"}}}}},"security":[{"OAuth2PasswordBearer":[]}]},"delete":{"tags":["users"],"summary":"Delete Current User","description":"Delete current user and all associated data","operationId":"users-delete_current_user","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteAccountRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]},"patch":{"tags":["users"],"summary":"Update Current User","description":"Update current user info","operationId":"users-update_current_user","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInfo"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/users/me/change-email":{"post":{"tags":["users"],"summary":"Change Email","description":"Step 1: Request email change - send verification code to new email.\n\nRequires `current_password` to re-confirm identity before initiating\nthe change. Email is the root-trust anchor of the account (used for\nforgot-password and security alerts), so a live JWT alone is not\nsufficient authority to swap it. See the `secure-email-change` OpenSpec\nchange for the threat model.","operationId":"users-change_email","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangeEmailRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/users/me/verify-email":{"post":{"tags":["users"],"summary":"Verify Email","description":"Step 2: Verify email change code and update email","operationId":"users-verify_email","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyEmailRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/users/me/password":{"patch":{"tags":["users"],"summary":"Change Password","description":"Change password","operationId":"users-change_password","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordChange"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/users/me/backups":{"get":{"tags":["users"],"summary":"List Backups","description":"Get user backup list","operationId":"users-list_backups","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/UserBackup"},"type":"array","title":"Response Users-List Backups"}}}}},"security":[{"OAuth2PasswordBearer":[]}]},"post":{"tags":["users"],"summary":"Create Backup","description":"Create backup (max 3)","operationId":"users-create_backup","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackupCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackupCreateResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/users/me/backups/{backup_id}":{"get":{"tags":["users"],"summary":"Get Backup","description":"Get specific backup","operationId":"users-get_backup","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"backup_id","in":"path","required":true,"schema":{"type":"integer","title":"Backup Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserBackup"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/teams/":{"get":{"tags":["teams"],"summary":"Search Teams","description":"Search teams","operationId":"teams-search_teams","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"questId","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"integer"}},{"type":"null"}],"description":"Match any quest ID; repeat questId for multiple quests.","title":"Questid"},"description":"Match any quest ID; repeat questId for multiple quests."},{"name":"userId","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Userid"}},{"name":"username","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Username"}},{"name":"phase","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Phase"}},{"name":"enemyHash","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Enemyhash"}},{"name":"ids","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"integer"}},{"type":"null"}],"title":"Ids"}},{"name":"decode","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Decode"}},{"name":"sort","in":"query","required":false,"schema":{"$ref":"#/components/schemas/TeamSortType","default":"upvotes"}},{"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":200,"minimum":1,"default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamSearchResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["teams"],"summary":"Create Team","description":"Create team — returns the created team's basic info (no message).","operationId":"teams-create_team","security":[{"OAuth2PasswordBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamCreateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/teams/ranking":{"get":{"tags":["teams"],"summary":"Get Ranking","description":"Get team ranking","operationId":"teams-get_ranking","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"decode","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Decode"}},{"name":"sort","in":"query","required":false,"schema":{"$ref":"#/components/schemas/TeamSortType","default":"downvotes"}},{"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":200,"minimum":1,"default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamSearchResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/teams/voted":{"get":{"tags":["teams"],"summary":"Get Voted Teams","description":"Get teams voted by user","operationId":"teams-get_voted_teams","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"userId","in":"query","required":true,"schema":{"type":"integer","title":"Userid"}},{"name":"value","in":"query","required":false,"schema":{"type":"integer","description":"1=upvote, -1=downvote, 0=all","default":1,"title":"Value"},"description":"1=upvote, -1=downvote, 0=all"},{"name":"decode","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Decode"}},{"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":200,"minimum":1,"default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamSearchResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/teams/{team_id}":{"get":{"tags":["teams"],"summary":"Get Team Detail","description":"Get team detail","operationId":"teams-get_team_detail","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"team_id","in":"path","required":true,"schema":{"type":"integer","title":"Team Id"}},{"name":"decode","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Decode"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamItem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["teams"],"summary":"Update Team","description":"Update team (admin)","operationId":"teams-update_team","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"team_id","in":"path","required":true,"schema":{"type":"integer","title":"Team Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["teams"],"summary":"Delete Team","description":"Delete team (owner or teamMod+)","operationId":"teams-delete_team","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"team_id","in":"path","required":true,"schema":{"type":"integer","title":"Team Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/teams/{team_id}/vote":{"post":{"tags":["teams"],"summary":"Vote Team","description":"Vote on a team","operationId":"teams-vote_team","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"team_id","in":"path","required":true,"schema":{"type":"integer","title":"Team Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoteResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/db-gc":{"post":{"tags":["admin"],"summary":"Db Gc","description":"Database garbage collection - delete backups older than 7 days and VACUUM","operationId":"admin-db_gc","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/admin/users":{"get":{"tags":["admin"],"summary":"List Users","description":"List users with optional search and role filter","operationId":"admin-list_users","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"}},{"name":"role","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Role"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminUsersList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["admin"],"summary":"Create User","description":"Create a new user (admin operation)","operationId":"admin-create_user","security":[{"OAuth2PasswordBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminUserCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInfo"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}":{"patch":{"tags":["admin"],"summary":"Update User","description":"Update a user (admin operation)","operationId":"admin-update_user","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminUserUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInfo"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Delete User","description":"Delete a user (admin operation)","operationId":"admin-delete_user","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["admin"],"summary":"Get User Detail","description":"Get user detail (including sessions and login records)","operationId":"admin-get_user_detail","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminUserDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/sessions":{"delete":{"tags":["admin"],"summary":"Revoke All Sessions","description":"Revoke all sessions for a user","operationId":"admin-revoke_all_sessions","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/sessions/{device}":{"delete":{"tags":["admin"],"summary":"Revoke Session","description":"Revoke a specific session for a user by device","operationId":"admin-revoke_session","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}},{"name":"device","in":"path","required":true,"schema":{"type":"string","title":"Device"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/recover":{"post":{"tags":["admin"],"summary":"Recover User","description":"Manual account recovery (Method 3 backend).\n\nTwo modes (email mode takes precedence if both provided):\n- Email mode: send a reset link with `bind_email` claim to `body.email`.\n  The user clicks the link to set a new password AND bind the email.\n- Direct password mode: set `body.password` directly, revoke all sessions,\n  increment token_version. No email is bound.\n\nReturns a rich bilingual response with action details and reminders for\nthe admin.","operationId":"admin-recover_user","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminRecoverRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminRecoverResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tools/sign-data":{"post":{"tags":["tools"],"summary":"Sign Data","description":"Sign data with RSA private key (RSASSA-PKCS1-v1_5 + SHA-256)","operationId":"tools-sign_data","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"data","in":"query","required":false,"schema":{"type":"string","default":"","title":"Data"}},{"name":"x-chaldea-uuid","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Uuid"}},{"name":"x-chaldea-ver","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Ver"}},{"name":"x-chaldea-build","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Build"}},{"name":"x-chaldea-lang","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Lang"}},{"name":"x-chaldea-platform","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Platform"}},{"name":"x-chaldea-client","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Chaldea-Client"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Tools-Sign Data"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tools/app-versions":{"get":{"tags":["tools"],"summary":"Get App Versions","description":"Get app version info","operationId":"tools-get_app_versions","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Tools-Get App Versions"}}}}}}},"/api/v1/tools/bili-fgo-servers":{"get":{"tags":["tools"],"summary":"Get Bili Fgo Servers","description":"Get Bili FGO server addresses with Redis cache (1 day TTL)","operationId":"tools-get_bili_fgo_servers","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Tools-Get Bili Fgo Servers"}}}}}}},"/api/v1/utils/test-email/":{"post":{"tags":["utils"],"summary":"Test Email","description":"Test email sending (admin only)","operationId":"utils-test_email","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"email_to","in":"query","required":true,"schema":{"type":"string","format":"email","title":"Email To"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/utils/health-check/":{"get":{"tags":["utils"],"summary":"Health Check","operationId":"utils-health_check","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"boolean"},"type":"object","title":"Response Utils-Health Check"}}}}}}},"/api/v1/recognition/screenshot":{"post":{"tags":["recognition"],"summary":"Recognize Screenshot","description":"Recognize servant avatars and skill levels from game screenshots.\n\nArgs:\n    files: 1-10 screenshot images (PNG/JPG).\n    mode: Recognition mode (speed/balanced/accuracy).\n    region: Game region (jp/na/cn/tw/kr).\n\nReturns:\n    Recognition results for each screenshot.","operationId":"recognition-recognize_screenshot","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_recognition-recognize_screenshot"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecognitionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/recognition/status":{"get":{"tags":["recognition"],"summary":"Get Recognition Status","description":"Get recognition service status.","operationId":"recognition-get_recognition_status","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecognitionStatus"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/v1/feedback/":{"post":{"tags":["feedback"],"summary":"Send Feedback V1","description":"Feedback endpoint (v1 API).\n\nReturns bilingual Message response.","operationId":"feedback-send_feedback_v1","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_feedback-send_feedback_v1"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/feedback":{"post":{"tags":["feedback"],"summary":"Send Feedback Legacy","description":"Legacy feedback endpoint (old chaldea-server compatible).\n\nReturns WrapResponse { success, message, body, error }.","operationId":"feedback-send_feedback_legacy","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_feedback-send_feedback_legacy"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WrapResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ping":{"get":{"tags":["health"],"summary":"Ping","description":"Liveness probe: returns 200 if the process is alive","operationId":"health-ping","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/network/ping":{"get":{"tags":["health"],"summary":"Ping Legacy","operationId":"health-ping_legacy","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health":{"get":{"tags":["health"],"summary":"Health Check","description":"Health check: verify PostgreSQL and Redis connections","operationId":"health-health_check","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AdminRecoverRequest":{"properties":{"email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Email"},"password":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Password"}},"type":"object","title":"AdminRecoverRequest","description":"Admin manual account recovery request (Method 3 backend).\n\nAt least one of `email` or `password` must be provided:\n- `email` mode (preferred): validate email, check not taken, send reset\n  link with `bind_email` claim. User clicks link to set password + bind.\n- `password` mode (rare admin operation): directly set password, revoke\n  all sessions, increment token_version. No email is bound.\n- If both are provided, email mode takes precedence."},"AdminRecoverResponse":{"properties":{"message":{"type":"string","title":"Message"},"message_zh":{"type":"string","title":"Message Zh"},"action":{"type":"string","enum":["reset_email_sent","password_directly_reset"],"title":"Action"},"user_id":{"type":"integer","title":"User Id"},"username":{"type":"string","title":"Username"},"email_provided":{"type":"boolean","title":"Email Provided"},"email_bound":{"type":"boolean","title":"Email Bound"},"details":{"type":"string","title":"Details"},"details_zh":{"type":"string","title":"Details Zh"},"reminders":{"items":{"type":"string"},"type":"array","title":"Reminders"},"reminders_zh":{"items":{"type":"string"},"type":"array","title":"Reminders Zh"}},"type":"object","required":["message","message_zh","action","user_id","username","email_provided","email_bound","details","details_zh","reminders","reminders_zh"],"title":"AdminRecoverResponse","description":"Admin manual account recovery response.\n\nRich response with action taken, user info, and reminders for the admin.\nAll fields are bilingual where applicable so the admin UI can render\nthe appropriate language."},"AdminUserCreate":{"properties":{"name":{"type":"string","title":"Name"},"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","title":"Password"},"role":{"type":"integer","title":"Role","default":1}},"type":"object","required":["name","email","password"],"title":"AdminUserCreate","description":"Admin create user request"},"AdminUserDetail":{"properties":{"user":{"$ref":"#/components/schemas/UserInfo"},"sessions":{"items":{"$ref":"#/components/schemas/AdminUserSession"},"type":"array","title":"Sessions","default":[]},"logins":{"items":{"$ref":"#/components/schemas/AdminUserLogin"},"type":"array","title":"Logins","default":[]},"backups_count":{"type":"integer","title":"Backups Count","default":0},"teams_count":{"type":"integer","title":"Teams Count","default":0}},"type":"object","required":["user"],"title":"AdminUserDetail","description":"Admin view of user detail with sessions and logins"},"AdminUserLogin":{"properties":{"device":{"type":"string","title":"Device"},"created_at":{"type":"integer","title":"Created At"},"updated_at":{"type":"integer","title":"Updated At"}},"type":"object","required":["device","created_at","updated_at"],"title":"AdminUserLogin","description":"Admin view of user login record"},"AdminUserSession":{"properties":{"device":{"type":"string","title":"Device"},"session_id":{"type":"string","title":"Session Id"}},"type":"object","required":["device","session_id"],"title":"AdminUserSession","description":"Admin view of user session"},"AdminUserUpdate":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Email"},"password":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Password"},"role":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Role"}},"type":"object","title":"AdminUserUpdate","description":"Admin update user request"},"AdminUsersList":{"properties":{"offset":{"type":"integer","title":"Offset"},"limit":{"type":"integer","title":"Limit"},"total":{"type":"integer","title":"Total"},"data":{"items":{"$ref":"#/components/schemas/UserInfo"},"type":"array","title":"Data","default":[]}},"type":"object","required":["offset","limit","total"],"title":"AdminUsersList","description":"Admin paginated list of users"},"AvatarCandidate":{"properties":{"svtId":{"type":"integer","title":"Svtid"},"limitCount":{"type":"integer","title":"Limitcount"},"score":{"type":"number","title":"Score"}},"type":"object","required":["svtId","limitCount","score"],"title":"AvatarCandidate","description":"Avatar match candidate."},"BackupCreate":{"properties":{"content":{"type":"string","title":"Content"},"os":{"type":"string","title":"Os","default":""},"app_ver":{"type":"string","title":"App Ver","default":""}},"type":"object","required":["content"],"title":"BackupCreate","description":"Create backup request"},"BackupCreateResult":{"properties":{"id":{"type":"integer","title":"Id"},"created_at":{"type":"integer","title":"Created At"}},"type":"object","required":["id","created_at"],"title":"BackupCreateResult"},"Body_auth-login_for_swagger":{"properties":{"grant_type":{"anyOf":[{"type":"string","pattern":"^password$"},{"type":"null"}],"title":"Grant Type"},"username":{"type":"string","title":"Username"},"password":{"type":"string","format":"password","title":"Password"},"scope":{"type":"string","title":"Scope","default":""},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"client_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"format":"password","title":"Client Secret"}},"type":"object","required":["username","password"],"title":"Body_auth-login_for_swagger"},"Body_feedback-send_feedback_legacy":{"properties":{"html":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Html"},"text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text"},"sender":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sender"},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject"},"files":{"items":{"type":"string","contentMediaType":"application/octet-stream"},"type":"array","title":"Files","default":[]}},"type":"object","title":"Body_feedback-send_feedback_legacy"},"Body_feedback-send_feedback_v1":{"properties":{"html":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Html"},"text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text"},"sender":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sender"},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject"},"files":{"items":{"type":"string","contentMediaType":"application/octet-stream"},"type":"array","title":"Files","default":[]}},"type":"object","title":"Body_feedback-send_feedback_v1"},"Body_recognition-recognize_screenshot":{"properties":{"files":{"items":{"type":"string","contentMediaType":"application/octet-stream"},"type":"array","title":"Files"},"mode":{"type":"string","title":"Mode","default":"balanced"},"region":{"type":"string","title":"Region","default":"jp"}},"type":"object","required":["files"],"title":"Body_recognition-recognize_screenshot"},"CellResult":{"properties":{"avatarCrop":{"type":"string","title":"Avatarcrop"},"candidates":{"items":{"$ref":"#/components/schemas/AvatarCandidate"},"type":"array","title":"Candidates","default":[]},"skills":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Skills"},"skillsCount":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Skillscount"},"complete":{"type":"boolean","title":"Complete","default":true},"classId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Classid"},"locked":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Locked"},"maxEnhanced":{"type":"boolean","title":"Maxenhanced","default":false},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["avatarCrop"],"title":"CellResult","description":"Recognition result for a single servant cell."},"ChangeEmailRequest":{"properties":{"current_password":{"type":"string","title":"Current Password"},"new_email":{"type":"string","format":"email","title":"New Email"}},"type":"object","required":["current_password","new_email"],"title":"ChangeEmailRequest","description":"Request to change email (sends verification code).\n\nRequires `current_password` to re-confirm the user's identity before\ninitiating the change — email is the root-trust anchor of the account\n(used for forgot-password and security alerts). See the\n`secure-email-change` OpenSpec change for the threat model."},"DeleteAccountRequest":{"properties":{"password":{"type":"string","title":"Password"}},"type":"object","required":["password"],"title":"DeleteAccountRequest","description":"Request body for account deletion — password in JSON body, not query"},"ForgotPasswordRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"}},"type":"object","required":["email"],"title":"ForgotPasswordRequest","description":"Forgot password request"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"LoginRequest":{"properties":{"username":{"type":"string","title":"Username"},"password":{"type":"string","title":"Password"}},"type":"object","required":["username","password"],"title":"LoginRequest","description":"Login request"},"LoginResponse":{"properties":{"access_token":{"type":"string","title":"Access Token"},"token_type":{"type":"string","title":"Token Type","default":"bearer"},"user_info":{"$ref":"#/components/schemas/UserInfo"}},"type":"object","required":["access_token","user_info"],"title":"LoginResponse","description":"Login response with full user info."},"Message":{"properties":{"message":{"type":"string","title":"Message"},"message_zh":{"type":"string","title":"Message Zh"}},"type":"object","required":["message","message_zh"],"title":"Message","description":"Bilingual message response"},"MigrateTokenRequest":{"properties":{"secret":{"type":"string","title":"Secret"}},"type":"object","required":["secret"],"title":"MigrateTokenRequest","description":"Legacy Worker secret → JWT migration request.\n\nThe `secret` field is the legacy Worker `user_sessions.session_id` (a 36-char\nuuid) that the old Flutter client stored as `ChaldeaUser.secret`."},"PasswordChange":{"properties":{"current_password":{"type":"string","title":"Current Password"},"new_password":{"type":"string","title":"New Password"}},"type":"object","required":["current_password","new_password"],"title":"PasswordChange","description":"Change password"},"RecognitionResponse":{"properties":{"results":{"items":{"$ref":"#/components/schemas/ScreenshotResult"},"type":"array","title":"Results","default":[]}},"type":"object","title":"RecognitionResponse","description":"Response for POST /recognition/screenshot."},"RecognitionStatus":{"properties":{"available":{"type":"boolean","title":"Available"},"availableModes":{"items":{"type":"string"},"type":"array","title":"Availablemodes","default":["speed","balanced"]},"phashDbVersion":{"type":"string","title":"Phashdbversion","default":""},"digitCnnVersion":{"type":"string","title":"Digitcnnversion","default":""},"currentConcurrency":{"type":"integer","title":"Currentconcurrency","default":0},"concurrencyLimit":{"type":"integer","title":"Concurrencylimit","default":2}},"type":"object","required":["available"],"title":"RecognitionStatus","description":"Response for GET /recognition/status."},"RecoverByDeviceRequest":{"properties":{"username":{"type":"string","title":"Username"},"device_id":{"type":"string","title":"Device Id"},"new_email":{"type":"string","format":"email","title":"New Email"}},"type":"object","required":["username","device_id","new_email"],"title":"RecoverByDeviceRequest","description":"Recover account via username + latest device_id + new email.\n\nIntended for migrated users whose `users.email` is NULL. On success, a reset\nJWT with a `bind_email` claim is sent to `new_email`; clicking the link resets\nthe password and binds the email in one step."},"RegisterRequest":{"properties":{"username":{"type":"string","title":"Username"},"password":{"type":"string","title":"Password"},"email":{"type":"string","format":"email","title":"Email"}},"type":"object","required":["username","password","email"],"title":"RegisterRequest","description":"Registration request"},"ResetPasswordByDeviceRequest":{"properties":{"username":{"type":"string","title":"Username"},"code":{"type":"string","title":"Code"},"new_email":{"type":"string","format":"email","title":"New Email"},"new_password":{"type":"string","title":"New Password"}},"type":"object","required":["username","code","new_email","new_password"],"title":"ResetPasswordByDeviceRequest","description":"Reset password request (Method 2: device recovery + verification code)"},"ResetPasswordByTokenRequest":{"properties":{"token":{"type":"string","title":"Token"},"new_password":{"type":"string","title":"New Password"}},"type":"object","required":["token","new_password"],"title":"ResetPasswordByTokenRequest","description":"Reset password request (Method 3: admin-generated JWT token).\n\nUsed when admin initiates a password recovery. The token contains:\n- sub: user_id\n- ver: token_version (must match user's current token_version)\n- bind_email: optional email to bind after password reset"},"ResetPasswordRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"code":{"type":"string","title":"Code"},"new_password":{"type":"string","title":"New Password"}},"type":"object","required":["email","code","new_password"],"title":"ResetPasswordRequest","description":"Reset password request (Method 1: email + verification code)"},"ScreenshotResult":{"properties":{"cells":{"items":{"$ref":"#/components/schemas/CellResult"},"type":"array","title":"Cells","default":[]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","title":"ScreenshotResult","description":"Recognition result for a single screenshot."},"TeamCreate":{"properties":{"ver":{"type":"integer","title":"Ver","default":1},"app_ver":{"type":"string","title":"App Ver","default":""},"quest_id":{"type":"integer","title":"Quest Id"},"phase":{"type":"integer","title":"Phase","default":0},"enemy_hash":{"type":"string","title":"Enemy Hash","default":""},"content":{"type":"string","title":"Content"}},"type":"object","required":["quest_id","content"],"title":"TeamCreate","description":"Create team request"},"TeamCreateResponse":{"properties":{"id":{"type":"integer","title":"Id"},"ver":{"type":"integer","title":"Ver"},"quest_id":{"type":"integer","title":"Quest Id"},"phase":{"type":"integer","title":"Phase"},"enemy_hash":{"type":"string","title":"Enemy Hash"},"created_at":{"type":"integer","title":"Created At"}},"type":"object","required":["id","ver","quest_id","phase","enemy_hash","created_at"],"title":"TeamCreateResponse","description":"Team create response — returns the created team's basic info (no message)."},"TeamItem":{"properties":{"id":{"type":"integer","title":"Id"},"ver":{"type":"integer","title":"Ver"},"app_ver":{"type":"string","title":"App Ver"},"user_id":{"type":"integer","title":"User Id"},"username":{"type":"string","title":"Username"},"quest_id":{"type":"integer","title":"Quest Id"},"phase":{"type":"integer","title":"Phase"},"enemy_hash":{"type":"string","title":"Enemy Hash"},"created_at":{"type":"integer","title":"Created At"},"up":{"type":"integer","title":"Up","default":0},"down":{"type":"integer","title":"Down","default":0},"mine":{"type":"integer","title":"Mine","default":0},"data":{"type":"string","title":"Data","default":""},"decoded_data":{"anyOf":[{},{"type":"null"}],"title":"Decoded Data"}},"type":"object","required":["id","ver","app_ver","user_id","username","quest_id","phase","enemy_hash","created_at"],"title":"TeamItem","description":"Team item (used for both list and detail)"},"TeamSearchResult":{"properties":{"offset":{"type":"integer","title":"Offset"},"limit":{"type":"integer","title":"Limit"},"total":{"type":"integer","title":"Total"},"data":{"items":{"$ref":"#/components/schemas/TeamItem"},"type":"array","title":"Data"}},"type":"object","required":["offset","limit","total","data"],"title":"TeamSearchResult","description":"Team search result (paginated)"},"TeamSortType":{"type":"string","enum":["id","created","upvotes","downvotes"],"title":"TeamSortType"},"UserBackup":{"properties":{"id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Id"},"user_id":{"type":"integer","title":"User Id"},"app_ver":{"type":"string","title":"App Ver","default":""},"os":{"type":"string","title":"Os","default":""},"created_at":{"type":"integer","title":"Created At"},"content":{"type":"string","title":"Content"}},"type":"object","required":["user_id","created_at","content"],"title":"UserBackup","description":"User backup table"},"UserInfo":{"properties":{"id":{"type":"integer","title":"Id"},"name":{"type":"string","title":"Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"role":{"type":"integer","title":"Role"},"created_at":{"type":"integer","title":"Created At"}},"type":"object","required":["id","name","role","created_at"],"title":"UserInfo","description":"User info visible to the user themselves and admins."},"UserUpdate":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"}},"type":"object","title":"UserUpdate","description":"Update user info"},"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"},"VerifyEmailRequest":{"properties":{"new_email":{"type":"string","format":"email","title":"New Email"},"code":{"type":"string","title":"Code"}},"type":"object","required":["new_email","code"],"title":"VerifyEmailRequest","description":"Verify email change with code"},"VerifyRegisterRequest":{"properties":{"username":{"type":"string","title":"Username"},"password":{"type":"string","title":"Password"},"email":{"type":"string","format":"email","title":"Email"},"code":{"type":"string","title":"Code"}},"type":"object","required":["username","password","email","code"],"title":"VerifyRegisterRequest","description":"Verify registration with email code"},"VoteRequest":{"properties":{"value":{"type":"integer","title":"Value"}},"type":"object","required":["value"],"title":"VoteRequest","description":"Vote request"},"VoteResult":{"properties":{"up":{"type":"integer","title":"Up"},"down":{"type":"integer","title":"Down"},"mine":{"type":"integer","title":"Mine"}},"type":"object","required":["up","down","mine"],"title":"VoteResult","description":"Vote result"},"WrapResponse":{"properties":{"success":{"type":"boolean","title":"Success","default":true},"message":{"type":"string","title":"Message","default":""},"body":{"title":"Body"},"error":{"title":"Error"}},"type":"object","title":"WrapResponse","description":"Response format matching the old chaldea-server for backward compatibility."}},"securitySchemes":{"OAuth2PasswordBearer":{"type":"oauth2","flows":{"password":{"scopes":{},"tokenUrl":"/api/v1/auth/token"}}}}}}