{"openapi":"3.1.0","info":{"title":"EasyToPaste API","version":"1.0.0"},"paths":{"/api/upload":{"post":{"tags":["upload"],"summary":"Upload","description":"Upload a file and generate a secret code","operationId":"upload_api_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_api_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/paste":{"post":{"tags":["paste"],"summary":"Paste","description":"Create a text paste and generate a secret code","operationId":"paste_api_paste_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasteRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/access/{code}":{"get":{"tags":["access"],"summary":"Access By Code","description":"Access a secret by code","operationId":"access_by_code_api_access__code__get","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","title":"Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/access/s/{short_code}":{"get":{"tags":["access"],"summary":"Access By Short Code","description":"Access a secret by short URL code","operationId":"access_by_short_code_api_access_s__short_code__get","parameters":[{"name":"short_code","in":"path","required":true,"schema":{"type":"string","title":"Short Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/google":{"get":{"tags":["auth"],"summary":"Google Login","description":"Initiate Google OAuth login","operationId":"google_login_auth_google_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/google/callback":{"get":{"tags":["auth"],"summary":"Google Callback","description":"Handle Google OAuth callback","operationId":"google_callback_auth_google_callback_get","parameters":[{"name":"code","in":"query","required":true,"schema":{"type":"string","title":"Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/facebook":{"get":{"tags":["auth"],"summary":"Facebook Login","description":"Initiate Facebook OAuth login","operationId":"facebook_login_auth_facebook_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/facebook/callback":{"get":{"tags":["auth"],"summary":"Facebook Callback","description":"Handle Facebook OAuth callback","operationId":"facebook_callback_auth_facebook_callback_get","parameters":[{"name":"code","in":"query","required":true,"schema":{"type":"string","title":"Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/user/secrets":{"get":{"tags":["user"],"summary":"List User Secrets","description":"List all non-expired secrets for the current user","operationId":"list_user_secrets_api_user_secrets_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/user/stats":{"get":{"tags":["user"],"summary":"Get User Stats Endpoint","description":"Get user statistics","operationId":"get_user_stats_endpoint_api_user_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/user/secrets/{code}/expire":{"post":{"tags":["user"],"summary":"Expire Secret","description":"Expire/invalidate a user's secret","operationId":"expire_secret_api_user_secrets__code__expire_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","title":"Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/user/profile":{"get":{"tags":["user"],"summary":"Get User Profile","description":"Get user profile information","operationId":"get_user_profile_api_user_profile_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/stats":{"get":{"tags":["admin"],"summary":"Get Global Stats Endpoint","description":"Get global statistics","operationId":"get_global_stats_endpoint_api_admin_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/stats/upload-timeline":{"get":{"tags":["admin"],"summary":"Get Upload Timeline","description":"Get upload timeline data","operationId":"get_upload_timeline_api_admin_stats_upload_timeline_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/stats/download-timeline":{"get":{"tags":["admin"],"summary":"Get Download Timeline","description":"Get download timeline data","operationId":"get_download_timeline_api_admin_stats_download_timeline_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/stats/countries":{"get":{"tags":["admin"],"summary":"Get Country Stats","description":"Get country statistics","operationId":"get_country_stats_api_admin_stats_countries_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/stats/users":{"get":{"tags":["admin"],"summary":"Get Top Users","description":"Get top users statistics","operationId":"get_top_users_api_admin_stats_users_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/stats/realtime":{"get":{"tags":["admin"],"summary":"Get Realtime Stats","description":"Get real-time statistics","operationId":"get_realtime_stats_api_admin_stats_realtime_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/secrets":{"post":{"tags":["secrets-v1"],"summary":"Create Secret","description":"Create a zero-knowledge secret envelope and assign a locator.","operationId":"create_secret_v1_secrets_post","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["secrets-v1"],"summary":"List Secrets","description":"Return a cursor-paginated list of the caller's own secrets (metadata only).","operationId":"list_secrets_v1_secrets_get","parameters":[{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/secrets/uploads":{"post":{"tags":["secrets-v1"],"summary":"Init Upload","description":"Mint presigned S3 PUT URLs so ciphertext uploads directly to S3 (§4.6).\n\nReturns one presigned PUT per chunk.  The client uploads each encrypted\nchunk, then commits with POST /v1/secrets carrying the returned uploadIds.\nCounts as a single create slot (abuse boundary) via the create rate-limit\nclass, but does NOT consume the daily quota — that is charged on commit.\n\nZero-knowledge: the server only issues opaque write URLs; it never sees the\nciphertext the client subsequently PUTs.","operationId":"init_upload_v1_secrets_uploads_post","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/secrets/{locator}":{"get":{"tags":["secrets-v1"],"summary":"Retrieve Secret","description":"Return envelope metadata + ciphertext access URL.\n\nDoes NOT commit burn — the separate POST /burn endpoint does that.\nIf the secret has requiresRecipientLogin=True and the caller is anon,\nreturns 403 recipient_auth_required per task spec.\n\nX2: Also accepts a short-lived recipient bearer token (from /authorize)\nthat satisfies the requires_recipient_login gate.","operationId":"retrieve_secret_v1_secrets__locator__get","parameters":[{"name":"locator","in":"path","required":true,"schema":{"type":"string","title":"Locator"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/secrets/{locator}/chunks":{"get":{"tags":["secrets-v1"],"summary":"Retrieve Secret Chunks","description":"Return the ordered chunk manifest with 5-minute presigned S3 URLs.\n\nBL-4: Integrity gate — if the live chunk count does not match Secret.chunk_count,\nreturns 500 and emits operator.manifest_count_mismatch audit.\n\nAuth + recipient gate: same as GET /v1/secrets/{locator}.","operationId":"retrieve_secret_chunks_v1_secrets__locator__chunks_get","parameters":[{"name":"locator","in":"path","required":true,"schema":{"type":"string","title":"Locator"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/secrets/{locator}/burn":{"post":{"tags":["secrets-v1"],"summary":"Burn Secret","description":"Atomically commit burn-after-read.\n\nExactly one concurrent caller wins (burned=True); all others get 409.\nPer task spec, the separate burn endpoint decouples retrieval from burn\nso the client can confirm ciphertext was decrypted before committing.","operationId":"burn_secret_v1_secrets__locator__burn_post","parameters":[{"name":"locator","in":"path","required":true,"schema":{"type":"string","title":"Locator"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/secrets/{locator}/revoke":{"post":{"tags":["secrets-v1"],"summary":"Revoke Secret","description":"Owner-initiated revocation before natural expiry.\n\nAuth required.  Only the owner (or an org admin for org-scoped secrets)\nmay revoke.  Returns { revoked: true } on success.","operationId":"revoke_secret_v1_secrets__locator__revoke_post","parameters":[{"name":"locator","in":"path","required":true,"schema":{"type":"string","title":"Locator"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/secrets/{locator}/authorize":{"post":{"tags":["secrets-v1"],"summary":"Authorize Recipient","description":"Obtain a short-lived bearer token for recipient-auth gated secrets.\n\nX2: API_SPEC §4.3.\n- Anon caller → 403 recipient_auth_required.\n- Authenticated caller → 200 with a 60s bearer token that satisfies\n  the requires_recipient_login gate on GET /v1/secrets/{locator}.\n\nThe short-lived token is a HS256 JWT scoped to this locator and caller.\nIt is verified in retrieve_secret when the caller presents it as\nAuthorization: Bearer <recipient_token>.","operationId":"authorize_recipient_v1_secrets__locator__authorize_post","parameters":[{"name":"locator","in":"path","required":true,"schema":{"type":"string","title":"Locator"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/secrets/code/{code}/lookup":{"post":{"tags":["secrets-v1"],"summary":"Lookup Code","description":"Resolve a 6-char retrieval code to its locator + Argon2id salt/params.\n\nWS-3 — Mode B (Code + Passphrase) retrieval flow.\n\nRate-limited: 3/min per IP (tight — brute-force defense).\nPer-code lockout: after 5 failed lookups in 5 minutes, return generic 404\nso the code's existence is not leaked.\n\nOn success: return { locator, saltB64, argonParams } — no ciphertext.\nOn failure (wrong code / locked): return 404 (no oracle on existence).\n\nAudit: emits code.looked_up on success (metadata only — no salt/params/key).","operationId":"lookup_code_v1_secrets_code__code__lookup_post","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","title":"Code"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/abuse-report":{"post":{"tags":["secrets-v1"],"summary":"Submit Abuse Report","description":"Recipient-side abuse report (anon or session).\n\nTight rate limit: 5/IP/hour per SECURITY_OPERATIONS §2.","operationId":"submit_abuse_report_v1_abuse_report_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/identity/signup":{"post":{"tags":["identity-v1"],"summary":"Signup","description":"Create a new email/password account.\n\nFR-32: acknowledgedIrreversibility MUST be true.\nRejects disposable email domains.\nRate-limited: 3/IP/hour.","operationId":"signup_v1_identity_signup_post","parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/identity/verify-email":{"post":{"tags":["identity-v1"],"summary":"Verify Email","description":"Confirm email address via the one-time token.","operationId":"verify_email_v1_identity_verify_email_post","parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/identity/login":{"post":{"tags":["identity-v1"],"summary":"Login","description":"Authenticate with email + password (+ optional TOTP code).\n\nB6: Constant-time gap when user not found (runs dummy Argon2id verify).\nH1: Blocks login if email_verified=false.\nB7: Per-account rate limit key added.","operationId":"login_v1_identity_login_post","parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/identity/refresh":{"post":{"tags":["identity-v1"],"summary":"Refresh Token","description":"Exchange a refresh token for a new access + refresh token pair.\n\nH2: Race fix — revoke_refresh_token uses ConditionExpression to detect\nconcurrent use.  On ConditionalCheckFailedException → revoke-all.\nB2: Check user status before issuing new tokens.\nM3: Propagate mfa_verified from refresh token record to new access token.","operationId":"refresh_token_v1_identity_refresh_post","parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/identity/logout":{"post":{"tags":["identity-v1"],"summary":"Logout","description":"Invalidate the current session.\n\nB4: jti guard removed — if jti is missing it's treated as a hard-fail 500\n(this is handled in require_session which already enforces jti presence).\nThe JTI is added to the denylist unconditionally.\nOptionally revokes the refresh token if provided.","operationId":"logout_v1_identity_logout_post","parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/identity/password-reset/request":{"post":{"tags":["identity-v1"],"summary":"Password Reset Request","description":"Initiate password reset — always returns 202 (no enumeration).","operationId":"password_reset_request_v1_identity_password_reset_request_post","parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/identity/password-reset/confirm":{"post":{"tags":["identity-v1"],"summary":"Password Reset Confirm","description":"Complete password reset using the one-time token.\n\nB3: Returns { success: true, requireLogin: true } — does NOT issue tokens.\nUser must re-authenticate (with MFA if enrolled).","operationId":"password_reset_confirm_v1_identity_password_reset_confirm_post","parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/identity/mfa/enroll":{"post":{"tags":["identity-v1"],"summary":"Mfa Enroll","description":"Begin TOTP enrollment.\n\nB8: If mfa_enrolled is already True → 409 mfa_already_enrolled.\nReturns otpauth URL + QR code SVG + recovery codes.\nMFA is NOT active until /mfa/verify is called.","operationId":"mfa_enroll_v1_identity_mfa_enroll_post","parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/identity/mfa/verify":{"post":{"tags":["identity-v1"],"summary":"Mfa Verify","description":"Confirm TOTP enrollment by verifying a fresh code.\n\nH3: Accepts current_password in body; verifies before activating MFA.\nActivates MFA on the account.","operationId":"mfa_verify_v1_identity_mfa_verify_post","parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/identity/mfa/disenroll":{"post":{"tags":["identity-v1"],"summary":"Mfa Disenroll","description":"Remove MFA from the account.\n\nRequires both current password and a valid TOTP code.","operationId":"mfa_disenroll_v1_identity_mfa_disenroll_post","parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/identity/mfa/recover":{"post":{"tags":["identity-v1"],"summary":"Mfa Recover","description":"Login using a recovery code (bypasses TOTP).\n\nB9: Accepts { email, password, recovery_code }.\nVerifies password (Argon2id), verifies recovery_code, issues full tokens\nwith mfa_verified=true on success.\nRate-limited: 3/hour per account.\nEmits user.mfa_recovery_used audit event.","operationId":"mfa_recover_v1_identity_mfa_recover_post","parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/identity/me":{"get":{"tags":["identity-v1"],"summary":"Get Me","description":"Return the current user's profile.\n\nM4: Rate-limited per IP.","operationId":"get_me_v1_identity_me_get","parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/identity/.well-known/jwks.json":{"get":{"tags":["identity-v1"],"summary":"Jwks","description":"Return the JWKS (public keys) for JWT verification.\n\nCacheable for 1 hour (Cache-Control: max-age=3600).\nM10: ETag header derived from JWKS body sha256.","operationId":"jwks_v1_identity__well_known_jwks_json_get","parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/identity/account":{"delete":{"tags":["identity-v1"],"summary":"Delete Account","description":"Schedule account deletion (soft-delete with 30-day grace period).\n\nRequires the literal string \"DELETE\" as confirmation.\nIf MFA is enrolled, the caller must also supply a valid TOTP code\nin the request body (M2: mfaCode field, not X-MFA-Code header).\nB2: Calls revoke_all_refresh_tokens_for_user on deletion.\nM8: Audits repeat (idempotent) delete calls.","operationId":"delete_account_v1_identity_account_delete","parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/identity/data-export":{"get":{"tags":["identity-v1"],"summary":"Data Export","description":"Return a data-portability export of the user's account data.\n\nZERO-KNOWLEDGE: never includes secret ciphertext, decryption keys, or\npassphrase values.  Only metadata and profile data are exported.\n\nPhase-1: synchronous export.  Phase-2: async + email delivery (WS-21).","operationId":"data_export_v1_identity_data_export_get","parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/operator/login":{"post":{"tags":["operator-v1"],"summary":"Operator Login","description":"Authenticate an operator with email + password + TOTP (MFA always required).\n\n- 15-minute token TTL\n- No refresh tokens (operator sessions are short-lived by design)\n- 5-failure / 5-minute lockout per operator account\n- Generic 401 for all failure modes (no enumeration)","operationId":"operator_login_v1_operator_login_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/operator/logout":{"post":{"tags":["operator-v1"],"summary":"Operator Logout","description":"Revoke the current operator JWT by adding its JTI to the deny-list.\n\nBL-3: After logout the token is rejected by verify_operator_token even if\nit has not yet expired.  The deny-list item self-cleans via DynamoDB TTL\nset to the token's exp.","operationId":"operator_logout_v1_operator_logout_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/operator/takedown/approve":{"post":{"tags":["operator-v1"],"summary":"Operator Takedown Approve","description":"BL-2: L3+ operator (the approver) issues a short-lived HMAC approval token.\n\nThe initiating operator presents {initiatorOperatorId, locator, reasonCode, nonce}\nto the approver; the approver calls this endpoint with their own JWT.  The server\nreturns a 5-min HMAC token bound to those fields.\n\nThe approval token is then passed to POST /v1/operator/secrets/{locator}/takedown\nas approvalToken.","operationId":"operator_takedown_approve_v1_operator_takedown_approve_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/operator/secrets/{locator}":{"get":{"tags":["operator-v1"],"summary":"Operator Get Secret Metadata","description":"Return secret envelope metadata.  NEVER returns ciphertext, key, or fragment.\n\nResponse carries X-Operator-Content-Blackout: true on every response.","operationId":"operator_get_secret_metadata_v1_operator_secrets__locator__get","parameters":[{"name":"locator","in":"path","required":true,"schema":{"type":"string","title":"Locator"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/operator/secrets/{locator}/takedown":{"post":{"tags":["operator-v1"],"summary":"Operator Takedown Secret","description":"Takedown a secret (mark taken_down + S3 purge).\n\nBL-2: Requires HMAC approval_token from POST /v1/operator/takedown/approve.\nBL-8: ConditionExpression rejects transitions from burned/revoked/taken_down.\n      Best-effort S3 deletion after state flip.\n\nRequires:\n  - L3+ role\n  - approverOperatorId (the L3+ operator who issued the approvalToken)\n  - approvalToken (HMAC-signed, 5-min TTL, bound to {initiator, locator, reason_code})\n  - reason_code from enum\n  - justification >= 50 chars","operationId":"operator_takedown_secret_v1_operator_secrets__locator__takedown_post","parameters":[{"name":"locator","in":"path","required":true,"schema":{"type":"string","title":"Locator"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/operator/users/{user_id}/suspend":{"post":{"tags":["operator-v1"],"summary":"Operator Suspend User","description":"Suspend a user account (soft or hard).\n\nL2: soft only.  Hard suspension requires L3+ per ADMIN_CONSOLE_SPEC §4.3.","operationId":"operator_suspend_user_v1_operator_users__user_id__suspend_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/operator/users/{user_id}/unsuspend":{"post":{"tags":["operator-v1"],"summary":"Operator Unsuspend User","description":"Restore a suspended user account.","operationId":"operator_unsuspend_user_v1_operator_users__user_id__unsuspend_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/operator/audit":{"get":{"tags":["operator-v1"],"summary":"Operator Audit Query","description":"Query the operator audit log.\n\nL1: own actions only (actor forced to self).\nL3+: unrestricted query.\nformat=csv: returns CSV download.\n\nBL-7: Emits operator.audit_log_read BEFORE returning — reading the audit\nlog is itself audited (SOC 2 requirement).","operationId":"operator_audit_query_v1_operator_audit_get","parameters":[{"name":"actor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor"}},{"name":"action","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"default":"json","title":"Format"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/operator/break-glass":{"post":{"tags":["operator-v1"],"summary":"Operator Break Glass","description":"Break-glass elevation endpoint.\n\nALWAYS returns 503 at Phase 1 launch per ADMIN_CONSOLE_SPEC §9.0 and\nowner-ratified decision of 2026-05-24.\n\nThe endpoint exists for API contract completeness.  Break-glass is\nre-enabled when BREAK_GLASS_ENABLED=enabled env var is set AND a second\nL4 co-signer has been provisioned.  Until then: 503.\n\nEvery invocation attempt is audit-logged regardless.","operationId":"operator_break_glass_v1_operator_break_glass_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/checkout":{"post":{"tags":["billing-v1"],"summary":"Create Checkout","description":"Initiate a Paddle-hosted checkout session for the calling user.\n\nAuth: session required.\nIdempotency: yes (24h cache on Idempotency-Key).\nAudit: billing.checkout_initiated","operationId":"create_checkout_v1_billing_checkout_post","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/portal":{"post":{"tags":["billing-v1"],"summary":"Create Portal","description":"Return a Paddle customer portal URL for self-serve billing management.\n\nAuth: session required.","operationId":"create_portal_v1_billing_portal_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/cancel":{"post":{"tags":["billing-v1"],"summary":"Cancel Subscription","description":"Cancel the caller's active subscription.\n\nAuth: session required.\nIdempotency: yes.\nAudit: billing.subscription_canceled","operationId":"cancel_subscription_v1_billing_cancel_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/subscription":{"get":{"tags":["billing-v1"],"summary":"Get Subscription","description":"Return current subscription metadata for the calling user.\n\nAuth: session required.\nIDOR: returns only the caller's own subscription.","operationId":"get_subscription_v1_billing_subscription_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/entitlement":{"get":{"tags":["billing-v1"],"summary":"Get Entitlement","description":"Return the AUTHORITATIVE tier and feature entitlement for the caller.\n\nAPI_SPEC §7.4: This is the source of truth for paid-feature gates.\nThe JWT plan claim is a cache hint and may lag webhook events by seconds.\nAll feature gates must read this endpoint, not the JWT.\n\nAuth: session required.\nIDOR: returns only the caller's own entitlement.","operationId":"get_entitlement_v1_billing_entitlement_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/billing/invoices":{"get":{"tags":["billing-v1"],"summary":"List Invoices","description":"Return recent invoices proxied from the payment processor.\n\nAuth: session required.","operationId":"list_invoices_v1_billing_invoices_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":12,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/paddle/webhook":{"post":{"tags":["billing-v1"],"summary":"Paddle Webhook","description":"Receive and process Paddle webhook events.\n\nAuth: HMAC-SHA256 signature verification (Paddle-Signature header).\nIdempotency: event_id written to DDB with conditional PutItem (30d TTL).\nDLQ: unhandled exceptions after signature verification go to SQS DLQ\n     (infrastructure concern, documented in DAY2_OPS_PLAN SOP-05).\n\nReturns 200 fast; webhook processing is done in a background task when\nthe handler is lightweight.  For DynamoDB writes this is synchronous\n(within Lambda timeout budget).","operationId":"paddle_webhook_v1_paddle_webhook_post","parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/usage":{"get":{"tags":["usage-v1"],"summary":"Get Usage","description":"Return the caller's current daily quota counters.\n\nAC-26: free daily cap is 50 creates/day for signed-in, 10 for guests.\nAC-30: quota banner uses this data to decide whether to render.\n\nAnon callers receive guest-tier limits scoped to their salted IP hash.\nAuthenticated callers receive limits from their billing entitlement.\n\nThis endpoint is read-only and never increments any counter.\nZero-knowledge invariant: no secret content is accessed or returned.","operationId":"get_usage_v1_usage_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}},{"name":"X-Request-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Request-Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/":{"get":{"summary":"Root","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health":{"get":{"summary":"Health","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"Body_upload_api_upload_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"},"one_time":{"type":"boolean","title":"One Time","default":false},"expires_in_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Expires In Days"}},"type":"object","required":["file"],"title":"Body_upload_api_upload_post"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"PasteRequest":{"properties":{"text":{"type":"string","title":"Text"},"one_time":{"type":"boolean","title":"One Time","default":false},"expires_in_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Expires In Days"}},"type":"object","required":["text"],"title":"PasteRequest"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}}}}