{"openapi":"3.1.0","info":{"title":"Talent Augmentation OS","description":"Personalised AI augmentation: hosted edition","version":"1.0.0"},"paths":{"/learn":{"get":{"summary":"Learn Index","operationId":"learn_index_learn_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/learn/{slug}":{"get":{"summary":"Learn Article","operationId":"learn_article_learn__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/quiz":{"get":{"summary":" Quiz","operationId":"_quiz_quiz_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/quiz/r/{key}":{"get":{"summary":" Result","operationId":"_result_quiz_r__key__get","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}},{"name":"you","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"You"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/config":{"get":{"summary":"Billing Config","description":"Client bootstrap for the pricing page.\n\n`provider` is what the page branches on: Polar redirects to a hosted\ncheckout, Paddle opens an overlay. No secrets: product ids are public.","operationId":"billing_config_api_billing_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/billing/health":{"get":{"summary":"Billing Health","description":"Is our billing credential actually able to open a checkout?\n\nPublic and cached: it exposes only whether OUR OWN credential works, which\nanyone can already discover by clicking \"start trial\", and the cache keeps\nan unauthenticated caller from turning this into an outbound-request\namplifier. The daily canary reads it with a plain GET.","operationId":"billing_health_api_billing_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/billing/checkout":{"post":{"summary":"Billing Checkout","description":"Create a Polar checkout session and hand back the URL to redirect to.\n\nSame contract as the Paddle route (plan, cycle, consent, seats) so the\npricing page's validation and the consent audit are provider-independent.","operationId":"billing_checkout_api_billing_checkout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/billing/webhook":{"post":{"summary":"Polar Webhook","operationId":"polar_webhook_api_billing_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/billing/refund-request":{"post":{"summary":"Refund Request","description":"Self-serve refund against the latest paid order (TERMS §3.1).\n\nZero usage since the charge → automatic full refund. Any usage → audited\nhuman review. Identical promise and identical usage test as the Paddle path;\nonly the provider calls differ.","operationId":"refund_request_api_billing_refund_request_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/account/billing":{"get":{"summary":"Billing Portal","description":"Redirect into Polar's hosted customer portal.\n\nKeyed on external_customer_id (our user id), so we never have to store\nPolar's customer UUID. The portal is the only PCI-safe place a customer can\nupdate a failing card, and Polar cannot disable it.","operationId":"billing_portal_account_billing_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/connector":{"get":{"summary":"Connector Sign In","description":"Where the connector install lands (from TALOAuthProvider.authorize).\n\nIt used to send the browser straight to Google, which left no way in\nfor anyone without a Google account. Deliberately never auto-approves\nfrom an existing web session: any client can register itself, so the\nperson must act on this page before their account is handed over.","operationId":"connector_sign_in_auth_connector_get","parameters":[{"name":"mcp_state","in":"query","required":false,"schema":{"type":"string","default":"","title":"Mcp State"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/email/start":{"post":{"summary":"Email Start","operationId":"email_start_auth_email_start_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_email_start_auth_email_start_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/email/verify":{"get":{"summary":"Email Verify Page","operationId":"email_verify_page_auth_email_verify_get","parameters":[{"name":"resent","in":"query","required":false,"schema":{"type":"string","default":"","title":"Resent"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"summary":"Email Verify","operationId":"email_verify_auth_email_verify_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_email_verify_auth_email_verify_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/reviewer":{"post":{"summary":"Reviewer Sign In","description":"Username and password for the app-review demo account only.\n\nIt can only finish a connector handshake: it never sets a web session,\nso the password opens an MCP token for one comped demo user and nothing\non the website. 404 unless REVIEWER_LOGIN_PASSWORD is set.","operationId":"reviewer_sign_in_auth_reviewer_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_reviewer_sign_in_auth_reviewer_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/":{"get":{"summary":"Landing","operationId":"landing__get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/privacy":{"get":{"summary":"Privacy","description":"Serve the privacy policy. Was a dead GitHub link before the repo went private.","operationId":"privacy_privacy_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/terms":{"get":{"summary":"Terms","description":"Serve the terms of service.","operationId":"terms_terms_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/demo":{"get":{"summary":"Demo","description":"Public 3-question scripted taster. No DB write, no LLM call.","operationId":"demo_demo_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/demo/coach":{"post":{"summary":"Api Demo Coach","description":"Anonymous demo coach. One LLM round-trip per request, hard caps.\n\nRate limit: 10 requests per IP per hour (slowapi decorator).\nInput cap: 2000 characters.\nOutput cap: 600 tokens.\n\nNo DB write, no auth required (this powers /demo Phase 2). If the LLM\nis unreachable, return a graceful fallback so the demo does not break.","operationId":"api_demo_coach_api_demo_coach_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/demo/profile":{"post":{"summary":"Api Demo Profile","description":"Anonymous demo snapshot. One LLM round-trip, hard caps.\n\nRate limit: 15 requests per IP per hour. Input cap: 400 chars per field.\nNo DB write, no auth (this powers /demo Phase 1). On any LLM or parse\nfailure the client falls back to echoing the raw answers, so the demo\nnever visibly breaks.","operationId":"api_demo_profile_api_demo_profile_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/contact":{"get":{"summary":"Contact","description":"Public feedback / contact form. ?topic= preselects the subject\n(product_feedback | bug | account_billing | enterprise_enquiry | other).","operationId":"contact_contact_get","parameters":[{"name":"topic","in":"query","required":false,"schema":{"type":"string","default":"","title":"Topic"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/contact":{"post":{"summary":"Api Contact","description":"Anonymous contact form submission.\n\nValidates server-side, sends to ``FEEDBACK_INBOX`` via SendGrid (or\nlogs in dev), and writes a ``lead.contact_submitted`` audit row.\nHoneypot field ``website`` silently 200s without sending.","operationId":"api_contact_api_contact_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/teams":{"get":{"summary":"Teams Page","description":"The team lead page: where LinkedIn ads and founder outreach land.","operationId":"teams_page_teams_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/teams/engineering":{"get":{"summary":"Teams Engineering Page","description":"The engineering team page: the organic October engineering track.","operationId":"teams_engineering_page_teams_engineering_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/pricing":{"get":{"summary":"Pricing","description":"Public pricing page. Renders regardless of ENABLE_BILLING (it is\nmarketing); the live checkout button only appears when billing is on,\notherwise the CTA starts the free trial.\n\ntrial_ended drives the stranded-user notice: the lifecycle emails and\nthe read-only 402 all point lapsed users here, and with billing off the\npage would otherwise offer them nothing but a sign-in button they have\nalready used (funnel QA §4.6).","operationId":"pricing_pricing_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/login":{"get":{"summary":"Login","description":"Sign-in page (U3). This used to 302 straight into Google's consent\nscreen: no context, no trial expectations, no alternative explanation —\nand the perfectly good login.html sat unrendered. The OAuth kick-off\nnow lives at /auth/google.","operationId":"login_login_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/auth/google":{"get":{"summary":"Auth Google","operationId":"auth_google_auth_google_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/callback":{"get":{"summary":"Auth Callback","operationId":"auth_callback_auth_callback_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/logout":{"get":{"summary":"Logout","operationId":"logout_logout_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/account/export":{"get":{"summary":"Api Account Export","description":"Return every row of personal data we hold about the current user.","operationId":"api_account_export_api_account_export_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/account":{"delete":{"summary":"Api Account Delete","description":"Hard-delete the current user and every related row.\n\nForeign keys on profiles, assessment_sessions, chat_logs,\ncheckin_reminders, pilot_surveys, and oauth_tokens use ON DELETE\nCASCADE, so deleting the user row removes everything downstream.\nAudit log rows for this user keep ``actor_user_id = NULL`` (ON\nDELETE SET NULL) so the org-level audit trail stays intact for\nother admins, but the email field is cleared too.","operationId":"api_account_delete_api_account_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/cli-token":{"get":{"summary":"Cli Token Page","description":"Show the current session JWT so CLI tools (/talent-sync) can use it.","operationId":"cli_token_page_cli_token_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/auth/token":{"get":{"summary":"Api Auth Token","description":"Return the currently-authenticated session JWT as JSON for CLI pickup.","operationId":"api_auth_token_api_auth_token_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/assess":{"get":{"summary":"Assess Page","description":"The assessment used to do exactly one thing: start a new 15-minute run.\n\nTwo consequences, both bad. Someone who already had a profile was offered a\nfresh assessment with no mention of the profile they already own. And the\nassessment is long, so people abandon it midway (the pilot's P5 did, verbatim:\n\"actually, I'm low on time\") — and every answer they had given was silently\nstranded, because the session id lived only in browser memory even though the\nserver had been persisting the conversation all along.\n\nSo this route now answers three questions before rendering: do you have a\nprofile, do you have an unfinished assessment, and did you explicitly ask to\nstart over.","operationId":"assess_page_assess_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/assess/message":{"post":{"summary":"Assess Message","description":"Send a message in the assessment conversation, get LLM response.","operationId":"assess_message_api_assess_message_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/assess/complete":{"post":{"summary":"Assess Complete","description":"Finalize assessment: extract data from conversation, compute scores, save profile.","operationId":"assess_complete_api_assess_complete_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/dashboard":{"get":{"summary":"Dashboard","operationId":"dashboard_dashboard_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/profile/controls":{"post":{"summary":"Api Profile Controls","description":"Save coaching-controls changes (B53).\n\nStructured toggle edits don't need the model-composed diff flow, but they\nget the same guarantees: the controls live in the profile's Section-7\ncalibration YAML (so they travel with the portable profile and the\nengine reads them for free), every change appends a dated change-log\nrow, a NEW profile version is written (never in-place), and the\ninsights_sharing switch is mirrored to users.insights_opt_out so the\nB54 aggregate pool can filter without parsing profiles.","operationId":"api_profile_controls_api_profile_controls_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/insights/friction":{"get":{"summary":"Api Insights Friction","description":"Operator view of friction-taste aggregates (B54 research loop).\n\nk-anonymous per org_service's posture: overall counts only when >= 5\ndistinct users contributed; per-mode breakdown applies the same floor\nper key; individual rows are never returned; opted-out users are\nexcluded in the query, not post-hoc.","operationId":"api_insights_friction_api_admin_insights_friction_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/install":{"get":{"summary":"Install Page","description":"Authed install instructions (backlog N2).\n\nThe landing page's install tabs are unreachable once signed in (\"/\"\nredirects authed users straight to /dashboard), which left the funnel\nwith no connect step after login — the biggest pricing-first dead end in\nthe 2026-07-07 funnel QA. Anonymous visitors go to the landing section.","operationId":"install_page_install_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/account":{"get":{"summary":"Account Root","description":"`/account` used to 404.\n\nBilling deliberately lives in Polar's hosted portal (card updates have to\nanyway: we are not taking card details on our own servers), so this is not a\npage, it is a signpost. But a bare 404 on the most guessable route in the\nproduct is its own bug, and lifecycle emails point people at their account.","operationId":"account_root_account_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/account/cancel":{"get":{"summary":"Account Cancel Page","operationId":"account_cancel_page_account_cancel_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}},"post":{"summary":"Account Cancel","description":"One-click cancellation (backlog B42 / TASKS E4, rebuilt in U1). No\ndark patterns: access runs to the end of the window, nothing is deleted.\n\nWhen a Paddle subscription exists (card-upfront trial or paid plan), the\ncancel MUST reach Paddle — the local flag alone never stopped the billing\nmachine, so \"cancelled\" users were still charged at trial end. If Paddle\nrefuses, we say so and do NOT tell the user they are cancelled.","operationId":"account_cancel_account_cancel_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/download/taos.mcpb":{"get":{"summary":"Download Desktop Extension","description":"Serve the Claude Desktop extension bundle (backlog B50).\n\nThe repo went private, which killed the public GitHub download path;\nthe hosted site is now the canonical distribution point for the .mcpb.","operationId":"download_desktop_extension_download_taos_mcpb_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/assess/quick":{"get":{"summary":"Quick Assess Form","operationId":"quick_assess_form_assess_quick_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/assess/quick":{"post":{"summary":"Api Assess Quick","description":"Create a starter profile from the 6-field quick form. Deterministic:\nno LLM in the loop, so this path cannot hang or hallucinate.","operationId":"api_assess_quick_api_assess_quick_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/reveal":{"get":{"summary":"Reveal","description":"Post-assessment teaser: show the headline score, lock the rest behind a\none-click free trial (no card). Only meaningful when ENABLE_TRIAL_GATE is\non; with it off (the default) anyone landing here goes to the dashboard.","operationId":"reveal_reveal_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/trial/start":{"post":{"summary":"Trial Start","description":"Begin a free trial for the current user. No payment; idempotent for\nactive/converted accounts.\n\nAn ENDED trial must not be restartable from here, or expiry is fiction:\nbefore N4, an expired user was bounced to /reveal whose button called\nthis endpoint and simply re-activated the trial — an infinite-trial\nloophole. Cancelled-within-window users may reactivate, but the clock\nis NOT reset (cancel + restart must not extend the window).\n\nCard-upfront mode (N7): this endpoint is retired — trials are Paddle\nsubscription trials started at checkout, so a no-card side door here\nwould defeat the whole design.","operationId":"trial_start_api_trial_start_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/admin":{"get":{"summary":"Admin Dashboard","description":"Org admin dashboard. Requires role admin or owner on the current user.","operationId":"admin_dashboard_admin_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/org/summary":{"get":{"summary":"Api Org Summary","description":"JSON feed for the admin dashboard (also useful for external tooling).","operationId":"api_org_summary_api_org_summary_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/admin/audit":{"get":{"summary":"Admin Audit View","description":"HTML view of recent audit events for the admin's organisation, with\nfiltering by action prefix, actor, and date range (backlog B9).","operationId":"admin_audit_view_admin_audit_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":200,"title":"Limit"}},{"name":"action","in":"query","required":false,"schema":{"type":"string","default":"","title":"Action"}},{"name":"actor_email","in":"query","required":false,"schema":{"type":"string","default":"","title":"Actor Email"}},{"name":"date_from","in":"query","required":false,"schema":{"type":"string","default":"","title":"Date From"}},{"name":"date_to","in":"query","required":false,"schema":{"type":"string","default":"","title":"Date To"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/audit.csv":{"get":{"summary":"Api Admin Audit Csv","description":"CSV export of audit events for the admin's organisation, last N days.","operationId":"api_admin_audit_csv_api_admin_audit_csv_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":90,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/invite":{"post":{"summary":"Api Admin Invite","description":"Admin creates an invite. Emails the invitee; returns the URL so the\nadmin can copy it manually if email delivery fails.","operationId":"api_admin_invite_api_admin_invite_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/invite/bulk":{"post":{"summary":"Api Admin Invite Bulk","description":"Bulk invite from CSV lines ('email,role' or just 'email' per line).\n\nA 200-seat pilot cannot be invited one at a time (backlog B7). Rows are\nprocessed independently: failures are reported per-row, never abort the\nbatch. Capped at 500 rows per call.","operationId":"api_admin_invite_bulk_api_admin_invite_bulk_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/invites":{"get":{"summary":"Api Admin List Invites","description":"List pending (not accepted, not revoked, not expired) invites for the admin's org.","operationId":"api_admin_list_invites_api_admin_invites_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/invite/{invite_id}/revoke":{"post":{"summary":"Api Admin Revoke Invite","operationId":"api_admin_revoke_invite_api_admin_invite__invite_id__revoke_post","parameters":[{"name":"invite_id","in":"path","required":true,"schema":{"type":"integer","title":"Invite Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/org/bootstrap":{"post":{"summary":"Api Org Bootstrap","description":"Create an organisation and attach an existing user as owner.\n\nPlatform-operator action: gated on FULL_ACCESS_EMAILS, not org-admin\nRBAC, because before the first org exists there are no org admins.\nBody: {\"name\": ..., \"slug\": ..., \"owner_email\": ...}","operationId":"api_org_bootstrap_api_admin_org_bootstrap_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/comp":{"post":{"summary":"Api Comp User","description":"Grant or revoke a comped (free, paid-equivalent) account.\n\nFor user testers and design partners. Platform-operator action, gated on\nFULL_ACCESS_EMAILS: comping someone must not be something a comped user can\ndo. The comp lives on the user row, so adding the next tester is one call,\nnot a commit and a redeploy, and no third party's email lands in the repo.\n\nBody: {\"email\": ..., \"on\": true|false}\n\nThe user must have signed in at least once (Google OAuth, web or connector)\nfor a row to exist. Revoking only clears a comp; it will not touch a real\nsubscription.","operationId":"api_comp_user_api_admin_comp_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/org/members/{user_id}":{"patch":{"summary":"Api Org Member Change Role","description":"Change a member's role. Owner involvement (either direction) needs an\nowner caller; the last owner can never be demoted. Body: {\"role\": ...}.","operationId":"api_org_member_change_role_api_admin_org_members__user_id__patch","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"Api Org Member Remove","description":"Remove a member from the organisation (account survives, org link and\nrole are cleared). Admin/owner targets need an owner caller.","operationId":"api_org_member_remove_api_admin_org_members__user_id__delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/invite/{token}":{"get":{"summary":"Accept Invite","description":"Accept an org invite. If not logged in, bounce through Google OAuth\nand return here. If the authed user's email matches the invite, join the\norg; otherwise show a mismatch page.","operationId":"accept_invite_invite__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/profile":{"get":{"summary":"Api Get Profile","description":"Get current profile as JSON.","operationId":"api_get_profile_api_profile_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/profile/export/{fmt}":{"get":{"summary":"Api Export Profile","description":"Export profile in various formats: markdown, json, chatgpt, claude, gemini.","operationId":"api_export_profile_api_profile_export__fmt__get","parameters":[{"name":"fmt","in":"path","required":true,"schema":{"type":"string","title":"Fmt"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/profile/update":{"get":{"summary":"Profile Update Page","description":"Render the quick-update form.\n\nWeb-app equivalent of the /talent-update MCP skill: 4-5 short questions,\nthe model folds answers into the existing profile, a new Profile version\nis saved. Sends users with no profile to /assess first.","operationId":"profile_update_page_profile_update_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}},"post":{"summary":"Profile Update Submit","description":"Fold the user's free-text update answers into a new profile version.","operationId":"profile_update_submit_profile_update_post","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/profile/update/confirm":{"post":{"summary":"Profile Update Confirm","description":"Apply a previewed profile revision after explicit user confirmation.","operationId":"profile_update_confirm_profile_update_confirm_post","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/profile/sync":{"post":{"summary":"Api Profile Sync","description":"Accept a PROFILE UPDATE BLOCK and apply it as a new profile version.","operationId":"api_profile_sync_api_profile_sync_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/profile/evolve":{"post":{"summary":"Api Profile Evolve","description":"Incrementally evolve a user's profile based on accumulated chat telemetry.\n\nCalled periodically (e.g. after N interactions) by the MCP server or a cron job.\nReads the latest profile + recent ChatLog entries, computes skill signal\ndeltas, and creates a new profile version with updated domain ratings.","operationId":"api_profile_evolve_api_profile_evolve_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/telemetry/chat-log":{"post":{"summary":"Api Ingest Chat Log","description":"Ingest a structured chat log entry from <tal_log> blocks.\n\nAccepts a JSON payload with telemetry fields and stores it in the\nChatLog table. Called by the MCP server after parsing <tal_log>.","operationId":"api_ingest_chat_log_api_telemetry_chat_log_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/telemetry/chat-log/batch":{"post":{"summary":"Api Ingest Chat Log Batch","description":"Ingest multiple chat log entries at once.","operationId":"api_ingest_chat_log_batch_api_telemetry_chat_log_batch_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/telemetry/passive-ratio":{"get":{"summary":"Api Passive Ratio","description":"Get the Engagement Passive Ratio (R_passive) for the current user.","operationId":"api_passive_ratio_api_telemetry_passive_ratio_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/pilot/survey":{"post":{"summary":"Api Submit Survey","description":"Submit or update pilot survey scores for a timepoint.","operationId":"api_submit_survey_api_pilot_survey_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/pilot/surveys":{"get":{"summary":"Api Get Surveys","description":"Get all pilot survey data for the current user.","operationId":"api_get_surveys_api_pilot_surveys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/checkin/{token}":{"get":{"summary":"Checkin Page","operationId":"checkin_page_checkin__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/checkin/{token}":{"post":{"summary":"Api Submit Checkin","operationId":"api_submit_checkin_api_checkin__token__post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/.well-known/oauth-protected-resource":{"get":{"summary":"Oauth Protected Resource Metadata","description":"RFC 9728: which authorization server protects the /mcp resource.","operationId":"oauth_protected_resource_metadata__well_known_oauth_protected_resource_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/oauth-protected-resource/mcp":{"get":{"summary":"Oauth Protected Resource Metadata","description":"RFC 9728: which authorization server protects the /mcp resource.","operationId":"oauth_protected_resource_metadata__well_known_oauth_protected_resource_mcp_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/oauth-authorization-server":{"get":{"summary":"Oauth Authorization Server Metadata","description":"RFC 8414 root-level metadata (aliases of the sub-app's copy).\n\nSame content as /mcp/.well-known/oauth-authorization-server, built from\nthe same options object, so the copies cannot drift. Serving both the\npath-inserted and root forms covers every client fallback order observed\nin the wild (Claude Code, claude.ai, ChatGPT connectors).","operationId":"oauth_authorization_server_metadata__well_known_oauth_authorization_server_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/oauth-authorization-server/mcp":{"get":{"summary":"Oauth Authorization Server Metadata","description":"RFC 8414 root-level metadata (aliases of the sub-app's copy).\n\nSame content as /mcp/.well-known/oauth-authorization-server, built from\nthe same options object, so the copies cannot drift. Serving both the\npath-inserted and root forms covers every client fallback order observed\nin the wild (Claude Code, claude.ai, ChatGPT connectors).","operationId":"oauth_authorization_server_metadata__well_known_oauth_authorization_server_mcp_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"Body_email_start_auth_email_start_post":{"properties":{"email":{"type":"string","title":"Email","default":""},"mcp_state":{"type":"string","title":"Mcp State","default":""},"website":{"type":"string","title":"Website","default":""}},"type":"object","title":"Body_email_start_auth_email_start_post"},"Body_email_verify_auth_email_verify_post":{"properties":{"code":{"type":"string","title":"Code","default":""}},"type":"object","title":"Body_email_verify_auth_email_verify_post"},"Body_reviewer_sign_in_auth_reviewer_post":{"properties":{"email":{"type":"string","title":"Email","default":""},"password":{"type":"string","title":"Password","default":""},"mcp_state":{"type":"string","title":"Mcp State","default":""}},"type":"object","title":"Body_reviewer_sign_in_auth_reviewer_post"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"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"}}}}