{"openapi":"3.1.0","info":{"title":"cashless-auth","description":"Authentication, token issuance, and permission management for the Cashless Media platform.","version":"0.1.0"},"paths":{"/auth/login":{"post":{"tags":["auth"],"summary":"Login","description":"Authenticate an admin, operator, or network manager via email+password.\n\nRate-limited to 10 requests per minute per source IP. The admin role\nis the most security-sensitive lane and we apply its limit\nuniformly here rather than dispatching by role — operators and\nnetwork managers also benefit from the same brute-force ceiling.","operationId":"login_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/login/phone":{"post":{"tags":["auth"],"summary":"Login Phone","description":"Request a phone OTP for consumer login.\n\nRate-limited to 30/min/IP. Higher than /auth/login because the\nconsumer flow is the legitimate high-volume lane (mobile users\nmoving between cells) and the OTP itself is single-use.\n\nReturns confirmation that the OTP was generated. In production, the OTP\nwould be sent via an SMS provider integration.","operationId":"login_phone_auth_login_phone_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneLoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Login Phone Auth Login Phone Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/login/phone/verify":{"post":{"tags":["auth"],"summary":"Login Phone Verify","description":"Verify a phone OTP and return tokens for the consumer.\n\nRate-limited to 30/min/IP. The OTP is single-use so brute-force\nis bounded to ~6 codes per minute against a 6-digit search space,\nbut the explicit cap closes the obvious enumeration attack and\nstops a flood of bogus codes from chewing through Redis quota.","operationId":"login_phone_verify_auth_login_phone_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyOTPRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/login/firebase":{"post":{"tags":["auth"],"summary":"Login Firebase","description":"Verify a Firebase ID token and return platform tokens for the consumer.\n\nRate-limited to 30/min/IP. Firebase ID tokens are minted by the\nmobile client; an attacker holding a stolen ID token can mint\nplatform tokens here. The cap bounds that exfiltration if the\ntoken has not yet been revoked upstream.","operationId":"login_firebase_auth_login_firebase_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FirebaseLoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/login/device":{"post":{"tags":["auth"],"summary":"Login Device","description":"Authenticate a device and issue an offline token.\n\nRequires an operator to be already associated with the device.\n\nRate-limited to 60/min/IP. POS devices typically reconnect a\nhandful of times per shift, so 60/min is far above the\nlegitimate-use ceiling but still tight enough to cap MAC-address\nenumeration attempts (an attacker scanning the MAC space to\nfingerprint provisioned devices).","operationId":"login_device_auth_login_device_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceAuthRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Login Device Auth Login Device Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/refresh":{"post":{"tags":["auth"],"summary":"Refresh","description":"Refresh an access token using a valid refresh token.\n\nRate-limited to 30/min/IP. A legitimate client only refreshes\nonce per access-token lifetime (15 min by default), so 30/min\nleaves 60x headroom while still capping refresh-token brute-force\n(e.g. an attacker holding a leaked log line that contains a\ntruncated JWT and trying to fuzz the suffix). slowapi requires\n``request: Request`` as the first parameter; reorder accordingly.","operationId":"refresh_auth_refresh_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/logout":{"post":{"tags":["auth"],"summary":"Logout","description":"Revoke a refresh token, effectively logging the user out.\n\nRate-limited to 30/min/IP. Logout is bearer-token-protected so\nabuse vectors are narrow, but the cap also bounds an attacker\nholding a stolen refresh-token from triggering Redis writes for\nevery token they want to revocation-test.","operationId":"logout_auth_logout_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Logout Auth Logout Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/admin/mfa/setup":{"post":{"tags":["admin"],"summary":"Mfa Setup","description":"Set up TOTP MFA for the current admin.\n\nGenerates a new secret and returns the secret + QR code URI.\nThe admin must verify the code via /admin/mfa/verify before MFA is activated.\n\nRate-limited to 30/min/IP. Bounds secret-rotation abuse if an\nadmin token is compromised — each call generates a fresh secret\nthat supersedes the prior one.","operationId":"mfa_setup_admin_mfa_setup_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MFASetupResponse"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/admin/mfa/verify":{"post":{"tags":["admin"],"summary":"Mfa Verify","description":"Verify an MFA code, activate TOTP for the admin, and reissue a\npair of access/refresh tokens carrying ``mfa_verified_at = now()``.\n\nMust be called after /admin/mfa/setup with a valid TOTP code from\nthe authenticator app. The reissued token unlocks routes guarded by\n``require_admin_mfa_fresh`` for the duration of the freshness\nwindow (default 8 hours).\n\nRate-limited to 10/min/IP. TOTP search space is 10^6 (six digits),\nso 10/min caps brute-force at ~14 400 attempts/day vs. an\nexpected sweep of ~1M codes — well below the legitimate-use\ncadence (an admin enters their code at most a couple of times).","operationId":"mfa_verify_admin_mfa_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MFAVerifyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/admin/sensitive/ping":{"get":{"tags":["admin"],"summary":"Sensitive Ping","description":"Smoke endpoint demonstrating ``require_admin_mfa_fresh``.\n\nReturns 200 only when the caller is an admin AND\n``mfa_verified_at`` is within the freshness window. Used by the\nadmin UI as a probe before invoking destructive operations\n(refunds, payouts, role grants); the production destructive\nroutes will replace this guard once they migrate to the new\ndependency.","operationId":"sensitive_ping_admin_sensitive_ping_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Sensitive Ping Admin Sensitive Ping Get"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/admin/me":{"get":{"tags":["admin"],"summary":"Get Me","description":"Get the current admin's profile information.\n\nRate-limited to 60/min/IP. Profile reads are admin-gated; the\ncap is generous enough for a UI that polls profile state but\nbounds enumeration of admin claims via a stolen token.","operationId":"get_me_admin_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Me Admin Me Get"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/admin/password-reset/email-send":{"post":{"tags":["admin","password-reset"],"summary":"Email Send","description":"Generate a reset token and email the link to the matching admin.\n\nAlways returns ``200 OK`` to prevent account enumeration, even when\nthe email is unknown or the mail provider is unreachable. Returns\n``429`` only when a known admin has exceeded the per-hour reset\nquota — at that point existence has already been confirmed (the\nuser/attacker triggered the throttle on their own account) so\nindicating the rate-limit does not leak new information.","operationId":"email_send_admin_password_reset_email_send_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetRequestBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Email Send Admin Password Reset Email Send Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/admin/password-reset/{token}":{"post":{"tags":["admin","password-reset"],"summary":"Confirm","description":"Validate a reset token and rotate the admin's password.\n\nReturns ``200 OK`` on success. Returns ``410 Gone`` for any\ninvalid token state (unknown, expired, already used, or admin\ndeleted) so the client cannot distinguish failure modes — all of\nthem require restarting the flow from ``/email-send``.\n\nRate-limited to 30/min/IP. The reset-token search space is the\nfull ``secrets.token_urlsafe(32)`` keyspace so brute-force is\nnot realistic, but the cap bounds an attacker who scraped a\ntoken from a leaked email log from chaining bogus password\nsubmissions — and protects the per-row ``used_at`` write budget.","operationId":"confirm_admin_password_reset__token__post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetConfirmBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Confirm Admin Password Reset  Token  Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tokens/offline":{"post":{"tags":["tokens"],"summary":"Issue Offline Token","description":"Issue an offline token for POS device use at a specific event.\n\nOffline tokens have a longer expiry and include the event_id for\nevent-scoped operations when the device is disconnected.\n\nRate-limited to 30/min/IP. Offline tokens are 72-hour-lived and\nbound to (event, device); a flood of issuance requests would\nexhaust the JWT signing budget. The cap is well above legitimate\nuse (operators rotate at most a few tokens per shift).","operationId":"issue_offline_token_tokens_offline_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfflineTokenRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfflineTokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/tokens/api-key":{"post":{"tags":["tokens"],"summary":"Create Api Key Endpoint","description":"Create a new API key for an event client.\n\nOnly admins can create API keys. The plaintext key is returned only once.\n\nRate-limited to 10/min/IP. API-key creation is high-value (the\nplaintext is irrecoverable), already gated by admin role, and a\nlegitimate workflow rarely creates more than a handful per day —\n10/min is plenty of headroom while bounding mass-creation abuse\nif an admin token is compromised. Bucket is per-IP rather than\nper-admin to keep the dependency on slowapi's default key_func;\nthe admin-role guard upstream prevents non-admins from triggering\nthe bucket at all.","operationId":"create_api_key_endpoint_tokens_api_key_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIKeyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/tokens/api-key/{key_id}":{"delete":{"tags":["tokens"],"summary":"Revoke Api Key Endpoint","description":"Revoke an API key by clearing the EventClient's token.\n\nRate-limited to 30/min/IP. Revocation is admin-gated; the cap\nbounds mass-revocation as a denial-of-service vector.","operationId":"revoke_api_key_endpoint_tokens_api_key__key_id__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"integer","title":"Key Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Revoke Api Key Endpoint Tokens Api Key  Key Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tokens/verify":{"post":{"tags":["tokens"],"summary":"Verify Token Endpoint","description":"Verify any token and return its claims.\n\nThis endpoint is used by other services to validate tokens without\nneeding the JWT secret directly.\n\nRate-limited to 600/minute/IP. Service-to-service callers (cm_v2,\npa_v4) use this on every inbound request to validate JWTs — the\ncap is intentionally generous because the legitimate volume is\nhigh-fanout. Public attackers fuzzing tokens still hit the cap\nquickly (10/sec is well below brute-force-useful throughput).\nThe audit doc suggests 60/min/IP — that would break the\nservice-to-service hot path; we deliberately deviate upward.","operationId":"verify_token_endpoint_tokens_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenVerifyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenVerifyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/devices/auth":{"post":{"tags":["devices"],"summary":"Authenticate Device","description":"Authenticate a device by its MAC address and event_id.\n\nReturns device info if found and not archived.\n\nRate-limited to 60/min/IP. Mirrors the ``/auth/login/device`` cap\nfor the same reason — POS reconnect cadence is well below 60/min\nbut MAC-address enumeration must stay capped.","operationId":"authenticate_device_devices_auth_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceAuthRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Authenticate Device Devices Auth Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/devices/{device_id}/token":{"post":{"tags":["devices"],"summary":"Issue Device Token Endpoint","description":"Issue an offline token for a specific device.\n\nRequires providing the operator_id who will be associated with this\ndevice session. The requesting user must be an admin or operator\nwith access to the device's event.\n\nRate-limited to 30/min/IP. Token issuance is JWT-signing-bound;\nthe cap matches ``/tokens/offline``.","operationId":"issue_device_token_endpoint_devices__device_id__token_post","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"device_id","in":"path","required":true,"schema":{"type":"integer","title":"Device Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceTokenRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Issue Device Token Endpoint Devices  Device Id  Token Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/devices/operator-token":{"post":{"tags":["devices"],"summary":"Issue Operator Session Token","description":"Port of legacy ``POST /api/auth/user/token`` (mPOS / Fastpass /\nDispatch / Report / Kids device login).\n\nReplaces the legacy fat ``MposLoginData`` envelope with a thin\n`(user_id, event_id, email, token, mode, dev_type)` reply. The\non-device caller bootstraps the rest of the operator-session\nstate via ``cm_v2 POST /v2/devices`` (DeviceApi.upload_device)\nusing the returned token. This separates **identity** (cashless-auth's\nconcern) from **device-session bootstrap state** (cm_v2's\nconcern), matching the modern architecture's bounded contexts.\n\nRole-check matrix preserved verbatim from legacy:\n\n  POS      → mode not in {3, 4, 5, 6}\n  REPORT   → mode == 3\n  FASTPASS → mode == 4\n  KIDS     → mode == 5\n  DISPATCH → mode == 6\n\nA rejected dev_type/mode combo returns 401 (not 200 with empty\nbody, as legacy did) so on-device callers can distinguish\nauth failures from happy-path empty results.\n\nGenerates and persists a fresh session token if the user row\nhas none; otherwise reuses the existing token (matches legacy\nrotation policy — token is rotated only on logout).","operationId":"issue_operator_session_token_devices_operator_token_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorLoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorLoginResponse"}}}},"401":{"description":"Invalid email/password OR dev_type/mode mismatch"},"422":{"description":"Validation error (unknown dev_type, missing fields)"}}}},"/health":{"get":{"summary":"Health","description":"Liveness probe.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Health Health Get"}}}}}}}},"components":{"schemas":{"APIKeyRequest":{"properties":{"event_id":{"type":"integer","title":"Event Id"},"name":{"type":"string","maxLength":100,"minLength":1,"title":"Name"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"}},"additionalProperties":false,"type":"object","required":["event_id","name","scopes"],"title":"APIKeyRequest"},"APIKeyResponse":{"properties":{"api_key":{"type":"string","title":"Api Key"},"name":{"type":"string","title":"Name"},"event_id":{"type":"integer","title":"Event Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["api_key","name","event_id","created_at"],"title":"APIKeyResponse"},"DeviceAuthRequest":{"properties":{"dev_mac":{"type":"string","title":"Dev Mac"},"event_id":{"type":"integer","title":"Event Id"}},"additionalProperties":false,"type":"object","required":["dev_mac","event_id"],"title":"DeviceAuthRequest"},"DeviceTokenRequest":{"properties":{"operator_id":{"type":"integer","title":"Operator Id"}},"additionalProperties":false,"type":"object","required":["operator_id"],"title":"DeviceTokenRequest"},"FirebaseLoginRequest":{"properties":{"id_token":{"type":"string","title":"Id Token"}},"additionalProperties":false,"type":"object","required":["id_token"],"title":"FirebaseLoginRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"LoginRequest":{"properties":{"email":{"type":"string","title":"Email"},"password":{"type":"string","title":"Password"},"event_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Event Id"},"role":{"type":"string","title":"Role","default":"admin"}},"additionalProperties":false,"type":"object","required":["email","password"],"title":"LoginRequest"},"MFASetupResponse":{"properties":{"secret":{"type":"string","title":"Secret"},"qr_uri":{"type":"string","title":"Qr Uri"}},"type":"object","required":["secret","qr_uri"],"title":"MFASetupResponse"},"MFAVerifyRequest":{"properties":{"code":{"type":"string","maxLength":6,"minLength":6,"title":"Code"}},"additionalProperties":false,"type":"object","required":["code"],"title":"MFAVerifyRequest"},"OfflineTokenRequest":{"properties":{"event_id":{"type":"integer","title":"Event Id"},"device_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Device Id"}},"additionalProperties":false,"type":"object","required":["event_id"],"title":"OfflineTokenRequest"},"OfflineTokenResponse":{"properties":{"offline_token":{"type":"string","title":"Offline Token"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"event_id":{"type":"integer","title":"Event Id"}},"type":"object","required":["offline_token","expires_at","event_id"],"title":"OfflineTokenResponse"},"OperatorLoginRequest":{"properties":{"email":{"type":"string","maxLength":254,"minLength":3,"title":"Email"},"password":{"type":"string","maxLength":256,"minLength":1,"title":"Password"},"dev_type":{"type":"string","pattern":"^(POS|REPORT|FASTPASS|KIDS|DISPATCH)$","title":"Dev Type"},"dev_mac":{"type":"string","maxLength":64,"minLength":1,"title":"Dev Mac"}},"additionalProperties":false,"type":"object","required":["email","password","dev_type","dev_mac"],"title":"OperatorLoginRequest","description":"Body for ``POST /devices/operator-token``.\n\nMirrors the legacy ``cm/api/endpoints/auth.py:post_auth_user_token_from_mobile``\ncontract: ``(email, password, dev_type, dev_mac)``. ``event_id`` is\nNOT required — the User row's own ``event_id`` resolves the\nsession binding, matching the legacy implicit shape."},"OperatorLoginResponse":{"properties":{"user_id":{"type":"integer","title":"User Id"},"event_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Event Id"},"email":{"type":"string","title":"Email"},"token":{"type":"string","title":"Token"},"mode":{"type":"integer","title":"Mode"},"dev_type":{"type":"string","title":"Dev Type"}},"type":"object","required":["user_id","email","token","mode","dev_type"],"title":"OperatorLoginResponse","description":"Thin reply for the operator-token route. The fat\n``MposLoginData`` envelope (event_conf, marketplace_conf, ws_id,\nproduct_count, area_name, …) is intentionally split off — callers\nbootstrap that via ``cm_v2 POST /v2/devices`` (DeviceApi.upload_device)\nafter authenticating, separating the concerns of identity vs\ndevice-session-state."},"PasswordResetConfirmBody":{"properties":{"new_password":{"type":"string","maxLength":128,"minLength":8,"title":"New Password"}},"additionalProperties":false,"type":"object","required":["new_password"],"title":"PasswordResetConfirmBody"},"PasswordResetRequestBody":{"properties":{"email":{"type":"string","maxLength":254,"minLength":3,"pattern":"^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$","title":"Email"}},"additionalProperties":false,"type":"object","required":["email"],"title":"PasswordResetRequestBody"},"PhoneLoginRequest":{"properties":{"phone":{"type":"string","title":"Phone"}},"additionalProperties":false,"type":"object","required":["phone"],"title":"PhoneLoginRequest"},"RefreshRequest":{"properties":{"refresh_token":{"type":"string","title":"Refresh Token"}},"additionalProperties":false,"type":"object","required":["refresh_token"],"title":"RefreshRequest"},"TokenResponse":{"properties":{"access_token":{"type":"string","title":"Access Token"},"refresh_token":{"type":"string","title":"Refresh Token"},"token_type":{"type":"string","title":"Token Type","default":"bearer"},"expires_in":{"type":"integer","title":"Expires In"}},"type":"object","required":["access_token","refresh_token","expires_in"],"title":"TokenResponse"},"TokenVerifyRequest":{"properties":{"token":{"type":"string","title":"Token"}},"additionalProperties":false,"type":"object","required":["token"],"title":"TokenVerifyRequest"},"TokenVerifyResponse":{"properties":{"valid":{"type":"boolean","title":"Valid"},"claims":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Claims"}},"type":"object","required":["valid"],"title":"TokenVerifyResponse"},"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"},"VerifyOTPRequest":{"properties":{"phone":{"type":"string","title":"Phone"},"code":{"type":"string","maxLength":6,"minLength":6,"title":"Code"}},"additionalProperties":false,"type":"object","required":["phone","code"],"title":"VerifyOTPRequest"}},"securitySchemes":{"OAuth2PasswordBearer":{"type":"oauth2","flows":{"password":{"scopes":{},"tokenUrl":"/auth/login"}}}}}}