{"openapi":"3.1.0","info":{"title":"ApplyJobFaster API","summary":"Tailor resumes, compile ATS-ready PDFs, and send application emails.","description":"Programmatic access to ApplyJobFaster.\n\n**Authentication.** Browser clients use the HttpOnly `rmcp_session` cookie set at Google OAuth completion; the MCP server uses an `X-MCP-Token` header. Named OAuth scopes are declared under `components.securitySchemes.OAuth2` so agents can request least-privilege access.\n\n**Rate limits.** 120 requests/minute per session (or per IP for anonymous traffic). Responses carry `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`; a `429` carries `Retry-After`.\n\n**Versioning.** URL-path versioning. A retiring version is announced at least 90 days ahead and then serves `Deprecation: true` + a `Sunset` date header (RFC 8594). See `GET /api/version`.\n\n**Discovery.** MCP manifest at `/.well-known/mcp.json`; developer portal at https://applyjobfaster.com/developers.","version":"1.0.0","contact":{"name":"ApplyJobFaster Support","url":"https://applyjobfaster.com/developers","email":"anup2001ojha@gmail.com"},"license":{"name":"Terms of Service","url":"https://applyjobfaster.com/terms"},"termsOfService":"https://applyjobfaster.com/terms"},"paths":{"/admin/{path}":{"delete":{"summary":"Admin Proxy","description":"Proxy to Adminer DB UI - password protected via ADMIN_PASSWORD env var.","operationId":"admin_proxy_admin__path__delete","security":[{"HTTPBasic":[]}],"parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"summary":"Admin Proxy","description":"Proxy to Adminer DB UI - password protected via ADMIN_PASSWORD env var.","operationId":"admin_proxy_admin__path__delete_patch","security":[{"HTTPBasic":[]}],"parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"Admin Proxy","description":"Proxy to Adminer DB UI - password protected via ADMIN_PASSWORD env var.","operationId":"admin_proxy_admin__path__delete_get","security":[{"HTTPBasic":[]}],"parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"summary":"Admin Proxy","description":"Proxy to Adminer DB UI - password protected via ADMIN_PASSWORD env var.","operationId":"admin_proxy_admin__path__delete_post","security":[{"HTTPBasic":[]}],"parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"summary":"Admin Proxy","description":"Proxy to Adminer DB UI - password protected via ADMIN_PASSWORD env var.","operationId":"admin_proxy_admin__path__delete_put","security":[{"HTTPBasic":[]}],"parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api":{"get":{"tags":["Agent & Discovery","Agent & Discovery"],"summary":"API index","description":"Human- and agent-readable index of the public API. Returns JSON by default, or Markdown when the request sends `Accept: text/markdown`.","operationId":"agent_api_index","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/status":{"get":{"tags":["Agent & Discovery","Agent & Discovery"],"summary":"Service status","description":"Public, unauthenticated machine-readable service status. Safe for agents to poll to confirm the API is reachable.","operationId":"agent_service_status","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/version":{"get":{"tags":["Agent & Discovery","Agent & Discovery"],"summary":"API version & deprecation policy","description":"The current API version and how deprecations are signalled (URL-path versioning; retiring versions ship Deprecation + Sunset headers).","operationId":"agent_api_version","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/llms.txt":{"get":{"tags":["Agent & Discovery","Agent & Discovery"],"summary":"Agent instructions (llms.txt)","description":"When-to-use guidance and discovery links for AI agents.","operationId":"agent_llms_txt","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/mcp.json":{"get":{"tags":["Agent & Discovery","Agent & Discovery"],"summary":"MCP discovery manifest","operationId":"agent_mcp_manifest","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"description":"MCP discovery manifest"}},"/.well-known/mcp/server-card.json":{"get":{"tags":["Agent & Discovery","Agent & Discovery"],"summary":"MCP server card","operationId":"agent_mcp_server_card","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"description":"MCP server card"}},"/.well-known/oauth-protected-resource":{"get":{"tags":["Agent & Discovery","Agent & Discovery"],"summary":"OAuth protected-resource metadata (RFC 9728)","operationId":"agent_protected_resource_metadata","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"description":"OAuth protected-resource metadata (RFC 9728)"}},"/":{"get":{"tags":["Pages & SEO"],"summary":"Root Redirect","description":"Redirect bare API domain visits to the Vercel frontend.","operationId":"root_redirect__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/sitemap.xml":{"get":{"tags":["Pages & SEO"],"summary":"Sitemap","operationId":"sitemap_sitemap_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"description":"Sitemap"}},"/oauth/callback":{"get":{"tags":["Pages & SEO"],"summary":"Serve Oauth Callback","operationId":"serve_oauth_callback_oauth_callback_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"description":"Serve Oauth Callback"}},"/robots.txt":{"get":{"tags":["Pages & SEO"],"summary":"Robots","operationId":"robots_robots_txt_get","responses":{"200":{"description":"Successful Response"}},"description":"Robots"}},"/auth/url":{"get":{"tags":["Authentication"],"summary":"Get Auth Url","description":"Return a Google OAuth2 URL the Telegram bot (or Mini App) can redirect to.\nsource: 'bot' (default) or 'webapp' - controls what the callback page returns.\nmode:   'signup' (default) or 'signin' - controls account creation vs login validation.","operationId":"get_auth_url_auth_url_get","parameters":[{"name":"telegram_user_id","in":"query","required":true,"schema":{"type":"string","title":"Telegram User Id"}},{"name":"source","in":"query","required":false,"schema":{"type":"string","default":"bot","title":"Source"}},{"name":"mode","in":"query","required":false,"schema":{"type":"string","default":"signup","title":"Mode"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/google/callback":{"get":{"tags":["Authentication"],"summary":"Google Callback","description":"Google redirects here after user consents.\nStores tokens in PostgreSQL, marks user as registered, and returns\nan appropriate response depending on the source:\n  - source=web    → 302 redirect to Vue landing/callback (errors include msg)\n  - source=webapp → HTML page that closes the Mini App\n  - source=bot    → plain HTML success card","operationId":"google_callback_auth_google_callback_get","parameters":[{"name":"code","in":"query","required":false,"schema":{"type":"string","title":"Code"}},{"name":"state","in":"query","required":false,"schema":{"type":"string","title":"State"}},{"name":"error","in":"query","required":false,"schema":{"type":"string","title":"Error"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/webapp-init":{"post":{"tags":["Authentication"],"summary":"Webapp Init","description":"Phase 2 - Verify Telegram Mini App initData signature.\nCalled by the Mini App JS on load to confirm identity and check registration status.\nReturns the user's profile + token balance if valid.","operationId":"webapp_init_api_auth_webapp_init_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebAppInitRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/me":{"get":{"tags":["Authentication"],"summary":"Get Me","description":"Resolve the current user from the session cookie. The frontend uses\nthis on app boot instead of /api/users/{uid}/profile - the uid stops\nappearing in URLs entirely.","operationId":"get_me_auth_me_get","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/session/{telegram_user_id}":{"get":{"tags":["Authentication"],"summary":"Get Session","description":"Return profile + token info for a web user. Requires Google sign-in.","operationId":"get_session_auth_session__telegram_user_id__get","parameters":[{"name":"telegram_user_id","in":"path","required":true,"schema":{"type":"string","title":"Telegram User Id"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Authentication"],"summary":"Logout","description":"Revoke Google tokens AND the current session cookie.\n\nSECURITY: previously this accepted any caller - anyone who knew a\ntelegram_id could nuke that user's tokens. Now require_same_user\nenforces that the caller's session matches the path uid.","operationId":"logout_auth_session__telegram_user_id__delete","parameters":[{"name":"telegram_user_id","in":"path","required":true,"schema":{"type":"string","title":"Telegram User Id"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/gmail/connected/{telegram_user_id}":{"get":{"tags":["Authentication"],"summary":"Gmail Connected","description":"Lightweight check: is Gmail connected for this user?\nUsed by the Telegram bot to gate /apply and similar features.","operationId":"gmail_connected_auth_gmail_connected__telegram_user_id__get","parameters":[{"name":"telegram_user_id","in":"path","required":true,"schema":{"type":"string","title":"Telegram User Id"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/logout":{"post":{"tags":["Authentication"],"summary":"Logout Via Cookie","description":"Cookie-only sign-out - no uid in the URL needed.\nRevokes the current session token and clears the cookie.","operationId":"logout_via_cookie_auth_logout_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/users/session":{"post":{"tags":["Users & Tokens"],"summary":"Create Or Get User Session","description":"Create or get a user session. Called by bot on /start.\n\nBot-only: requires the shared X-Bot-Token secret. Without it, anyone could\nspam new telegram_user rows or upsert profile fields.","operationId":"create_or_get_user_session_api_users_session_post","parameters":[{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/users/{telegram_id}/balance":{"get":{"tags":["Users & Tokens"],"summary":"Get Token Balance","description":"Get token balance for a user.\n\nSECURITY: previously this endpoint auto-created a missing user row with\ndefault 5 tokens. That caused a \"ghost user\" bug after account deletion:\nthe frontend's periodic balance refresh would resurrect the just-deleted\nuser with a stub profile (no Google data), making the UI look logged-in\nwith a username of \"User\" and 5 fresh tokens. We now return 404 so the\nfrontend can detect the deletion and force a sign-out.","operationId":"get_token_balance_api_users__telegram_id__balance_get","parameters":[{"name":"telegram_id","in":"path","required":true,"schema":{"type":"string","title":"Telegram Id"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/users/{telegram_id}/deduct":{"post":{"tags":["Users & Tokens"],"summary":"Deduct Tokens","description":"Check and deduct tokens for an operation.","operationId":"deduct_tokens_api_users__telegram_id__deduct_post","parameters":[{"name":"telegram_id","in":"path","required":true,"schema":{"type":"string","title":"Telegram Id"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/users/{telegram_id}/profile":{"get":{"tags":["Users & Tokens"],"summary":"Get User Profile","description":"Get full user profile.","operationId":"get_user_profile_api_users__telegram_id__profile_get","parameters":[{"name":"telegram_id","in":"path","required":true,"schema":{"type":"string","title":"Telegram Id"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/onboarding/seen":{"post":{"tags":["Users & Tokens"],"summary":"Mark Onboarding Seen","description":"Mark the first-run welcome popup as seen for the authed user.\n\nTakes no path parameter on purpose - the caller can only ever mark itself,\nso there is nothing to spoof. Idempotent: repeat calls keep the original\ntimestamp.","operationId":"mark_onboarding_seen_api_onboarding_seen_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/users/{telegram_id}":{"delete":{"tags":["Users & Tokens"],"summary":"Delete User Account","description":"Permanently delete a user account and ALL associated data.\n\nCascade order:\n  1. Revoke Google OAuth tokens with Google (best-effort)\n  2. Delete R2 PDF objects (best-effort)\n  3. Hard-delete DB rows: google_tokens → token_usage → user_resumes → telegram_users","operationId":"delete_user_account_api_users__telegram_id__delete","parameters":[{"name":"telegram_id","in":"path","required":true,"schema":{"type":"string","title":"Telegram Id"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/custom-resume":{"post":{"tags":["PDFs & System"],"summary":"Upload Custom Resume","description":"Upload a user's own resume PDF into the single 'custom' slot.\n\nValidated like the rest of the app (PDF only, 5 MB max). The file is stored\nverbatim in R2 (custom/{tid}.pdf) and recorded as slot='custom' with no\nresume_json - it is never tailored. Uploading again overwrites the slot.","operationId":"upload_custom_resume_api_custom_resume_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_custom_resume_api_custom_resume_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__routers__pdfs__PDFResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/custom-resume/rename":{"post":{"tags":["PDFs & System"],"summary":"Rename Custom Resume","description":"Rename the display name of the user's custom resume.\n\nThe R2 object is keyed per-slot and never moves; this only updates the\nhuman-facing `filename` shown in My PDFs and the Apply picker.","operationId":"rename_custom_resume_api_custom_resume_rename_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenameCustomRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__routers__pdfs__PDFResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/generate":{"post":{"tags":["PDFs & System"],"summary":"Generate Pdf","description":"Generate a PDF from LaTeX code\n\nArgs:\n    request: GeneratePDFRequest with latex_code and optional filename\n\nReturns:\n    PDFResponse with success status and message","operationId":"generate_pdf_api_generate_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeneratePDFRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__routers__pdfs__PDFResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/pdfs":{"get":{"tags":["PDFs & System"],"summary":"List Pdfs","description":"List the authenticated user's 2 resume slots from DB (master + tailored).\nIdentity is resolved from the session cookie (web) or X-Bot-Token (bot).","operationId":"list_pdfs_api_pdfs_get","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/pdfs/{filename}/download":{"get":{"tags":["PDFs & System"],"summary":"Force Download Pdf","description":"Force-download a PDF with Content-Disposition: attachment.\n\nUnlike GET /api/pdfs/{filename} which 302-redirects to a signed R2 URL\n(browsers silently ignore the `download` attribute on cross-origin redirects),\nthis endpoint fetches the bytes directly and returns them with an attachment\nheader so the browser shows a Save-As dialog every time.\n\nAUTH: identity from session cookie (web) or X-Bot-Token (bot). The\nuser_id query param has been removed.","operationId":"force_download_pdf_api_pdfs__filename__download_get","parameters":[{"name":"filename","in":"path","required":true,"schema":{"type":"string","title":"Filename"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/pdfs/{filename}":{"get":{"tags":["PDFs & System"],"summary":"Download Pdf","description":"Download a generated PDF.\n\nSource-of-truth precedence:\n  1. If the slot has an r2_key, mint a fresh short-lived signed URL and 302-redirect.\n     (Stable forever-URL on FastAPI, never-expiring from the user's POV.)\n  2. Else fall back to streaming the local file (legacy / R2-disabled mode).\n\nAUTH: caller's identity is resolved from the rmcp_session cookie (web)\nor X-Bot-Token + X-User-Id (internal bot). The user_id query param is\nno longer accepted - it used to leak uids into browser history, server\nlogs, and email tracking pixel URLs.","operationId":"download_pdf_api_pdfs__filename__get","parameters":[{"name":"filename","in":"path","required":true,"schema":{"type":"string","title":"Filename"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["PDFs & System"],"summary":"Delete Pdf","description":"Delete a generated PDF - removes the R2 object (if any) and the local file.\nIdentity is resolved from the session cookie (web) or X-Bot-Token (bot).","operationId":"delete_pdf_api_pdfs__filename__delete","parameters":[{"name":"filename","in":"path","required":true,"schema":{"type":"string","title":"Filename"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/build-info":{"get":{"tags":["PDFs & System"],"summary":"Build Info","description":"Return deploy/build timestamp and git info for the footer 'Last updated' display.","operationId":"build_info_api_build_info_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/health":{"get":{"tags":["PDFs & System"],"summary":"Health Check","description":"Check if LaTeX is installed and system is ready","operationId":"health_check_api_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/r2/test":{"get":{"tags":["PDFs & System"],"summary":"R2 Smoke Test","description":"Upload a tiny dummy file to R2, generate a signed URL, verify existence, then delete it.\nReturns a step-by-step report so you can see exactly where the flow breaks.","operationId":"r2_smoke_test_api_r2_test_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/stats":{"get":{"tags":["PDFs & System"],"summary":"Get Stats","description":"Return usage statistics for the landing page (floored to marketing baseline).","operationId":"get_stats_api_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/rename-pdf":{"post":{"tags":["PDFs & System"],"summary":"Rename Pdf","description":"Rename an existing PDF in the output directory.\n\nThe renamed file must belong to the authenticated user - we reject any\ncross-user rename even if the filename argument doesn't reveal ownership.","operationId":"rename_pdf_api_rename_pdf_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenamePDFRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__routers__pdfs__PDFResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/parse-resume-pdf":{"post":{"tags":["Resume"],"summary":"Parse Resume Pdf","description":"Extract plain text (+ embedded hyperlinks) from an uploaded resume PDF or DOCX.\n\nReturns:\n    { success, text, links: { linkedin, github, portfolio, other[] } }\n\nThe frontend uses this to pre-fill the \"Your details\" textarea so the user\ndoesn't have to retype their background. Detected profile URLs are appended\nto the text so the AI picks them up when building the structured resume JSON.","operationId":"parse_resume_pdf_api_parse_resume_pdf_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_parse_resume_pdf_api_parse_resume_pdf_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/parse-resume-form":{"post":{"tags":["Resume"],"summary":"Parse Resume Form","description":"Parse an uploaded resume into the guided-wizard FORM shape (prefill).\n\nReturns:\n    { success, form: {...}, warnings: [...] }\n\nFree on purpose: nothing generated here is keepable, and the user still pays\n(or spends their one free grant) at Generate. Charging would bill twice for a\nsingle resume. The abuse guard is the 10/minute limit shared with\n/api/parse-resume-pdf.","operationId":"parse_resume_form_api_v2_parse_resume_form_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_parse_resume_form_api_v2_parse_resume_form_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/parse-jd":{"post":{"tags":["Resume"],"summary":"Parse Jd","description":"Parse job description from file or text and extract requirements\n\nArgs:\n    jd_file: Optional uploaded file (image, PDF, DOCX)\n    jd_text: Optional plain text JD\n\nReturns:\n    Extracted requirements and analysis","operationId":"parse_jd_api_parse_jd_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_parse_jd_api_parse_jd_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/customize-resume":{"post":{"tags":["Resume"],"summary":"Customize Resume","description":"[Deprecated] Generate a customized resume based on a job description (v1 pipeline).\n\nReplaced by `/api/v2/create-resume` + `/api/v2/tailor-resume`. Kept for\nbackward compatibility with the MCP `customize_resume_for_jd` tool. Will\nbe removed in a future cleanup.\n\nArgs:\n    jd_file: Optional uploaded JD file (image, PDF, DOCX)\n    jd_text: Optional plain text JD\n    user_details: Optional JSON string with user information\n    filename: Output PDF filename\n\nReturns:\n    PDFResponse with generated customized resume","operationId":"customize_resume_api_customize_resume_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_customize_resume_api_customize_resume_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__routers__resume__PDFResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/tailor-smart":{"post":{"tags":["Resume"],"summary":"Tailor Smart","description":"Smart tailor endpoint - v2 pipeline (Jinja2 + LaTeX).\n\nSource = the user's master/primary resume. We never tailor a tailored copy.\nSource priority:\n  1. Saved JSON from v2 create (resume_{user_id}.json)\n  2. Saved LaTeX source (resume_{user_id}.tex)\n  3. Uploaded resume_file (only if no master saved yet)\n  4. Typed resume_text (only if no master saved yet)\n\nOutput is the user's single 'tailored' slot - overwrites the previous one.\nFilename: {uid}_tailored.pdf (caller `filename` arg is ignored to keep this\ninvariant; the slot is always the same file).","operationId":"tailor_smart_api_tailor_smart_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_tailor_smart_api_tailor_smart_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__routers__resume__PDFResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/update-resume":{"post":{"tags":["Resume"],"summary":"Update Resume","description":"[v2] Update the user's saved master resume based on free-form instructions.\n\nPipeline:\n  1. Load saved JSON (output/resume_{uid}.json) - written by /create\n  2. AI re-runs the create flow with: existing_resume_text + update_instructions\n  3. New JSON overwrites the old → Jinja2 → LaTeX → PDF\n  4. R2 upload + master slot saved to DB\n  5. PDF filename derived from candidate name (consistent with /create)","operationId":"update_resume_api_update_resume_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateResumeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__routers__resume__PDFResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/enhance-bullets":{"post":{"tags":["Resume"],"summary":"Enhance Bullets","description":"Transform a basic resume bullet into 3 high-impact ATS-optimized variations.\n\nReturns a JSON array of 3 enhanced bullet strings.","operationId":"enhance_bullets_api_enhance_bullets_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnhanceBulletsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/draft-email":{"post":{"tags":["Resume"],"summary":"Draft Email","operationId":"draft_email_api_draft_email_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_draft_email_api_draft_email_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"Draft Email"}},"/api/apply-smart":{"post":{"tags":["Resume"],"summary":"Apply Smart","description":"Tailor resume to JD, compose a cover email, and send via user's Gmail.\nRequires the user to be authenticated with Google OAuth (/login).\nIf email_subject and email_body are provided, they are used as-is and\nAI email composition is skipped.\n\nsource_slot controls which saved resume is used as the base for tailoring:\n  'master'   - primary saved resume (default)\n  'tailored' - previous tailored copy (useful when re-applying to similar roles)\nHas no effect when resume_text or resume_file is provided.","operationId":"apply_smart_api_apply_smart_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_apply_smart_api_apply_smart_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/jd-match-check":{"post":{"tags":["Resume"],"summary":"Jd Match Check","description":"Read-only relevance gate used by the Tailor/Apply tabs BEFORE the paid\ntailor/send path. No token deduction, no AI writes - just one scoring call.\n\nReturns: { band, score (0-100|null), reason, word_count }\n  band ∈ \"match\" | \"weak\" | \"mismatch\" | \"incomplete\" | \"skip\" | \"unknown\"\n    - mismatch   → JD and resume are fundamentally different; warn the user\n    - incomplete → JD too short/vague to assess\n    - skip       → custom slot (sent as-is, never tailored) - no gate\n    - unknown    → could not score (AI down / no saved resume) → fail OPEN","operationId":"jd_match_check_api_jd_match_check_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JdMatchCheckRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/extract-jd-details":{"post":{"tags":["Resume"],"summary":"Extract Jd Details","description":"Extract recipient email, job title, and company name from a JD file or text.","operationId":"extract_jd_details_api_extract_jd_details_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_extract_jd_details_api_extract_jd_details_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/create-resume":{"post":{"tags":["Resume"],"summary":"Create Resume V2","description":"[v2]  Create a resume using the Jinja2+LaTeX pipeline.\n\nSteps:\n  1.  Gemini generates structured JSON from free-form user details.\n  2.  Jinja2 renders the JSON into deterministic LaTeX (resume.tex.j2).\n  3.  pdflatex compiles LaTeX → PDF.\n\nThe JSON is also saved as  output/resume_{user_id}.json  for inspection.","operationId":"create_resume_v2_api_v2_create_resume_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateResumeV2Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__routers__resume__PDFResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v2/tailor-resume":{"post":{"tags":["Resume"],"summary":"Tailor Resume V2","description":"[v2]  Tailor an existing resume to a JD using the Jinja2+LaTeX pipeline.\n\nSteps:\n  1.  Gemini generates tailored JSON from resume text + JD requirements.\n  2.  Jinja2 renders the JSON into deterministic LaTeX (resume.tex.j2).\n  3.  pdflatex compiles LaTeX → PDF.","operationId":"tailor_resume_v2_api_v2_tailor_resume_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TailorResumeV2Request"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__routers__resume__PDFResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/resume-exists/{user_id}":{"get":{"tags":["Resume"],"summary":"Resume Exists","description":"Check whether a saved master resume exists for a user.\nUses DB (slot row) as authoritative source, falls back to JSON file on disk.","operationId":"resume_exists_api_resume_exists__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/reports":{"post":{"tags":["Reports"],"summary":"Submit Report","description":"Submit a bug report. Image is optional; if provided, must be JPEG.","operationId":"submit_report_api_reports_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_submit_report_api_reports_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/reports/{report_id}/image":{"get":{"tags":["Reports"],"summary":"Report Image","description":"Serve the JPEG screenshot for a report.\n\nAdmin-only: protected by the same `_is_authed` cookie used by /db. UUIDs\nare unguessable but report bodies may contain PII (screenshots), so we\ngate this behind admin auth rather than relying on URL secrecy alone.","operationId":"report_image_api_reports__report_id__image_get","parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"string","title":"Report Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/t/o/{tracking_id}.gif":{"get":{"tags":["Tracking & Notifications"],"summary":"Track Open","description":"Tracking pixel endpoint. Returns the 43-byte transparent GIF unconditionally,\nand best-effort records the open in the email_opens table.\n\nNever raises - always returns 200 with the pixel bytes, even for unknown IDs\n(we don't want recipients seeing a broken image in their inbox).\n\n@limiter.exempt: this route MUST NOT be rate-limited. All Gmail opens\narrive from Google's shared image-proxy IPs, so the app-wide default\nlimit (120/min per anonymous IP bucket) could 429 a burst of pixel hits\nand silently lose real opens - Gmail never retries a failed image load.","operationId":"track_open_t_o__tracking_id__gif_get","parameters":[{"name":"tracking_id","in":"path","required":true,"schema":{"type":"string","title":"Tracking Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/notifications/opens":{"get":{"tags":["Tracking & Notifications"],"summary":"List Opens","description":"List this user's apply-smart sends with open status. Newest first.","operationId":"list_opens_api_notifications_opens_get","parameters":[{"name":"telegram_user_id","in":"query","required":true,"schema":{"type":"string","title":"Telegram User Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/notifications/opens/unseen-count":{"get":{"tags":["Tracking & Notifications"],"summary":"Unseen Count","description":"Bell-badge count: real opens (not prefetch) the user hasn't viewed yet.","operationId":"unseen_count_api_notifications_opens_unseen_count_get","parameters":[{"name":"telegram_user_id","in":"query","required":true,"schema":{"type":"string","title":"Telegram User Id"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/notifications/opens/seen":{"post":{"tags":["Tracking & Notifications"],"summary":"Mark Seen","description":"Clear the badge - called when the user opens the bell dropdown.","operationId":"mark_seen_api_notifications_opens_seen_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_mark_seen_api_notifications_opens_seen_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/notifications/{tracking_id}/email":{"get":{"tags":["Tracking & Notifications"],"summary":"View Sent Email","description":"Return the exact email content sent for this tracking row, rendered\nfrom our DB - never from Gmail. Viewing here cannot fire the pixel.","operationId":"view_sent_email_api_notifications__tracking_id__email_get","parameters":[{"name":"tracking_id","in":"path","required":true,"schema":{"type":"string","title":"Tracking Id"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/notifications/{tracking_id}/follow-up/draft":{"post":{"tags":["Tracking & Notifications"],"summary":"Follow Up Draft","description":"Generate (but do NOT send) a follow-up email draft. No token cost.\nReturns subject (already 'Re: ...') + body. The client renders both into\nan editable confirm modal; the actual send is a separate request.","operationId":"follow_up_draft_api_notifications__tracking_id__follow_up_draft_post","parameters":[{"name":"tracking_id","in":"path","required":true,"schema":{"type":"string","title":"Tracking Id"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_follow_up_draft_api_notifications__tracking_id__follow_up_draft_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/notifications/{tracking_id}/follow-up/send":{"post":{"tags":["Tracking & Notifications"],"summary":"Follow Up Send","description":"Send the (possibly edited) follow-up email. Deducts 2 tokens.\nStamps email_opens.follow_up_sent_at on success so the button is gone.","operationId":"follow_up_send_api_notifications__tracking_id__follow_up_send_post","parameters":[{"name":"tracking_id","in":"path","required":true,"schema":{"type":"string","title":"Tracking Id"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_follow_up_send_api_notifications__tracking_id__follow_up_send_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/success-stories":{"post":{"tags":["Success Stories"],"summary":"Submit Story","description":"Create or replace the caller's story. Always resets status to 'pending'.","operationId":"submit_story_api_success_stories_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorySubmit"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Success Stories"],"summary":"List Public Stories","description":"Public landing-page feed. Only approved + opted-in stories.\nNo auth required - the data exposed is what the user explicitly opted to\npublish, never raw emails/uids.","operationId":"list_public_stories_api_success_stories_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/success-stories/me":{"get":{"tags":["Success Stories"],"summary":"Get My Story","operationId":"get_my_story_api_success_stories_me_get","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"Get My Story"},"delete":{"tags":["Success Stories"],"summary":"Delete My Story","operationId":"delete_my_story_api_success_stories_me_delete","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"Delete My Story"}},"/db/api/success-stories":{"get":{"tags":["Success Stories"],"summary":"Admin List Stories","operationId":"admin_list_stories_db_api_success_stories_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(pending|approved|rejected)$"},{"type":"null"}],"title":"Status"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":200,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"Admin List Stories"}},"/db/api/success-stories/{story_id}/approve":{"post":{"tags":["Success Stories"],"summary":"Admin Approve Story","operationId":"admin_approve_story_db_api_success_stories__story_id__approve_post","parameters":[{"name":"story_id","in":"path","required":true,"schema":{"type":"string","title":"Story Id"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationAction","default":{}}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"Admin Approve Story"}},"/db/api/success-stories/{story_id}/reject":{"post":{"tags":["Success Stories"],"summary":"Admin Reject Story","operationId":"admin_reject_story_db_api_success_stories__story_id__reject_post","parameters":[{"name":"story_id","in":"path","required":true,"schema":{"type":"string","title":"Story Id"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModerationAction","default":{}}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"Admin Reject Story"}},"/api/payments/create-order":{"post":{"tags":["Payments"],"summary":"Create Order","operationId":"create_order_api_payments_create_order_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"Create Order"}},"/api/payments/verify":{"post":{"tags":["Payments"],"summary":"Verify Payment","operationId":"verify_payment_api_payments_verify_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"Verify Payment"}},"/api/payments/webhook":{"post":{"tags":["Payments"],"summary":"Razorpay Webhook","description":"Razorpay server-to-server webhook. Verified via X-Razorpay-Signature =\nHMAC-SHA256(raw_body, webhook_secret). No user auth; rate-limit exempt so a\nburst of provider callbacks is never dropped.","operationId":"razorpay_webhook_api_payments_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/outreach/templates":{"get":{"tags":["Auto-Apply"],"summary":"Outreach Templates","description":"Active writing styles + their (admin-managed) credit cost.","operationId":"outreach_templates_api_outreach_templates_get","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/outreach/contacts":{"get":{"tags":["Auto-Apply"],"summary":"Outreach Contacts","description":"Approved contacts (with approved company), filtered + paginated.","operationId":"outreach_contacts_api_outreach_contacts_get","parameters":[{"name":"role","in":"query","required":false,"schema":{"type":"string","default":"","title":"Role"}},{"name":"domain","in":"query","required":false,"schema":{"type":"string","default":"","title":"Domain"}},{"name":"city","in":"query","required":false,"schema":{"type":"string","default":"","title":"City"}},{"name":"q","in":"query","required":false,"schema":{"type":"string","default":"","title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/outreach/preview":{"post":{"tags":["Auto-Apply"],"summary":"Outreach Preview","description":"Run the AI loop on one contact and return the drafted email. No send/charge.","operationId":"outreach_preview_api_outreach_preview_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewReq"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/outreach/quota":{"get":{"tags":["Auto-Apply"],"summary":"Outreach Quota","operationId":"outreach_quota_api_outreach_quota_get","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"Outreach Quota"}},"/api/outreach/batches":{"post":{"tags":["Auto-Apply"],"summary":"Create Batch","operationId":"create_batch_api_outreach_batches_post","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchReq"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"Create Batch"},"get":{"tags":["Auto-Apply"],"summary":"List Batches","description":"Recent batches for the user, each with per-status counts.","operationId":"list_batches_api_outreach_batches_get","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/outreach/batches/{batch_id}":{"get":{"tags":["Auto-Apply"],"summary":"Get Batch","operationId":"get_batch_api_outreach_batches__batch_id__get","parameters":[{"name":"batch_id","in":"path","required":true,"schema":{"type":"string","title":"Batch Id"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"Get Batch"}},"/api/outreach/batches/{batch_id}/cancel":{"post":{"tags":["Auto-Apply"],"summary":"Cancel Batch","operationId":"cancel_batch_api_outreach_batches__batch_id__cancel_post","parameters":[{"name":"batch_id","in":"path","required":true,"schema":{"type":"string","title":"Batch Id"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"Cancel Batch"}},"/api/outreach/optout":{"get":{"tags":["Auto-Apply"],"summary":"Optout","operationId":"optout_api_outreach_optout_get","parameters":[{"name":"t","in":"query","required":false,"schema":{"type":"string","default":"","title":"T"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"description":"Optout"}},"/api/dashboard/summary":{"get":{"tags":["Dashboard"],"summary":"Dashboard Summary","description":"Stat-card counts for the signed-in user.","operationId":"dashboard_summary_api_dashboard_summary_get","parameters":[{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/dashboard/applications":{"get":{"tags":["Dashboard"],"summary":"Dashboard Applications","description":"Paginated list of the user's sent applications, newest first.\n\npage/per_page are validated and bounded by FastAPI (per_page <= 50) so a\ncaller cannot request an unbounded page.","operationId":"dashboard_applications_api_dashboard_applications_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Per Page"}},{"name":"X-User-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-User-Id"}},{"name":"X-Bot-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Bot-Token"}},{"name":"rmcp_session","in":"cookie","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rmcp Session"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"BatchReq":{"properties":{"contact_ids":{"items":{"type":"string"},"type":"array","title":"Contact Ids"},"resume_slot":{"type":"string","title":"Resume Slot","default":"master"},"template_key":{"type":"string","title":"Template Key","default":"standard"},"target_role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Role"},"target_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Domain"}},"type":"object","required":["contact_ids"],"title":"BatchReq"},"Body_apply_smart_api_apply_smart_post":{"properties":{"telegram_user_id":{"type":"string","title":"Telegram User Id"},"jd_text":{"type":"string","title":"Jd Text"},"recipient_email":{"type":"string","title":"Recipient Email"},"job_title":{"type":"string","title":"Job Title","default":""},"company_name":{"type":"string","title":"Company Name","default":""},"resume_file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"Resume File"},"resume_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resume Text"},"template":{"type":"string","title":"Template","default":"classic"},"email_subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email Subject"},"email_body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email Body"},"source_slot":{"type":"string","title":"Source Slot","default":"master"},"confirm_mismatch":{"type":"boolean","title":"Confirm Mismatch","default":false}},"type":"object","required":["telegram_user_id","jd_text","recipient_email"],"title":"Body_apply_smart_api_apply_smart_post"},"Body_customize_resume_api_customize_resume_post":{"properties":{"jd_file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"Jd File"},"jd_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jd Text"},"user_details":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Details"},"filename":{"type":"string","title":"Filename","default":"customized_resume"}},"type":"object","title":"Body_customize_resume_api_customize_resume_post"},"Body_draft_email_api_draft_email_post":{"properties":{"telegram_user_id":{"type":"string","title":"Telegram User Id"},"jd_text":{"type":"string","title":"Jd Text"},"job_title":{"type":"string","title":"Job Title","default":""},"company_name":{"type":"string","title":"Company Name","default":""},"resume_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resume Text"},"source_slot":{"type":"string","title":"Source Slot","default":"master"},"style":{"type":"string","title":"Style","default":"default"}},"type":"object","required":["telegram_user_id","jd_text"],"title":"Body_draft_email_api_draft_email_post"},"Body_extract_jd_details_api_extract_jd_details_post":{"properties":{"jd_file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"Jd File"},"jd_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jd Text"}},"type":"object","title":"Body_extract_jd_details_api_extract_jd_details_post"},"Body_follow_up_draft_api_notifications__tracking_id__follow_up_draft_post":{"properties":{"style":{"type":"string","title":"Style","default":"status_check"}},"type":"object","title":"Body_follow_up_draft_api_notifications__tracking_id__follow_up_draft_post"},"Body_follow_up_send_api_notifications__tracking_id__follow_up_send_post":{"properties":{"subject":{"type":"string","title":"Subject"},"body":{"type":"string","title":"Body"}},"type":"object","required":["subject","body"],"title":"Body_follow_up_send_api_notifications__tracking_id__follow_up_send_post"},"Body_mark_seen_api_notifications_opens_seen_post":{"properties":{"telegram_user_id":{"type":"string","title":"Telegram User Id"}},"type":"object","required":["telegram_user_id"],"title":"Body_mark_seen_api_notifications_opens_seen_post"},"Body_parse_jd_api_parse_jd_post":{"properties":{"jd_file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"Jd File"},"jd_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jd Text"}},"type":"object","title":"Body_parse_jd_api_parse_jd_post"},"Body_parse_resume_form_api_v2_parse_resume_form_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_parse_resume_form_api_v2_parse_resume_form_post"},"Body_parse_resume_pdf_api_parse_resume_pdf_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_parse_resume_pdf_api_parse_resume_pdf_post"},"Body_submit_report_api_reports_post":{"properties":{"email":{"type":"string","title":"Email"},"name":{"type":"string","title":"Name"},"report_type":{"type":"string","title":"Report Type"},"description":{"type":"string","title":"Description"},"image":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"Image"}},"type":"object","required":["email","name","report_type","description"],"title":"Body_submit_report_api_reports_post"},"Body_tailor_smart_api_tailor_smart_post":{"properties":{"jd_text":{"type":"string","title":"Jd Text"},"user_id":{"type":"string","title":"User Id"},"resume_file":{"anyOf":[{"type":"string","contentMediaType":"application/octet-stream"},{"type":"null"}],"title":"Resume File"},"resume_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resume Text"},"filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filename"},"custom_prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Custom Prompt"},"template":{"type":"string","title":"Template","default":"classic"}},"type":"object","required":["jd_text","user_id"],"title":"Body_tailor_smart_api_tailor_smart_post"},"Body_upload_custom_resume_api_custom_resume_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"}},"type":"object","required":["file"],"title":"Body_upload_custom_resume_api_custom_resume_post"},"CreateOrderIn":{"properties":{"plan_id":{"type":"string","title":"Plan Id"}},"type":"object","required":["plan_id"],"title":"CreateOrderIn"},"CreateResumeV2Request":{"properties":{"user_details_text":{"type":"string","title":"User Details Text"},"user_id":{"type":"string","title":"User Id"},"custom_prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Custom Prompt"},"template":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Template","default":"classic"},"filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filename"},"slot":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Slot","default":"master"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"}},"type":"object","required":["user_details_text","user_id"],"title":"CreateResumeV2Request"},"EnhanceBulletsRequest":{"properties":{"job_title":{"type":"string","title":"Job Title"},"industry":{"type":"string","title":"Industry"},"current_bullet":{"type":"string","title":"Current Bullet"},"exclude_verbs":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Exclude Verbs"}},"type":"object","required":["job_title","industry","current_bullet"],"title":"EnhanceBulletsRequest"},"GeneratePDFRequest":{"properties":{"latex_code":{"type":"string","title":"Latex Code"},"filename":{"type":"string","title":"Filename","default":"resume"}},"type":"object","required":["latex_code"],"title":"GeneratePDFRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"JdMatchCheckRequest":{"properties":{"user_id":{"type":"string","title":"User Id"},"jd_text":{"type":"string","title":"Jd Text"},"source_slot":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Slot","default":"master"},"resume_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resume Text"}},"type":"object","required":["user_id","jd_text"],"title":"JdMatchCheckRequest"},"ModerationAction":{"properties":{"admin_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Admin Note"}},"type":"object","title":"ModerationAction"},"PreviewReq":{"properties":{"contact_id":{"type":"string","title":"Contact Id"},"resume_slot":{"type":"string","title":"Resume Slot","default":"master"},"template_key":{"type":"string","title":"Template Key","default":"standard"},"target_role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Role"},"target_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Domain"}},"type":"object","required":["contact_id"],"title":"PreviewReq"},"RenameCustomRequest":{"properties":{"new_filename":{"type":"string","title":"New Filename"}},"type":"object","required":["new_filename"],"title":"RenameCustomRequest"},"RenamePDFRequest":{"properties":{"current_filename":{"type":"string","title":"Current Filename"},"new_filename":{"type":"string","title":"New Filename"}},"type":"object","required":["current_filename","new_filename"],"title":"RenamePDFRequest"},"StorySubmit":{"properties":{"company":{"type":"string","maxLength":200,"minLength":1,"title":"Company"},"job_title":{"type":"string","maxLength":200,"minLength":1,"title":"Job Title"},"source":{"type":"string","title":"Source"},"outcome":{"type":"string","title":"Outcome"},"features_used":{"items":{"type":"string"},"type":"array","title":"Features Used"},"story":{"type":"string","maxLength":800,"minLength":50,"title":"Story"},"display_mode":{"type":"string","title":"Display Mode","default":"anonymous"},"show_on_public":{"type":"boolean","title":"Show On Public","default":false}},"type":"object","required":["company","job_title","source","outcome","story"],"title":"StorySubmit"},"TailorResumeV2Request":{"properties":{"resume_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resume Text"},"jd_text":{"type":"string","title":"Jd Text"},"user_id":{"type":"string","title":"User Id"},"custom_prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Custom Prompt"},"template":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Template","default":"classic"},"source_slot":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Slot","default":"master"}},"type":"object","required":["jd_text","user_id"],"title":"TailorResumeV2Request"},"UpdateResumeRequest":{"properties":{"user_id":{"type":"string","title":"User Id"},"update_instructions":{"type":"string","title":"Update Instructions"},"custom_prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Custom Prompt"},"filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filename"},"template":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Template","default":"classic"},"source_slot":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Slot","default":"master"}},"type":"object","required":["user_id","update_instructions"],"title":"UpdateResumeRequest"},"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"},"VerifyIn":{"properties":{"razorpay_order_id":{"type":"string","title":"Razorpay Order Id"},"razorpay_payment_id":{"type":"string","title":"Razorpay Payment Id"},"razorpay_signature":{"type":"string","title":"Razorpay Signature"}},"type":"object","required":["razorpay_order_id","razorpay_payment_id","razorpay_signature"],"title":"VerifyIn"},"WebAppInitRequest":{"properties":{"init_data":{"type":"string","title":"Init Data"}},"type":"object","required":["init_data"],"title":"WebAppInitRequest"},"app__routers__pdfs__PDFResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"message":{"type":"string","title":"Message"},"filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filename"}},"type":"object","required":["success","message"],"title":"PDFResponse"},"app__routers__resume__PDFResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"message":{"type":"string","title":"Message"},"filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filename"},"charged":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Charged"},"tokens_used":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Tokens Used"}},"type":"object","required":["success","message"],"title":"PDFResponse"}},"securitySchemes":{"HTTPBasic":{"type":"http","scheme":"basic"},"OAuth2":{"type":"oauth2","description":"Google OAuth. Request only the scopes your integration needs.","flows":{"authorizationCode":{"authorizationUrl":"https://api.applyjobfaster.com/auth/url","tokenUrl":"https://oauth2.googleapis.com/token","scopes":{"read:profile":"Read the signed-in user's profile, plan and credit balance.","read:resumes":"List the user's resume slots (master, tailored, custom) and metadata.","write:resumes":"Create, tailor and update the user's resumes.","read:pdfs":"List and download the user's generated PDF resumes.","write:pdfs":"Generate, rename and delete the user's PDF resumes.","apply:send":"Send a job-application email from the user's own connected Gmail.","read:usage":"Read the user's token-usage history and account statistics.","read:*":"Read-only access to all of the user's resources.","write:*":"Read and write access to all of the user's resources."}}}},"SessionCookie":{"type":"apiKey","in":"cookie","name":"rmcp_session","description":"HttpOnly session cookie set at OAuth completion (web app)."},"MCPToken":{"type":"apiKey","in":"header","name":"X-MCP-Token","description":"Shared MCP access token for the /mcp server (also accepted as ?api_key=)."}}},"servers":[{"url":"https://api.applyjobfaster.com","description":"Production"}],"externalDocs":{"description":"Developer portal","url":"https://applyjobfaster.com/developers"}}