{"name":"UnlikeOtherAuthenticator","description":"Centralized OAuth and authentication service used by multiple products.","version":"0.0.0","repository":"https://github.com/UnlikeOtherAI/UnlikeOtherAuthenticator","home":"/","docs":"/llm","api":"/api","config_jwt":{"description":"The config JWT is a signed JWT containing all client-specific settings. The payload is the config. The signature must be RS256 and the protected header must include kid.","signing":{"algorithms":["RS256"],"key_selection":"The JWT protected header must include kid. The auth service resolves that kid from CONFIG_JWKS_URL and rejects tokens without kid."},"required_fields":{"domain":"string — client domain. This must exactly match the hostname of the HTTPS config_url when the auth service fetches the JWT.","redirect_urls":"string[] — non-empty list of absolute HTTP/HTTPS callback URLs. The runtime redirect_url must match an entry byte-for-byte, including scheme, host, port, path, AND query string. No normalization, no prefix matching, no query wildcards. Do NOT append per-request state (?state=…) to the redirect — carry it out-of-band via sessionStorage or a first-party cookie.","enabled_auth_methods":"string[] — non-empty list. Supported values are email_password, google, facebook, github, linkedin, apple. Social provider names here are both enabled and allowed.","language_config":"string | string[] — either one language code or a non-empty array of language codes.","ui_theme":{"description":"object — every auth page style comes from ui_theme. This object is required and the main source of integration mistakes.","required_sections":{"colors":{"required_keys":["bg","surface","text","muted","primary","primary_text","border","danger","danger_text"],"value_format":"hex color only (#RGB, #RGBA, #RRGGBB, #RRGGBBAA) or transparent"},"radii":{"required_keys":["card","button","input"],"value_format":"CSS length string: px, rem, em, %, or 0"},"density":"compact | comfortable | spacious","typography":{"required_keys":["font_family","base_text_size"],"font_family":"Preset values like sans, serif, mono are valid. Custom font names must match /^[A-Za-z0-9 _-]+$/.","base_text_size":"sm | md | lg","font_import_url":"optional HTTPS stylesheet URL. The Auth UI silently drops font imports outside fonts.googleapis.com, fonts.gstatic.com, and fonts.bunny.net."},"button":{"required_keys":["style"],"style":"solid | outline | ghost"},"card":{"required_keys":["style"],"style":"plain | bordered | shadow"},"logo":{"required_keys":["url","alt"],"url":"HTTPS URL on the same host as config.domain, or empty string","alt":"required non-empty string","text":"optional text logo, max 100 chars, used when url is empty","font_size":"optional CSS length string","color":"optional hex color","style":"optional flat object. The Auth UI applies only color, fontSize, fontWeight, and letterSpacing."}},"optional_sections":{"css_vars":"Record<string, string> — optional advanced CSS variable overrides."},"common_failures":["Missing one of the required ui_theme sections such as colors, radii, button, card, typography, or logo.","Using non-hex colors like rgb(...), hsl(...), or named colors.","Using bare numbers for radii/font sizes instead of CSS length strings like 12px or 1rem.","Providing a text logo without logo.alt.","Forgetting button.style or card.style."],"example":{"colors":{"bg":"#f8fafc","surface":"#ffffff","text":"#0f172a","muted":"#475569","primary":"#2563eb","primary_text":"#ffffff","border":"#e2e8f0","danger":"#dc2626","danger_text":"#ffffff"},"radii":{"card":"16px","button":"12px","input":"12px"},"density":"comfortable","typography":{"font_family":"sans","base_text_size":"md"},"button":{"style":"solid"},"card":{"style":"bordered"},"logo":{"url":"","alt":"Client logo","text":"Client","font_size":"24px","color":"#0f172a","style":{"fontWeight":"800","letterSpacing":"0.02em"}}}}},"optional_fields":{"2fa_enabled":"boolean (default false)","debug_enabled":"boolean (default false)","user_scope":"\"global\" | \"per_domain\" (default \"global\")","allow_registration":"boolean (default true)","registration_mode":"\"password_required\" | \"passwordless\" (default \"password_required\")","allowed_registration_domains":"string[] — lowercase email domains allowed to register","registration_domain_mapping":"array of { email_domain, org_id, team_id? } — email-domain-based org/team placement","language":"string — currently selected language override","session":{"remember_me_enabled":"boolean (default true)","remember_me_default":"boolean (default true)","short_refresh_token_ttl_hours":"number (1-168, default 1)","long_refresh_token_ttl_days":"number (1-90, default 30)","access_token_ttl_minutes":"number (15-60)"},"org_features":{"enabled":"boolean (default false)","groups_enabled":"boolean (default false)","user_needs_team":"boolean (default false)","auto_create_personal_org_on_first_login":"boolean (default false) — create a personal org + default team on first verified login when no mapping matches","allow_user_create_org":"boolean (default false) — permit end-users to call POST /org/organisations with their own access token (superusers bypass)","pending_invites_block_auto_create":"boolean (default true) — a pending invite for the user email suppresses auto_create_personal_org_on_first_login","max_teams_per_org":"number (default 100, max 1000)","max_groups_per_org":"number (default 20, max 200)","max_members_per_org":"number (default 1000, max 10000)","max_members_per_team":"number (default 200, max 5000)","max_members_per_group":"number (default 500, max 5000)","max_team_memberships_per_user":"number (default 50, max 200)","org_roles":"string[] (default [\"owner\", \"admin\", \"member\"]). Must include \"owner\".","max_flags_per_app":"number (default 100, max 500)","scim_override_retention":"\"retain\" | \"clear\" (default \"retain\")","global_missing_flag_default":"\"enabled\" | \"disabled\" (default \"disabled\")"},"access_requests":{"enabled":"boolean (default false)","target_org_id":"string (required when enabled=true)","target_team_id":"string (required when enabled=true)","auto_grant_domains":"string[]","notify_org_roles":"string[] (default [\"owner\", \"admin\"])","admin_review_url":"absolute URL"}},"example_payload":{"domain":"client.example.com","redirect_urls":["https://client.example.com/oauth/callback"],"enabled_auth_methods":["email_password","google"],"ui_theme":{"colors":{"bg":"#f8fafc","surface":"#ffffff","text":"#0f172a","muted":"#475569","primary":"#2563eb","primary_text":"#ffffff","border":"#e2e8f0","danger":"#dc2626","danger_text":"#ffffff"},"radii":{"card":"16px","button":"12px","input":"12px"},"density":"comfortable","typography":{"font_family":"sans","base_text_size":"md"},"button":{"style":"solid"},"card":{"style":"bordered"},"logo":{"url":"","alt":"Client logo","text":"Client","font_size":"24px","color":"#0f172a","style":{"fontWeight":"800","letterSpacing":"0.02em"}}},"language_config":"en","debug_enabled":true,"allow_registration":true,"registration_mode":"password_required","user_scope":"global"}},"access_token":{"description":"The access_token returned by POST /auth/token is an HS256 JWT signed with the deployment SHARED_SECRET. Relying parties cannot and should not verify it cryptographically — trust derives from the authenticated backend channel (domain-hash bearer + PKCE). When an RP needs to validate a presented access token later, it should call UOA (e.g. GET /org/me) rather than attempting local verification.","signing":{"algorithm":"HS256","key":"deployment-wide SHARED_SECRET (not exposed, no UOA-side public JWKS)","audience":"uoa:access-token"},"claims":{"sub":"string — stable external user id. Primary foreign key for the RP into the UOA user.","email":"string — user primary email. Advisory (user may change it); sub is the stable identity.","role":"string — UOA platform role, \"user\" or \"superuser\". Gates UOA admin surfaces only. DO NOT use for RP authorization; honour firstLogin.memberships.orgs[].role (or GET /org/me) instead.","domain":"string — integration domain from the config JWT. Confirms which integration minted this token.","client_id":"string — SHA256(domain + clientSecret) hex. Identifies the exact client credential used.","org":"object | absent — present only when org_features.enabled and the user has an org on this domain. Shape: { org_id, org_role, teams[], team_roles{}, groups?[], group_admin?[] }.","iss":"string — UOA host (e.g. authentication.unlikeotherai.com).","aud":"string — always \"uoa:access-token\".","iat":"number — issued at, epoch seconds.","exp":"number — expiry, epoch seconds."},"rp_guidance":["Do NOT read response.user — there is no top-level user field. Decode access_token and read claims.sub.","Do NOT verify access_token against the /.well-known/jwks.json JWKS; that JWKS is for RS256 config JWTs only.","Do NOT use claims.role for RP authorization; it is the UOA platform role. Use firstLogin.memberships.orgs[].role (or GET /org/me).","Do NOT fall back to a synthetic tenant when firstLogin.memberships.orgs is empty — branch on firstLogin.capabilities (can_create_org / can_accept_invite). See /llm Phase 4.5.","Keep access_token server-side when possible. If forwarded to UOA-owned endpoints, use the X-UOA-Access-Token header."],"response_envelope_casing":"The /auth/token response envelope is snake_case (access_token, refresh_token, expires_in, refresh_token_expires_in, token_type). The key firstLogin is camelCase; memberships.orgs[].orgId, memberships.teams[].teamId, memberships.teams[].orgId, and pending_invites[].inviteId/orgId/teamId/teamName are camelCase. pending_invites and capabilities.can_* remain snake_case."},"config_validation":{"path":"/config/validate","method":"POST","description":"Production-safe configuration validation endpoint for raw config JSON, a signed config JWT, or a config_url fetch target. It reports schema, signature, config_url/domain, runtime-policy, and optional customization guidance.","auth":"Public, IP rate limited. Uses the deployment CONFIG_JWKS_URL for config JWT signature verification.","body":{"config":"object (optional) — raw config payload to schema-validate directly. This skips JWT signature checking unless config_jwt or config_url is also supplied instead.","config_jwt":"string (optional) — signed config JWT to decode, inspect, schema-validate, and verify with a JWKS.","config_url":"string (optional) — URL that should return the signed config JWT. The endpoint fetches it and then runs the same checks."},"source_priority":["config","config_jwt","config_url"],"response":{"ok":"boolean — true when every executed check passed","schema_valid":"boolean","jwt_signature_valid":"boolean | null — null when signature checking was skipped","domain_match":"boolean | null — null when config_url was not part of the request or schema parsing failed","checks":"object — per-stage results for source, fetch, decode, secret_scan, signature, schema, runtime_policy, and domain_match","issues":"array — structured stage-specific failures and warnings","recommendations":"array — required next steps, operational notes, and optional customization guidance","config_summary":"object | null — safe summary of the parsed config when schema validation succeeds"}},"config_verification":{"path":"/config/verify","method":"POST","description":"Non-production DEBUG_ENABLED-only debug endpoint that validates raw config JSON, a signed config JWT, or a config_url fetch target. It also accepts a jwks_url override for local setup debugging.","auth":"Available only when DEBUG_ENABLED=true and NODE_ENV is not production; IP rate limited.","body":{"config":"object (optional) — raw config payload to schema-validate directly. This skips JWT signature checking unless config_jwt or config_url is also supplied instead.","config_jwt":"string (optional) — signed config JWT to decode, inspect, schema-validate, and verify with a JWKS.","config_url":"string (optional) — URL that should return the signed config JWT. The endpoint fetches it and then runs the same checks.","jwks_url":"string (optional) — JWKS URL used to verify config_jwt or the JWT fetched from config_url. Defaults to CONFIG_JWKS_URL"},"source_priority":["config","config_jwt","config_url"],"response":{"ok":"boolean — true when every executed check passed","schema_valid":"boolean","jwt_signature_valid":"boolean | null — null when signature checking was skipped","domain_match":"boolean | null — null when config_url was not part of the request or schema parsing failed","checks":"object — per-stage results for source, fetch, decode, secret_scan, signature, schema, runtime_policy, and domain_match","issues":"array — structured stage-specific failures and warnings","recommendations":"array — required next steps, operational notes, and optional customization guidance","config_summary":"object | null — safe summary of the parsed config when schema validation succeeds"}},"endpoints":[{"method":"GET","path":"/","description":"Holding page linking to Admin, /llm, and /api"},{"method":"GET","path":"/api","description":"API information and full endpoint schema"},{"method":"GET","path":"/llm","description":"Markdown integration guide for LLMs and humans; links /api for JSON schema"},{"method":"GET","path":"/.well-known/jwks.json","description":"Public JWKS used to verify RS256 config JWT signatures","auth":"public","response":{"keys":"array — public RSA JWKs only; private key members are rejected at boot-time use"}},{"method":"GET","path":"/health","description":"Health check"},{"method":"GET","path":"/admin/*","description":"First-party UOA Admin CSR app served from the API origin","response":{"200":"Admin HTML shell or static asset"}},{"method":"POST","path":"/config/validate","description":"Production-safe configuration validation for raw config JSON, signed config JWTs, or config_url fetch targets","auth":"Public, IP rate limited; uses this deployment CONFIG_JWKS_URL for config JWT signature verification","body":{"config?":"object — raw config payload to schema-validate directly","config_jwt?":"string — signed config JWT to decode and validate","config_url?":"string — HTTPS URL that should return the signed config JWT"},"response":{"ok":"boolean — true when every executed validation and runtime-policy check passed","schema_valid":"boolean — true when the config payload matches the schema","jwt_signature_valid":"boolean|null — null when signature validation was skipped","domain_match":"boolean|null — null when config_url/domain matching was not applicable","checks":"object — stage-by-stage results for source, fetch, decode, secret_scan, signature, schema, runtime_policy, and domain_match","issues":"array — explicit validation/debug issues with stage, code, summary, and details","recommendations":"array — required next steps, operational notes, and optional customization guidance"}},{"method":"POST","path":"/config/verify","description":"Non-production DEBUG_ENABLED-only configuration validation for raw config JSON, config JWTs, or config_url fetch targets","auth":"Available only when DEBUG_ENABLED=true and NODE_ENV is not production; IP rate limited","body":{"config?":"object — raw config payload to schema-validate directly","config_jwt?":"string — signed config JWT to decode and validate","config_url?":"string — HTTPS URL that should return the signed config JWT","jwks_url?":"string — JWKS URL used to verify the RS256 JWT signature; defaults to CONFIG_JWKS_URL"},"response":{"ok":"boolean — true when every executed validation check passed","schema_valid":"boolean — true when the config payload matches the schema","jwt_signature_valid":"boolean|null — null when signature validation was skipped","domain_match":"boolean|null — null when config_url/domain matching was not applicable","checks":"object — stage-by-stage results for source, fetch, decode, signature, schema, and domain_match","issues":"array — explicit validation/debug issues with stage, code, summary, and details"}},{"method":"GET","path":"/auth","description":"OAuth entrypoint — renders the auth UI","query":{"config_url":"string (required) — HTTPS URL to fetch signed config JWT","redirect_url":"string (optional) — OAuth redirect URL override (redirect_uri also accepted)","code_challenge":"string (required for sign-in actions) — exactly 43-char PKCE S256 challenge","code_challenge_method":"\"S256\" when code_challenge is sent"}},{"method":"POST","path":"/auth/login","description":"Email/password login","auth":"config_url query param","query":{"redirect_url":"string (optional, redirect_uri also accepted)","code_challenge":"string (required) — exactly 43-char PKCE S256 challenge","code_challenge_method":"\"S256\" (required)","request_access":"string (optional) — when truthy, auto-grant or create a pending access request"},"body":{"email":"string (required)","password":"string (required)","remember_me":"boolean (optional) — defaults to session.remember_me_default from config"},"response":{"ok":"true","code":"authorization code","redirect_to":"full redirect URL with code","twofa_required":"true (only if 2FA needed)","twofa_token":"challenge token (only if 2FA needed)","access_request_status":"\"pending\" when request_access created a pending access request"}},{"method":"POST","path":"/auth/register","description":"User registration — sends verification email","auth":"config_url query param","query":{"redirect_url":"string (optional)","request_access":"string (optional) — bypass gating and request configured-team access","code_challenge":"string (required) — exactly 43-char PKCE S256 challenge preserved through email verification","code_challenge_method":"\"S256\" (required)"},"body":{"email":"string (required)"},"response":{"message":"\"We sent instructions to your email\" (always, no enumeration)"}},{"method":"POST","path":"/auth/verify-email","description":"Complete email verification (registration)","auth":"config_url query param","query":{"redirect_url":"string (optional)","code_challenge":"string (required) — exactly 43-char PKCE S256 challenge","code_challenge_method":"\"S256\" (required)","request_access":"string (optional) — auto-grant or create a pending access request"},"body":{"token":"string (required) — email verification token","password":"string (optional) — required for password_required registration mode"},"response":{"ok":"true","code":"authorization code","redirect_to":"redirect URL","access_request_status":"\"pending\" when request_access created a pending access request"}},{"method":"POST","path":"/auth/token","description":"Exchange authorization code or refresh token for access + refresh tokens","auth":"config_url query param + domain hash bearer token","body":{"grant_type?":"\"authorization_code\" (default) or \"refresh_token\"","code?":"authorization code (for authorization_code grant)","redirect_url?":"required for authorization_code grant; must match issued URL","code_verifier?":"required for authorization_code grant; must match the S256 challenge","refresh_token?":"refresh token (for refresh_token grant)"},"response":{"access_token":"HS256 JWT signed with the deployment SHARED_SECRET. aud=\"uoa:access-token\". RPs cannot and should not verify it cryptographically — trust comes from the authenticated backend channel. See /api > access_token.claims for the claim schema.","expires_in":"number — seconds until access_token expiry","refresh_token":"string — opaque, server-side only; never hand to the browser","refresh_token_expires_in":"number — seconds until refresh_token expiry","token_type":"\"Bearer\"","firstLogin?":"object { memberships: { orgs, teams }, pending_invites, capabilities { can_create_org, can_accept_invite } } — included on authorization_code exchange when org_features.enabled is true. memberships.orgs[] = { orgId, role } camelCase; memberships.teams[] = { teamId, orgId, role } camelCase; pending_invites[] = { inviteId, type, orgId, teamId, teamName } camelCase. Not included on refresh_token grants.","[note]":"There is NO top-level `user` field. User identity lives inside access_token claims (read claims.sub). The outer envelope is snake_case; firstLogin.* IDs are camelCase."}},{"method":"POST","path":"/auth/revoke","description":"Revoke refresh token family (logout)","auth":"config_url query param + domain hash bearer token","body":{"refresh_token":"string (required)"},"response":{"ok":"true"}},{"method":"POST","path":"/auth/reset-password/request","description":"Initiate password reset (no enumeration)","auth":"config_url query param","body":{"email":"string (required)"},"response":{"message":"\"We sent instructions to your email\" (always)"}},{"method":"POST","path":"/auth/reset-password","description":"Complete password reset with token","auth":"config_url query param","body":{"token":"string (required)","password":"string (required)"},"response":{"ok":"true"}},{"method":"GET","path":"/auth/email/reset-password","description":"Email link landing — renders set-password UI","query":{"token":"string (required)","config_url":"string (required)"}},{"method":"GET","path":"/auth/email/twofa-reset","description":"Email link landing for 2FA reset — renders confirmation page only","query":{"token":"string (required)","config_url":"string (required)"}},{"method":"POST","path":"/auth/email/twofa-reset/confirm","description":"Confirm email-based 2FA reset and consume the one-time token","auth":"config_url query param","query":{"token":"string (required)","config_url":"string (required)"}},{"method":"GET","path":"/auth/email/link","description":"Email registration/login link landing","query":{"token":"string (required)","config_url":"string (required)","redirect_url":"string (optional)","code_challenge":"string (required) — exactly 43-char PKCE S256 challenge preserved through email verification","code_challenge_method":"\"S256\" (required)","request_access":"string (optional) — preserves access-request intent through email auth"}},{"method":"GET","path":"/auth/email/team-invite","description":"Team invite landing page with accept/decline actions","query":{"token":"string (required)","config_url":"string (required)","redirect_url":"string (optional)"}},{"method":"GET","path":"/auth/email/team-invite/decline","description":"Decline a team invitation","query":{"token":"string (required)","config_url":"string (required)"}},{"method":"GET","path":"/auth/email/team-invite-open/:inviteId.gif","description":"Tracking pixel for team invite open events"},{"method":"GET","path":"/auth/social/:provider","description":"Initiate social OAuth flow (google, facebook, github, linkedin, apple)","query":{"config_url":"string (required)","redirect_url":"string (optional)","code_challenge":"string (required) — exactly 43-char PKCE S256 challenge","code_challenge_method":"\"S256\" (required)","request_access":"string (optional) — routes social auth through configured-team access policy"}},{"method":"GET","path":"/auth/callback/:provider","description":"OAuth provider callback (server-to-server)"},{"method":"GET","path":"/auth/domain-mapping","description":"Look up org/team mapping for an email domain","auth":"config_url query param + domain hash bearer token","query":{"config_url":"string (required)","email_domain":"string (required)"}},{"method":"POST","path":"/2fa/verify","description":"Verify TOTP 2FA code during login","auth":"config_url query param","body":{"twofa_token":"string (required)","code":"string (required) — 6-digit TOTP"},"response":{"ok":"true","code":"authorization code","redirect_to":"redirect URL","access_request_status":"\"pending\" when request_access created a pending access request"}},{"method":"POST","path":"/2fa/reset/request","description":"Initiate 2FA reset via email","auth":"config_url query param","body":{"email":"string (required)"}},{"method":"POST","path":"/2fa/reset","description":"Complete 2FA reset with email token","auth":"config_url query param","body":{"token":"string (required)"},"response":{"ok":"true"}},{"method":"GET","path":"/i18n/get","description":"Fetch translation data for a language","query":{"lang":"string (required)","config_url":"string (required)"}},{"method":"GET","path":"/apps/startup","description":"Server-facing startup payload combining kill switch state and resolved feature flags","auth":"signed RS256 config JWT fetched from config_url, same verification path as /auth/login and /auth/register","query":{"config_url":"string (required) — HTTPS URL to fetch signed config JWT","appIdentifier":"string (required) — registered app identifier, e.g. com.acme.ios","platform":"string (required) — ios | android | web | macos | windows | other","versionName":"string (optional) — semantic/display version","versionCode":"string (optional) — Android numeric version code","buildNumber":"string (optional) — iOS/macOS build number","userId":"string (optional) — applies per-user flag overrides and kill-switch test targeting","teamId":"string (optional) — reserved for multi-team flag resolution"},"response":{"killSwitch":"object|null — matched kill-switch entry, or null when clear","flags":"object — all resolved feature flags for the app as key:boolean","cacheTtl":"number — seconds the caller may cache the response","serverTime":"string — ISO timestamp","activatesIn":"number (optional) — seconds until a pending kill switch activates"}},{"method":"POST","path":"/email/send","description":"Send a per-domain transactional email through UOA-managed email infrastructure","auth":"X-UOA-Config-JWT header containing a signed RS256 client config JWT","body":{"to":"email address (required)","subject":"string (required)","text":"string (required)","html":"string (optional)","reply_to":"email address (optional; overrides the configured default reply-to)"},"response":{"202":"{ ok: true }","401/403":"generic error for missing/invalid config JWT or unconfigured/unverified domain email"}},{"method":"GET","path":"/domain/users","description":"List users for a domain","auth":"domain hash bearer token","query":{"domain":"string (required)","limit":"number (optional)"}},{"method":"GET","path":"/domain/logs","description":"Login logs for a domain","auth":"domain hash bearer token","query":{"domain":"string (required)","limit":"number (optional)"}},{"method":"GET","path":"/domain/debug","description":"Debug info (requires debug_enabled in config)","auth":"domain hash bearer token","query":{"domain":"string (required)"}},{"method":"GET","path":"/org/me","description":"Current user org context","auth":"access token (X-UOA-Access-Token header)","query":{"config_url":"string (required)"}},{"method":"GET","path":"/org/organisations","description":"List organisations for domain","auth":"domain hash bearer token","query":{"config_url":"string (required)"}},{"method":"POST","path":"/org/organisations","description":"Create organisation","auth":"domain hash bearer token","body":{"name":"string (required)","owner_id":"string (required)"}},{"method":"GET","path":"/org/organisations/:orgId","description":"Get organisation details","auth":"domain hash bearer token"},{"method":"PUT","path":"/org/organisations/:orgId","description":"Update organisation","auth":"domain hash bearer token","body":{"name":"string (optional)"}},{"method":"DELETE","path":"/org/organisations/:orgId","description":"Delete organisation","auth":"domain hash bearer token"},{"method":"GET","path":"/org/organisations/:orgId/members","description":"List organisation members","auth":"domain hash bearer token"},{"method":"POST","path":"/org/organisations/:orgId/members","description":"Add organisation member","auth":"domain hash bearer token","body":{"user_id":"string (required)","role":"string (optional)"}},{"method":"PUT","path":"/org/organisations/:orgId/members/:userId","description":"Change member role","auth":"domain hash bearer token","body":{"role":"string (required)"}},{"method":"DELETE","path":"/org/organisations/:orgId/members/:userId","description":"Remove organisation member","auth":"domain hash bearer token"},{"method":"POST","path":"/org/organisations/:orgId/transfer-ownership","description":"Transfer organisation ownership","auth":"domain hash bearer token","body":{"new_owner_id":"string (required)"}},{"method":"GET","path":"/org/organisations/:orgId/teams","description":"List teams","auth":"domain hash bearer token","response":{"data":"array — team records including id, name, slug, description, groupId, isDefault"}},{"method":"POST","path":"/org/organisations/:orgId/teams","description":"Create team","auth":"domain hash bearer token","body":{"name":"string (required)","slug?":"string — optional custom team slug; otherwise derived from name","description":"string (optional)"},"response":{"slug":"string — unique team slug within the organisation"}},{"method":"GET","path":"/org/organisations/:orgId/teams/:teamId","description":"Get team details (includes members)","auth":"domain hash bearer token","response":{"slug":"string — unique team slug within the organisation","members":"array — current team members"}},{"method":"PUT","path":"/org/organisations/:orgId/teams/:teamId","description":"Update team","auth":"domain hash bearer token","body":{"name":"string (optional)","slug?":"string — optional custom team slug; omitted leaves the current slug unchanged","description":"string (optional)"},"response":{"slug":"string — unique team slug within the organisation"}},{"method":"DELETE","path":"/org/organisations/:orgId/teams/:teamId","description":"Delete team","auth":"domain hash bearer token"},{"method":"POST","path":"/org/organisations/:orgId/teams/:teamId/invitations","description":"Bulk invite users to a team and send invitation emails","auth":"domain hash bearer token","body":{"redirectUrl?":"string — optional final OAuth redirect URL","invitedBy?":"object — optional inviter metadata { userId?, name?, email? }","invites":"array (required, 1-200) — [{ email: string, name?: string, teamRole?: string }]"},"response":{"results":"array — per-email status: invited | resent_existing | already_member | conflict"}},{"method":"GET","path":"/org/organisations/:orgId/teams/:teamId/invitations","description":"List invitation history for a team","auth":"domain hash bearer token","response":{"data":"array — invite records with status, inviter, send/open, accepted/declined state"}},{"method":"POST","path":"/org/organisations/:orgId/teams/:teamId/invitations/:inviteId/resend","description":"Resend a pending team invitation email","auth":"domain hash bearer token"},{"method":"GET","path":"/org/organisations/:orgId/teams/:teamId/access-requests","description":"List access requests for the configured team","auth":"domain hash bearer token","query":{"config_url":"string (required)","domain":"string (required) — must match the config domain for domain-hash auth","status":"string (optional) — pending | approved | rejected"},"response":{"data":"array — access requests with requester, status, timestamps, reviewer metadata"}},{"method":"POST","path":"/org/organisations/:orgId/teams/:teamId/access-requests/:requestId/approve","description":"Approve an access request and add the user to the configured team","auth":"domain hash bearer token","query":{"config_url":"string (required)","domain":"string (required) — must match the config domain for domain-hash auth"},"body":{"reviewedByUserId":"string (optional) — reviewer user ID recorded with the approval","reviewReason":"string (optional, max 500) — free-form audit note"}},{"method":"POST","path":"/org/organisations/:orgId/teams/:teamId/access-requests/:requestId/reject","description":"Reject an access request for the configured team","auth":"domain hash bearer token","query":{"config_url":"string (required)","domain":"string (required) — must match the config domain for domain-hash auth"},"body":{"reviewedByUserId":"string (optional) — reviewer user ID recorded with the rejection","reviewReason":"string (optional, max 500) — free-form audit note"}},{"method":"POST","path":"/org/organisations/:orgId/teams/:teamId/members","description":"Add team member","auth":"domain hash bearer token","body":{"user_id":"string (required)","team_role":"string (optional)"}},{"method":"PUT","path":"/org/organisations/:orgId/teams/:teamId/members/:userId","description":"Change team member role","auth":"domain hash bearer token","body":{"team_role":"string (required)"}},{"method":"DELETE","path":"/org/organisations/:orgId/teams/:teamId/members/:userId","description":"Remove team member","auth":"domain hash bearer token"},{"method":"GET","path":"/org/organisations/:orgId/groups","description":"List groups","auth":"domain hash bearer token"},{"method":"GET","path":"/org/organisations/:orgId/groups/:groupId","description":"Get group details","auth":"domain hash bearer token"},{"method":"GET","path":"/integrations/claim/:token","description":"One-time claim confirmation page for an approved integration request. Intentionally does NOT consume the token — link scanners cannot accidentally burn it.","auth":"public, no auth; token in path is a 32-byte random value hashed at rest","response":{"200":"HTML confirm page with a POST form targeting /integrations/claim/:token/confirm","404":"HTML invalid-link page when the token is missing, expired, used, or malformed"}},{"method":"POST","path":"/integrations/claim/:token/confirm","description":"Consumes the claim token and renders the one-time reveal page containing the partner client_secret and client_hash. Subsequent requests return the invalid-link page.","auth":"public; the token in the path IS the credential","response":{"200":"HTML reveal page: { domain, client_secret, client_hash, hash_prefix } shown once. Partner copies both into their backend secret store before closing.","404":"HTML invalid-link page when the token is missing, expired, used, or malformed"}},{"method":"GET","path":"/internal/admin/config","description":"Serve the signed first-party Admin config JWT used by /admin/login","auth":"Public, no-store. The returned JWT must verify through CONFIG_JWKS_URL and contain the admin domain, Google-only auth, and allow_registration=false.","response":{"200":"Signed RS256 config JWT as text/plain"}},{"method":"POST","path":"/internal/admin/token","description":"Browser-safe Admin UI authorization-code exchange; returns an admin access token only","auth":"Verified config_url whose domain matches ADMIN_AUTH_DOMAIN; one-time authorization code with PKCE. Does not use domain-hash bearer auth and does not return refresh tokens.","body":{"code":"string (required)","redirect_url":"string (required)","code_verifier":"string (optional; required when the authorization code used PKCE)"},"query":{"config_url":"string (required)"},"response":{"200":"{ access_token, expires_in, token_type }"}},{"method":"GET","path":"/internal/admin/session","description":"Validate the current admin session access token","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"200":"{ ok: true, adminUser }","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/dashboard","description":"Admin dashboard aggregate data","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"200":"{ stats, domains, organisations, users, logs, handshakeErrors, bans, apps }","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/domains","description":"List domains and per-domain secret status known from the domain registry, roles, organisations, and logs","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","query":{"limit":"number (optional, max 200)"},"response":{"200":"Admin domain summary array","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"POST","path":"/internal/admin/domains","description":"Register a domain and create its first domain client secret","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","body":{"domain":"string (required)","label":"string (optional)","client_secret":"string (optional, min 32); omitted means the API generates one"},"response":{"200":"{ domain, client_secret, client_hash, client_hash_prefix }","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"PUT","path":"/internal/admin/domains/:domain","description":"Update domain label or active/disabled status","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","body":{"label":"string (optional)","status":"active | disabled (optional)"},"response":{"200":"Updated domain registry row","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/domains/:domain","description":"Get one domain with its organisations, teams, and users for directory browsing","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"200":"{ domain, organisations, teams, users } or null","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"POST","path":"/internal/admin/domains/:domain/rotate-secret","description":"Rotate the domain client secret. deliveryMode=email (default) mints a 24h one-time claim link and emails it to the partner; the previous secret keeps working until the claim is consumed. deliveryMode=reveal returns the freshly minted credentials once in the response for out-of-band delivery and skips email. Self-supplied or generated secrets are prefixed with `uoa_sec_` and the hashing rule is still `sha256(domain + client_secret)` — prefix is part of the hashed input.","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","body":{"client_secret":"string (optional, min 32); omit to let the API generate one. Generated secrets start with `uoa_sec_`.","deliveryMode":"\"email\" (default) or \"reveal\" — reveal returns credentials once and skips email"},"response":{"200":"{ domain, contact_email, delivery_mode, email_dispatched, hash_prefix, credentials? }. credentials = { domain, client_secret, client_hash, hash_prefix } is only present when delivery_mode=reveal and will never appear again.","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/domains/:domain/jwks","description":"List signing JWKs registered for a domain (active and deactivated)","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"200":"Array of { id, kid, fingerprint, active, created_at, deactivated_at, created_by_email }","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"POST","path":"/internal/admin/domains/:domain/jwks","description":"Add a public RSA JWK to a registered domain. Validator enforces kty=RSA, required kid/n/e, and rejects any private members.","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","body":{"jwk":"object (required) — public RSA JWK with kty, kid, n, e"},"response":{"200":"Inserted { id, kid, fingerprint, active, created_at, deactivated_at, created_by_email }","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"DELETE","path":"/internal/admin/domains/:domain/jwks/:kid","description":"Soft-deactivate a domain JWK by kid. Config JWTs signed with this kid will stop verifying.","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"200":"Deactivated { id, kid, fingerprint, active, created_at, deactivated_at, created_by_email }","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/domains/:domain/email","description":"Read per-domain email sending configuration, live SES status, and DNS records","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"200":"{ config, liveStatus, dnsRecords, adminCredentialsConfigured }","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"PUT","path":"/internal/admin/domains/:domain/email","description":"Create or update per-domain sender settings without calling SES","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","body":{"mailingDomain":"string (required)","fromAddress":"email (required; must end with @mailingDomain)","fromName":"string (optional)","replyToDefault":"email (optional)"},"response":{"200":"{ config }","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"POST","path":"/internal/admin/domains/:domain/email/register","description":"Register the configured mailing domain with SES and return TXT/DKIM DNS records","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"200":"{ verification, dkim }","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"POST","path":"/internal/admin/domains/:domain/email/refresh","description":"Refresh SES verification and DKIM status for the configured mailing domain","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"200":"{ verification, dkim, config }","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"PATCH","path":"/internal/admin/domains/:domain/email/enabled","description":"Enable or disable per-domain email sending; enabling requires SES verification and DKIM Success","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","body":{"enabled":"boolean (required)"},"response":{"200":"{ config }","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"DELETE","path":"/internal/admin/domains/:domain/email","description":"Delete local per-domain email config without removing the SES identity","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"204":"No content","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/superusers","description":"List users with SUPERUSER on ADMIN_AUTH_DOMAIN","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"200":"Array of { userId, email, name, createdAt }","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/superusers/search","description":"Search non-superuser UOA users by email or name","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","query":{"q":"string (optional)"},"response":{"200":"Array of { userId, email, name }","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"POST","path":"/internal/admin/superusers","description":"Grant SUPERUSER on ADMIN_AUTH_DOMAIN to an existing user","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","body":{"userId":"string (required)"},"response":{"201":"{ userId, email, name, createdAt }","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"DELETE","path":"/internal/admin/superusers/:userId","description":"Revoke SUPERUSER on ADMIN_AUTH_DOMAIN; refuses self-removal and last-superuser removal","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"204":"No content","409":"generic conflict for safety rails","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/integration-requests","description":"List auto-onboarding integration requests captured when unknown partner domains called /auth with jwks_url + contact_email in their config JWT.","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","query":{"status":"PENDING | ACCEPTED | DECLINED (optional)","limit":"number (optional, max 200)"},"response":{"200":"Array of integration request summary rows","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/integration-requests/:id","description":"Get one integration request including public_jwk, config_summary, and pre_validation_result","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"200":"Integration request detail object or null","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"POST","path":"/internal/admin/integration-requests/:id/accept","description":"Approve a pending integration request. Creates the ClientDomain, the first ClientDomainJwk, and a ClientDomainSecret in one transaction, then either emails a 24h one-time claim link to contact_email (deliveryMode=email) or returns the freshly minted credentials once in the response (deliveryMode=reveal).","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","body":{"label":"string (optional) — friendly label stored on the created ClientDomain","clientSecret":"string (optional, min 32) — omit to let the API generate one","deliveryMode":"\"email\" (default) or \"reveal\" — email dispatches a claim link; reveal returns credentials once and skips email"},"response":{"200":"Integration request detail (status=ACCEPTED) plus { delivery_mode, email_dispatched, credentials? }. credentials = { domain, client_secret, client_hash, hash_prefix } is only present when delivery_mode=reveal and will never appear again.","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"POST","path":"/internal/admin/integration-requests/:id/decline","description":"Decline a pending integration request with a required internal reason. The partner is NOT emailed.","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","body":{"reason":"string (required, max 1000) — internal audit reason"},"response":{"200":"Updated integration request detail (status=DECLINED)","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"POST","path":"/internal/admin/integration-requests/:id/resend-claim","description":"Generate a fresh 24h claim token for an ACCEPTED request. Old token is revoked. deliveryMode=email (default) re-emails the claim link; deliveryMode=reveal returns the claim credentials once without sending an email.","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","body":{"deliveryMode":"\"email\" (default) or \"reveal\""},"response":{"200":"Integration request detail plus { delivery_mode, email_dispatched, credentials? }. credentials = { domain, client_secret, client_hash, hash_prefix } is only present when delivery_mode=reveal and will never appear again.","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"DELETE","path":"/internal/admin/integration-requests/:id","description":"Remove an integration request row. Any created ClientDomain is NOT deleted; this only clears the request record.","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"200":"{ ok: true }","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/organisations","description":"List organisations with teams, members, and pre-approval rows","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","query":{"limit":"number (optional, max 200)"},"response":{"200":"Admin organisation summary array","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/organisations/:orgId","description":"Get one organisation with teams, members, and pre-approval rows","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"200":"Admin organisation object or null","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/organisations/:orgId/teams/:teamId","description":"Get one team with its parent organisation","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"200":"{ org, team } or null","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/teams","description":"List all teams with organisation context","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","query":{"limit":"number (optional, max 200)"},"response":{"200":"Admin team summary array","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/users","description":"List users with domains and latest login metadata","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","query":{"limit":"number (optional, max 200)"},"response":{"200":"Admin user summary array","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/users/:userId","description":"Get one user summary","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"200":"Admin user summary object or null","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/logs","description":"List login logs across domains","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","query":{"limit":"number (optional, max 500)"},"response":{"200":"Login log array","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/handshake-errors","description":"List sanitized app handshake and config JWT errors, including redacted request/response context for config fetch failures","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","query":{"limit":"number (optional, max 500)"},"response":{"200":"Sanitized handshake error log array with requestJson, responseJson, jwtHeader, jwtPayload, and redactions","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/settings","description":"Admin settings backing data for bans and apps","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","response":{"200":"{ bans, apps }","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}},{"method":"GET","path":"/internal/admin/search","description":"Search organisations, teams, and users","auth":"Authorization: Bearer <access_token>; admin tokens must be signed with ADMIN_ACCESS_TOKEN_SECRET, token role must be superuser, domain must match ADMIN_AUTH_DOMAIN, and DB-backed deployments require a SUPERUSER domain_roles row","query":{"q":"string (optional)"},"response":{"200":"Search result array for organisations, teams, and users","401/403":"401 when bearer token is missing/invalid; 403 when token is not an admin-domain superuser"}}]}