# Nova Mobile App API — shared components. components: responses: Failure: description: Invalid request, validation failure, auth error, or internal server error. content: application/json: schema: $ref: "#/components/schemas/ApiError" example: code: 180215 message: "Invalid request body" details: - source: "phone_number" issue: "required" Unauthorized: description: Unauthorized or invalid credentials. content: application/json: schema: $ref: "#/components/schemas/ApiError" example: code: 180206 message: "Incorrect phone number or password" details: [] NotFound: description: Session, OTP window, or device was not found. content: application/json: schema: $ref: "#/components/schemas/ApiError" example: code: 180234 message: "Session not found or expired" details: - source: "session_id" issue: "not found or expired" Locked: description: Account is temporarily locked by retry policy. content: application/json: schema: $ref: "#/components/schemas/ApiError" example: code: 180208 message: "Account is locked. Please try again in 15 minutes." details: - source: "account" issue: "locked" retry_after_seconds: 900 TooManyRequests: description: Rate limit, cooldown, or resend limit was exceeded. content: application/json: schema: $ref: "#/components/schemas/ApiError" example: code: 180236 message: "Please wait before requesting a new OTP" details: - source: "otp" issue: "cooldown active" retry_after_seconds: 60 Gone: description: OTP or pending authentication step expired. content: application/json: schema: $ref: "#/components/schemas/ApiError" example: code: 180238 message: "OTP session has expired. Please log in again." details: - source: "session" issue: "expired" schemas: ApiError: type: object required: [code, message] description: | Error envelope returned by `awsBase/apiMiddleware.go`. Mobile authentication codes generated by `internal/llmCode/client` with the `lambda/client-mobile` base `180200`: - `180202` — Database query error. - `180203` — Internal/config error, such as missing server JWT key, missing mobile attestation app ID, challenge generation failure, missing toolset, invalid user ID, missing email template, or OTP generation/email failure. - `180206` — Invalid phone/password, invalid authentication proof, or external service failure during login OTP delivery. - `180208` — Account locked by password retry policy. - `180213` — User/client not found. - `180215` — Validation failed, invalid request body, invalid challenge token, platform mismatch, device attestation failure, device attestation required, invalid passcode format, or invalid passcode. - `180221` — User/client deactivated. - `180234` — Session not found. - `180235` — Session is not active. - `180236` — Max OTP resend/attempt limit reached. - `180237` — User temporarily blocked. - `180238` — OTP record not found. - `180239` — Invalid OTP. properties: code: type: integer format: int32 example: 180215 message: type: string example: "Invalid request body" details: type: array items: $ref: "#/components/schemas/ApiErrorDetail" default: [] ApiErrorDetail: type: object properties: source: type: string example: "phone_number" issue: type: string example: "required" retry_after_seconds: type: integer format: int64 example: 60 remaining_attempts: type: integer format: int32 example: 4 additionalProperties: true MobilePlatform: type: string enum: [ios, android] example: "ios" MobileLoginMode: type: string enum: [registration, registered_device] deprecated: true description: Deprecated. Login mode is now inferred from `confirm_jwt.tracking_id`; `mode` is not accepted as a login request field. example: "registration" Ed25519PublicJWK: type: object required: [kty, crv, x] properties: kty: type: string enum: [OKP] example: "OKP" crv: type: string enum: [Ed25519] example: "Ed25519" x: type: string description: Base64url-encoded Ed25519 public key value. example: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" AttestationProof: type: object required: [type] properties: type: type: string enum: [apple_app_attest, android_key_attestation, play_integrity] platform_key_id: type: string description: Required for iOS App Attest. attestation_object: type: string description: Base64-encoded Apple App Attest object. certificate_chain: type: array description: Required when type is `android_key_attestation`. items: type: string play_integrity_token: type: string description: Required when type is `play_integrity` or risk policy requires it. AssertionProof: type: object required: [type] properties: type: type: string enum: [apple_app_attest_assertion, android_key_signature] platform_key_id: type: string description: Required for iOS App Attest assertion. assertion_object: type: string description: Required for iOS App Attest assertion. signature: type: string description: Required for Android platform key signature. GetLoginChallengeRequest: type: object required: [platform, app_version, phone_number] properties: platform: $ref: "#/components/schemas/MobilePlatform" app_version: type: string minLength: 1 example: "1.0.0" phone_number: type: string description: Client phone number used to resolve the password salt included in the challenge JWT. Unknown phones receive a deterministic fake salt to avoid account enumeration. example: "+66812345678" GetLoginChallengeResponse: type: object required: [challenge_token] properties: challenge_token: type: string description: Server-signed EdDSA JWT containing the mobile login challenge. example: "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9..." LoginRequest: oneOf: - $ref: "#/components/schemas/RegistrationLoginRequest" - $ref: "#/components/schemas/RegisteredDeviceLoginRequest" description: | Login mode is inferred from `confirm_jwt.tracking_id`: a non-empty `tracking_id` means new-device registration; an empty or absent `tracking_id` means registered-device login. Phone number and password are not sent as top-level fields; they are carried in the signed `confirm_jwt` as `phone_number` and `password_proof`. LoginRequestBase: type: object required: [platform, challenge_token, confirm_jwt] properties: platform: $ref: "#/components/schemas/MobilePlatform" challenge_token: type: string description: Token from `getChallenge`. confirm_jwt: type: string description: Client-signed EdDSA JWT proving device-key possession and carrying `phone_number`, `password_proof`, `challenge_token_hash`, `platform`, and optional `tracking_id`. play_integrity_token: type: string description: Optional Android Play Integrity token based on risk policy. RegistrationLoginRequest: allOf: - $ref: "#/components/schemas/LoginRequestBase" - type: object required: [jwt_public_key, attestation] properties: jwt_public_key: $ref: "#/components/schemas/Ed25519PublicJWK" attestation: $ref: "#/components/schemas/AttestationProof" example: platform: "ios" challenge_token: "" jwt_public_key: kty: "OKP" crv: "Ed25519" x: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" confirm_jwt: "" attestation: type: "apple_app_attest" platform_key_id: "apple-app-attest-key-id" attestation_object: "base64-attestation-object" RegisteredDeviceLoginRequest: allOf: - $ref: "#/components/schemas/LoginRequestBase" - type: object required: [device_id] properties: device_id: type: string description: Server-generated devices._id identifier. example: "64f1a2b3c4d5e6f7a8b9c011" assertion: $ref: "#/components/schemas/AssertionProof" example: platform: "android" challenge_token: "" device_id: "64f1a2b3c4d5e6f7a8b9c011" confirm_jwt: "" play_integrity_token: "" LoginResponse: type: object required: [session_id, device_id, user_id, otp_sent_to, otp_remaining_time] properties: session_id: type: string description: Pending mobile session Mongo ObjectId hex used in OTP and passcode calls. example: "6650f0a2e1b2c3d4e5f60718" device_id: type: string description: Registered device ID bound to this login flow. example: "64f1a2b3c4d5e6f7a8b9c011" user_id: type: string description: Authenticated client user ID. example: "64f1a2b3c4d5e6f7a8b9c012" tracking_id: type: string description: Registration tracking ID returned only for new-device registration flows. example: "registration-tracking-id" otp_sent_to: type: string description: Masked destination email address. example: "us**@example.com" otp_remaining_time: type: integer format: int64 description: Unix timestamp when the OTP expires. example: 1717998600 SendOTPRequest: type: object properties: email: type: string format: email description: Deprecated. Email is resolved from the Authorization JWT user and the current mobile user record. example: "user@example.com" SendOTPResponse: type: object required: [otp_sent_to, resend_available_in, remaining_resends] properties: otp_sent_to: type: string example: "us**@example.com" resend_available_in: type: integer format: int64 description: Unix timestamp when the next resend is available. example: 1717998600 remaining_resends: type: integer format: int32 description: Remaining resend attempts in this session. example: 4 VerifyOTPRequest: type: object required: [otp] properties: otp: type: string pattern: "^[0-9]{6}$" minLength: 6 maxLength: 6 example: "123456" VerifyOTPResponse: type: object required: [next_step] properties: next_step: type: string enum: [create_passcode, verify_passcode] description: Next step returned after OTP verification. example: "verify_passcode" CreatePasscodeRequest: type: object required: [session_id, passcode, confirm_passcode] properties: session_id: type: string description: Pending mobile session Mongo ObjectId hex issued during login. example: "6650f0a2e1b2c3d4e5f60718" passcode: type: string pattern: "^[0-9]{6}$" minLength: 6 maxLength: 6 description: Six-digit account passcode. example: "213456" confirm_passcode: type: string pattern: "^[0-9]{6}$" minLength: 6 maxLength: 6 description: Must exactly match `passcode`. example: "213456" CreatePasscodeResponse: type: object required: [next_step] properties: next_step: type: string enum: [enable_biometric, verify_biometric] description: Next step returned after creating passcode. example: "enable_biometric" VerifyPasscodeResponse: type: object required: [next_step] properties: next_step: type: string enum: [enable_biometric, verify_biometric] description: Next step returned after verifying passcode. example: "enable_biometric" VerifyPasscodeRequest: type: object required: [session_id, device_id, passcode] properties: session_id: type: string description: Pending mobile session Mongo ObjectId hex issued during login. example: "6650f0a2e1b2c3d4e5f60718" device_id: type: string description: Trusted device ID. example: "64f1a2b3c4d5e6f7a8b9c011" passcode: type: string pattern: "^[0-9]{6}$" minLength: 6 maxLength: 6 description: Six-digit account passcode. example: "213456" EnableBiometricRequest: type: object description: Empty JSON object. Session and device are resolved from the Authorization JWT. additionalProperties: false EnableBiometricResponse: type: object description: Empty JSON object returned after biometric is enabled. additionalProperties: false