{"openapi":"3.0.3","info":{"title":"@faablecloud/auth","description":"Auth Platform made by Faable. Manage Users and Roles","version":"2.51.2","license":{"name":"private","url":"https://faable.com/docs/platform/privacy-policy"},"termsOfService":"https://faable.com/docs/platform/terms-of-service"},"components":{"securitySchemes":{"apikey":{"type":"apiKey","in":"query","name":"api_key"},"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"},"faableauth":{"type":"oauth2","flows":{"authorizationCode":{"authorizationUrl":"http://localhost:4500/authorize","tokenUrl":"http://localhost:4500/oauth/token","scopes":{"base":"openid profile email offline_access"}}}}},"schemas":{"AuthAccount":{"type":"object","required":["id","name","domain","slug","callback_hostnames","token_signature","token_signing_alg","enabled_locales","notification_settings","createdAt"],"properties":{"id":{"type":"string","description":"AuthAccount ID"},"name":{"type":"string"},"domain":{"type":"string"},"slug":{"type":"string"},"logo_src":{"type":"string","nullable":true},"icon_src":{"type":"string","nullable":true},"callback_hostnames":{"type":"array","items":{"type":"string"}},"token_signature":{"type":"string","description":"Shared HMAC secret. Used when token_signing_alg is an HS* algorithm; ignored for RS*/ES*/PS*."},"token_signing_alg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign tokens issued by this Account. Today only RS256 is implemented."},"default_connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"enabled_locales":{"type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"},"description":"Free-form labels on the account. The dashboard stores the environment as an `env:<name>` tag (e.g. `env:production`, `env:staging`, `env:test`)."},"team":{"type":"string","nullable":true},"notification_settings":{"type":"object","required":["welcome_email_enabled","verify_email_auto_send"],"properties":{"welcome_email_enabled":{"type":"boolean","description":"Send the built-in welcome email on user.created","default":false},"verify_email_auto_send":{"type":"boolean","description":"Send a verification email automatically when a user is created with `email_verified=false` and an email address. The link in the email lands on `GET /verify-email?ticket=...` and flips `email_verified=true` with `email_verified_method=verification_flow`. When `false`, verification emails must be requested explicitly via `POST /user/:id/verify-email/start`.","default":false}},"additionalProperties":false},"email_reply_to":{"type":"string","format":"email","maxLength":320,"description":"Reply-To on every email this tenant sends to its users. Absent = no Reply-To, so a reply goes to the From address — which is a `no-reply@` mailbox nobody reads. Set it to your own support address if you want your users to be able to answer.","nullable":true},"email_from_name":{"type":"string","maxLength":100,"description":"Display name on every email this tenant sends to its users. Absent = the account `name`.","nullable":true},"email_from_address":{"type":"string","description":"Sender address of every email this tenant sends to its users. Absent = the platform default. Set by Faable only.","nullable":true},"welcome_email":{"type":"object","properties":{"cta_url":{"type":"string","format":"uri","maxLength":500,"description":"Where the welcome button sends the user. Defaults to `https://<first callback hostname>`. Point it at your app (not your marketing site) so a fresh user lands somewhere useful.","nullable":true},"body":{"type":"string","maxLength":600,"description":"Replaces the default one-line body (\"Your account on … is ready to go.\"). Plain text, sent verbatim in every enabled locale.","nullable":true},"social_links":{"type":"array","items":{"type":"object","required":["label","url"],"properties":{"label":{"type":"string","minLength":1,"maxLength":40},"url":{"type":"string","format":"uri","maxLength":500}},"additionalProperties":false},"maxItems":6,"description":"Rendered as a \"Follow <account>\" block after the button — GitHub, LinkedIn, X, YouTube… Empty or absent hides the block.","nullable":true}},"additionalProperties":false,"nullable":true},"email_change_verification_mode":{"anyOf":[{"type":"string","enum":["new_only"]},{"type":"string","enum":["old_and_new"]}],"description":"Policy for the user email-change flow. `new_only` (default) sends a single confirmation link to the new email. `old_and_new` requires the user to also click a link sent to the previous email before the swap takes effect — stricter, useful for tenants with higher-risk users."},"email_oauth_sync_policy":{"anyOf":[{"type":"string","enum":["preserve_manual"]},{"type":"string","enum":["always_sync"]}],"description":"Controls what happens to `user.email` on subsequent OAuth/federated logins when the user previously changed their email manually through this auth server (i.e. `user.email_change_locked_at` is set).\n\n- `preserve_manual` (default): the manually-set email wins. The federated provider's email is ignored on re-sync; `email` and `email_verified` are not touched. The identity link stays valid via `provider_user_id`, so the user can still log in with Google/etc.\n- `always_sync`: the federated provider's email is always written back, overwriting any manual change. Useful for tenants whose source of truth for identity is the IdP (corporate SSO, etc.)."},"login_methods":{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"},"description":"Order in which the hosted login screen renders the available methods. Entries are Connection resource ids (`connection_xxx`) or the literal `passkey`. Methods not listed keep the built-in order (passwordless, database, social) after the listed ones; ids that no longer resolve are ignored. Listing an `oidc` connection here is also the only way to surface it on the login screen — see resolveLoginMethods()."},"disabled_connections":{"type":"array","items":{"type":"string"},"description":"Connection resource ids (`connection_xxx`) this client does NOT offer. Removing a method here is enforced, not cosmetic: the connection disappears from the login screen AND `/authorize?connection=<id>` is refused for this client, the same as a connection whose `enabled_clients` excludes it. Use it to say \"this app does not do Google\"; use `enabled_clients` on the connection itself to say \"that connection is not for this app\". A connection has to pass both."},"passkey_login_enabled":{"type":"boolean","description":"Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."},"identifier_first":{"type":"boolean","description":"Ask for the email first and only then reveal the methods that apply to it, instead of showing every method up front. Defaults to false."},"remember_last_method":{"type":"boolean","description":"Surface the method the returning user chose last time. Defaults to false."},"passkey_promotion":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["offer"]}],"description":"Invite users to create a passkey right after a successful login with another method. `offer` shows a hosted screen once per `passkey_promotion_snooze_days`, at most `passkey_promotion_max_prompts` times, to users who have no second factor yet; it never blocks the login. Requires `passkey_login_enabled`. Defaults to `off`."},"passkey_promotion_snooze_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."},"passkey_promotion_max_prompts":{"type":"integer","minimum":0,"maximum":10,"description":"How many times a user is offered a passkey over their lifetime. `0` switches the offer off for this tenant without changing `passkey_promotion`. Defaults to 3."},"remember_me":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]}],"description":"Show a \"Remember me on this device\" checkbox on the password and email-code forms. `optional`: unchecked, the session ends when the browser closes; checked, it lasts `remember_me_days`. `off` (the default): no checkbox, every session lasts the built-in 30 days."},"remember_me_days":{"type":"integer","minimum":1,"maximum":365,"description":"How long a session lasts when the user ticked \"Remember me\". Defaults to 30."}},"additionalProperties":false,"description":"Which login methods the hosted login screen offers and in what order. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so a client can flip one flag without restating the rest. Absent everywhere = built-in defaults."},"login_flow":{"type":"string","description":"The login flow bound to this account (`loginflow_xxx`). Absent = the flow compiled from the settings. A Client may bind its own.","nullable":true},"mfa_policy":{"type":"object","properties":{"mode":{"anyOf":[{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]},{"type":"string","enum":["required"]}]}],"description":"`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."},"allowed_factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."},"remember_device_days":{"type":"integer","minimum":0,"maximum":365,"description":"How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."}},"additionalProperties":false,"description":"Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."},"recovery_channels":{"type":"object","properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."},"default":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."}},"additionalProperties":false,"description":"Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."},"default_country_iso":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 country assumed for phone numbers written without an international prefix (`636647460` → `+34…` with `ES`). Needed before SMS can reach anyone: most people type their number without a prefix.","nullable":true},"webauthn_rp_id":{"type":"string","description":"WebAuthn Relying Party ID for this tenant's passkeys. Defaults to the account domain. Set it to a registrable suffix you own (e.g. `acme.com`) when the login screen is served from more than one host — the RP ID is frozen into every credential at registration, so changing it afterwards invalidates every passkey already enrolled.","nullable":true},"logout_confirm_required":{"type":"boolean","description":"When true, `/logout` asks the End-User to confirm before ending the session unless the request carries a verified `id_token_hint`. Off by default — a plain link to `/logout` is otherwise enough to sign anyone out (logout CSRF)."},"canonical_host":{"type":"string","description":"The one host this tenant's OIDC issuer and discovery document are always minted with, regardless of which host actually served the request — must be the account's own platform host or one of its verified custom domains. `/authorize` and `/logout` 302 here from any other host of the tenant. Unset (the default): every URL uses whatever host served the request, as before this field existed. Changing it can invalidate every WebAuthn passkey already enrolled unless `webauthn_rp_id` already covers the new host.","nullable":true},"createdAt":{"type":"string","description":"AuthAccount creation date"},"updatedAt":{"type":"string","description":"AuthAccount updated date"}},"description":"AuthAccount"},"TokenSigningAlg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign tokens issued by this Account. Today only RS256 is implemented."},"AccountNotificationSettings":{"type":"object","required":["welcome_email_enabled","verify_email_auto_send"],"properties":{"welcome_email_enabled":{"type":"boolean","description":"Send the built-in welcome email on user.created","default":false},"verify_email_auto_send":{"type":"boolean","description":"Send a verification email automatically when a user is created with `email_verified=false` and an email address. The link in the email lands on `GET /verify-email?ticket=...` and flips `email_verified=true` with `email_verified_method=verification_flow`. When `false`, verification emails must be requested explicitly via `POST /user/:id/verify-email/start`.","default":false}},"additionalProperties":false},"EmailChangeVerificationMode":{"anyOf":[{"type":"string","enum":["new_only"]},{"type":"string","enum":["old_and_new"]}],"description":"Policy for the user email-change flow. `new_only` (default) sends a single confirmation link to the new email. `old_and_new` requires the user to also click a link sent to the previous email before the swap takes effect — stricter, useful for tenants with higher-risk users."},"Connection":{"type":"object","required":["id","connection_name","connection_type","authorize_url","token_url","userinfo_url","client_id","client_secret","issuer","jwks_url","response_type","enabled","enabled_clients","scope","authorize_params","is_using_default_credentials","account","createdAt"],"properties":{"id":{"type":"string","description":"Connection ID"},"connection_name":{"type":"string"},"connection_type":{"enum":["database","custom","github","figma","google_oauth2","microsoft","oidc","passwordless_email"]},"authorize_url":{"type":"string","default":null},"token_url":{"type":"string","default":null},"userinfo_url":{"type":"string","default":null},"client_id":{"type":"string","default":null},"client_secret":{"type":"string","default":null},"issuer":{"type":"string","default":null},"jwks_url":{"type":"string","default":null},"response_type":{"type":"string","default":null},"enabled":{"type":"boolean"},"enabled_clients":{"type":"array","items":{"type":"string"},"default":null,"nullable":true},"scope":{"type":"array","items":{"type":"string"},"default":[]},"authorize_params":{"type":"object","description":"Authorize params","default":{},"additionalProperties":{"type":"string"}},"claims_mapping":{"type":"object","additionalProperties":{"type":"string"}},"is_using_default_credentials":{"type":"boolean","readOnly":true,"description":"Derived: true when the connection_type has shared Faable defaults AND the tenant has not set its own client_id/client_secret. Read-only — set by the server on every read."},"email_oauth_sync_policy":{"anyOf":[{"type":"string","enum":["preserve_manual"]},{"type":"string","enum":["always_sync"]}],"description":"Per-connection override of `Account.email_oauth_sync_policy`. When set on a Connection, takes precedence over the Account-level setting for logins through this IdP. When unset (default), the Account-level policy applies. Use this to allow `always_sync` for a trusted IdP (e.g. corporate SSO) while keeping `preserve_manual` for others within the same tenant."},"password_policy":{"type":"object","required":["level"],"properties":{"level":{"anyOf":[{"type":"string","enum":["none"]},{"type":"string","enum":["low"]},{"type":"string","enum":["fair"]},{"type":"string","enum":["good"]},{"type":"string","enum":["excellent"]}]},"min_length":{"type":"integer","minimum":1,"maximum":128},"require_lowercase":{"type":"boolean"},"require_uppercase":{"type":"boolean"},"require_number":{"type":"boolean"},"require_special":{"type":"boolean"}},"description":"Password policy enforced when writing a password through this database connection (password reset and admin set-password). `level` is an Auth0-style preset (none/low/fair/good/excellent); the dashboard expands it into the explicit rule fields when saving. The rule fields override the preset baseline. When unset on the connection, the server applies the `good` default. Does not affect login."},"login_identifier":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["username"]},{"type":"string","enum":["email_or_username"]}],"description":"What users of this database connection sign in with: `email`, `username`, or `email_or_username` (email first, then username). The login looks up only the matching field, and the hosted login screen asks for it — validating the address format under `email`. New database connections default to `email`; a connection created before this setting existed behaves as `email_or_username`. Only meaningful on `database` connections."},"disable_signup":{"type":"boolean","description":"When true, the public self-service signup endpoint (`POST /dbconnections/signup`) rejects new registrations against this database connection with `403 signup_disabled`. Defaults to false (signup allowed). Only meaningful on `database` connections; mirrors Auth0 `disable_signup`."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ConnectionMetadata","default":{}},"createdAt":{"type":"string","description":"Connection creation date"},"updatedAt":{"type":"string","description":"Connection updated date"}},"description":"Connection"},"ConnectionMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ConnectionMetadata","default":{}},"ConnectionCreate":{"type":"object","required":["connection_name","connection_type"],"properties":{"connection_name":{"type":"string"},"connection_type":{"enum":["database","custom","github","figma","google_oauth2","microsoft","oidc","passwordless_email"]},"authorize_url":{"type":"string"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"issuer":{"type":"string"},"jwks_url":{"type":"string"},"scope":{"type":"array","items":{"type":"string"}},"enabled":{"type":"boolean"},"claims_mapping":{"type":"object","additionalProperties":{"type":"string"}},"authorize_params":{"type":"object","description":"Authorize params","default":{},"additionalProperties":{"type":"string"}},"email_oauth_sync_policy":{"anyOf":[{"type":"string","enum":["preserve_manual"]},{"type":"string","enum":["always_sync"]}],"description":"Controls what happens to `user.email` on subsequent OAuth/federated logins when the user previously changed their email manually through this auth server (i.e. `user.email_change_locked_at` is set).\n\n- `preserve_manual` (default): the manually-set email wins. The federated provider's email is ignored on re-sync; `email` and `email_verified` are not touched. The identity link stays valid via `provider_user_id`, so the user can still log in with Google/etc.\n- `always_sync`: the federated provider's email is always written back, overwriting any manual change. Useful for tenants whose source of truth for identity is the IdP (corporate SSO, etc.)."},"password_policy":{"type":"object","required":["level"],"properties":{"level":{"anyOf":[{"type":"string","enum":["none"]},{"type":"string","enum":["low"]},{"type":"string","enum":["fair"]},{"type":"string","enum":["good"]},{"type":"string","enum":["excellent"]}]},"min_length":{"type":"integer","minimum":1,"maximum":128},"require_lowercase":{"type":"boolean"},"require_uppercase":{"type":"boolean"},"require_number":{"type":"boolean"},"require_special":{"type":"boolean"}},"description":"Password policy enforced when writing a password through this database connection (password reset and admin set-password). `level` is an Auth0-style preset (none/low/fair/good/excellent); the dashboard expands it into the explicit rule fields when saving. The rule fields override the preset baseline. When unset on the connection, the server applies the `good` default. Does not affect login."},"disable_signup":{"type":"boolean"},"login_identifier":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["username"]},{"type":"string","enum":["email_or_username"]}],"description":"What users of this database connection sign in with: `email`, `username`, or `email_or_username` (email first, then username). The login looks up only the matching field, and the hosted login screen asks for it — validating the address format under `email`. New database connections default to `email`; a connection created before this setting existed behaves as `email_or_username`. Only meaningful on `database` connections."},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"ConnectionCreate","additionalProperties":false},"ConnectionMetadataCreate":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"},"ConnectionUpdate":{"type":"object","properties":{"connection_name":{"type":"string"},"enabled":{"type":"boolean"},"enabled_clients":{"type":"array","items":{"type":"string"}},"scope":{"type":"array","items":{"type":"string"}},"client_id":{"type":"string"},"client_secret":{"type":"string"},"issuer":{"type":"string"},"authorize_url":{"type":"string"},"token_url":{"type":"string"},"userinfo_url":{"type":"string"},"jwks_url":{"type":"string"},"response_type":{"type":"string"},"authorize_params":{"type":"object","description":"Authorize params","default":{},"additionalProperties":{"type":"string"}},"claims_mapping":{"type":"object","additionalProperties":{"type":"string"}},"email_oauth_sync_policy":{"anyOf":[{"type":"string","enum":["preserve_manual"]},{"type":"string","enum":["always_sync"]},{"type":"null"}]},"password_policy":{"anyOf":[{"type":"object","required":["level"],"properties":{"level":{"anyOf":[{"type":"string","enum":["none"]},{"type":"string","enum":["low"]},{"type":"string","enum":["fair"]},{"type":"string","enum":["good"]},{"type":"string","enum":["excellent"]}]},"min_length":{"type":"integer","minimum":1,"maximum":128},"require_lowercase":{"type":"boolean"},"require_uppercase":{"type":"boolean"},"require_number":{"type":"boolean"},"require_special":{"type":"boolean"}},"description":"Password policy enforced when writing a password through this database connection (password reset and admin set-password). `level` is an Auth0-style preset (none/low/fair/good/excellent); the dashboard expands it into the explicit rule fields when saving. The rule fields override the preset baseline. When unset on the connection, the server applies the `good` default. Does not affect login."},{"type":"null"}]},"disable_signup":{"type":"boolean"},"login_identifier":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["username"]},{"type":"string","enum":["email_or_username"]}],"description":"What users of this database connection sign in with: `email`, `username`, or `email_or_username` (email first, then username). The login looks up only the matching field, and the hosted login screen asks for it — validating the address format under `email`. New database connections default to `email`; a connection created before this setting existed behaves as `email_or_username`. Only meaningful on `database` connections."}},"description":"Partial update for a Connection. Only the supplied fields are modified. `connection_type` is intentionally excluded — changing it would orphan every identity already linked to this connection.","additionalProperties":false},"ConnectionTypeInfo":{"type":"object","required":["type","label","icon","category","configured_provider","supports_default_credentials"],"properties":{"type":{"enum":["database","custom","github","figma","google_oauth2","microsoft","oidc","passwordless_email"]},"label":{"type":"string","description":"Brand name for this provider. Not translatable — \"GitHub\" is \"GitHub\" in every locale."},"icon":{"type":"string","description":"Stable icon slug (e.g. `github`, `google`, `microsoft`). Clients map it to their own icon set; it is not a URL."},"category":{"type":"string","enum":["database","social","passwordless","oidc"],"description":"Grouping this type belongs to. Matches the `?query=category:<value>` filter on GET /connection."},"configured_provider":{"type":"boolean","description":"The server preconfigures this provider's OAuth2 endpoints, so a tenant only supplies credentials. False for `custom` (bring your own URLs) and for non-OAuth types."},"supports_default_credentials":{"type":"boolean","description":"Faable ships a shared OAuth app for this provider, so a tenant can enable it without registering anything of its own."},"docs":{"type":"object","required":["label","url"],"properties":{"label":{"type":"string"},"url":{"type":"string"}},"description":"Provider documentation for tenants registering their own OAuth app."}},"description":"Presentation metadata for one connection type. Contains no endpoint URLs and no credentials.","additionalProperties":false},"LoginOptionMethod":{"anyOf":[{"type":"object","required":["kind"],"properties":{"kind":{"type":"string","enum":["passkey"]}},"additionalProperties":false},{"type":"object","required":["kind","id","connection_type","connection_name","password_policy"],"properties":{"kind":{"type":"string","enum":["connection"]},"id":{"type":"string"},"connection_type":{"type":"string"},"connection_name":{"type":"string"},"password_policy":{"type":"object","required":["level"],"properties":{"level":{"anyOf":[{"type":"string","enum":["none"]},{"type":"string","enum":["low"]},{"type":"string","enum":["fair"]},{"type":"string","enum":["good"]},{"type":"string","enum":["excellent"]}]},"min_length":{"type":"integer","minimum":1,"maximum":128},"require_lowercase":{"type":"boolean"},"require_uppercase":{"type":"boolean"},"require_number":{"type":"boolean"},"require_special":{"type":"boolean"}},"description":"Password policy enforced when writing a password through this database connection (password reset and admin set-password). `level` is an Auth0-style preset (none/low/fair/good/excellent); the dashboard expands it into the explicit rule fields when saving. The rule fields override the preset baseline. When unset on the connection, the server applies the `good` default. Does not affect login."},"login_identifier":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["username"]},{"type":"string","enum":["email_or_username"]}],"description":"What users of this database connection sign in with: `email`, `username`, or `email_or_username` (email first, then username). The login looks up only the matching field, and the hosted login screen asks for it — validating the address format under `email`. New database connections default to `email`; a connection created before this setting existed behaves as `email_or_username`. Only meaningful on `database` connections."}},"additionalProperties":false}],"description":"One method the login screen should offer. Discriminated by `kind`. Connection methods carry only what a login screen needs — never credentials, endpoints or the rest of the Connection document."},"Client":{"type":"object","required":["id","name","description","client_id","client_secret","callbacks","logout_urls","web_origins","refresh_token","account","createdAt"],"properties":{"id":{"type":"string","description":"Client ID"},"name":{"type":"string"},"description":{"type":"string"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"callbacks":{"type":"array","items":{"type":"string"}},"logout_urls":{"type":"array","items":{"type":"string"}},"web_origins":{"type":"array","items":{"type":"string"},"description":"Origins allowed to make cross-origin (CORS) requests to the token and passwordless endpoints. Empty = any origin."},"refresh_token":{"type":"object","required":["expiration_mode","infinite_token_lifetime","token_lifetime"],"properties":{"expiration_mode":{"enum":["expire","not-expire"]},"infinite_token_lifetime":{"type":"boolean"},"token_lifetime":{"type":"number"}}},"access_token":{"type":"object","properties":{"token_lifetime":{"type":"number","minimum":60,"description":"Access token lifetime in seconds for tokens issued to this client. A resolved audience (Api) token_lifetime takes precedence. Omit to use the tenant default (24h)."}}},"client_uri":{"type":"string","nullable":true},"logo_uri":{"type":"string","nullable":true},"tos_uri":{"type":"string","nullable":true},"policy_uri":{"type":"string","nullable":true},"contacts":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_method":{"type":"string"},"application_type":{"type":"string"},"software_id":{"type":"string","nullable":true},"software_version":{"type":"string","nullable":true},"frontchannel_logout_uri":{"type":"string","nullable":true},"frontchannel_logout_session_required":{"type":"boolean"},"backchannel_logout_uri":{"type":"string","nullable":true},"backchannel_logout_session_required":{"type":"boolean"},"login_methods":{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"},"description":"Order in which the hosted login screen renders the available methods. Entries are Connection resource ids (`connection_xxx`) or the literal `passkey`. Methods not listed keep the built-in order (passwordless, database, social) after the listed ones; ids that no longer resolve are ignored. Listing an `oidc` connection here is also the only way to surface it on the login screen — see resolveLoginMethods()."},"disabled_connections":{"type":"array","items":{"type":"string"},"description":"Connection resource ids (`connection_xxx`) this client does NOT offer. Removing a method here is enforced, not cosmetic: the connection disappears from the login screen AND `/authorize?connection=<id>` is refused for this client, the same as a connection whose `enabled_clients` excludes it. Use it to say \"this app does not do Google\"; use `enabled_clients` on the connection itself to say \"that connection is not for this app\". A connection has to pass both."},"passkey_login_enabled":{"type":"boolean","description":"Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."},"identifier_first":{"type":"boolean","description":"Ask for the email first and only then reveal the methods that apply to it, instead of showing every method up front. Defaults to false."},"remember_last_method":{"type":"boolean","description":"Surface the method the returning user chose last time. Defaults to false."},"passkey_promotion":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["offer"]}],"description":"Invite users to create a passkey right after a successful login with another method. `offer` shows a hosted screen once per `passkey_promotion_snooze_days`, at most `passkey_promotion_max_prompts` times, to users who have no second factor yet; it never blocks the login. Requires `passkey_login_enabled`. Defaults to `off`."},"passkey_promotion_snooze_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."},"passkey_promotion_max_prompts":{"type":"integer","minimum":0,"maximum":10,"description":"How many times a user is offered a passkey over their lifetime. `0` switches the offer off for this tenant without changing `passkey_promotion`. Defaults to 3."},"remember_me":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]}],"description":"Show a \"Remember me on this device\" checkbox on the password and email-code forms. `optional`: unchecked, the session ends when the browser closes; checked, it lasts `remember_me_days`. `off` (the default): no checkbox, every session lasts the built-in 30 days."},"remember_me_days":{"type":"integer","minimum":1,"maximum":365,"description":"How long a session lasts when the user ticked \"Remember me\". Defaults to 30."}},"additionalProperties":false,"description":"Which login methods the hosted login screen offers and in what order. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so a client can flip one flag without restating the rest. Absent everywhere = built-in defaults."},"mfa_policy":{"type":"object","properties":{"mode":{"anyOf":[{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]},{"type":"string","enum":["required"]}]}],"description":"`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."},"allowed_factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."},"remember_device_days":{"type":"integer","minimum":0,"maximum":365,"description":"How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."}},"additionalProperties":false,"description":"Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."},"recovery_channels":{"type":"object","properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."},"default":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."}},"additionalProperties":false,"description":"Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."},"login_flow":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A login flow bound to this client (`loginflow_xxx`), overriding the account's. Absent = inherit."},"default_audience":{"type":"string","description":"Audience used when a client_credentials token request sends none (e.g. `faable:management:<account_id>`). An explicit `audience` in the request wins. Absent = `${iss}/userinfo`.","nullable":true},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ClientMetadata","default":{}},"createdAt":{"type":"string","description":"Client creation date"},"updatedAt":{"type":"string","description":"Client updated date"}},"description":"Client"},"ClientMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ClientMetadata","default":{}},"ClientCreate":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"callbacks":{"type":"array","items":{"type":"string"}},"logout_urls":{"type":"array","items":{"type":"string"}},"web_origins":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"anyOf":[{"type":"string","enum":["authorization_code"]},{"type":"string","enum":["client_credentials"]},{"type":"string","enum":["refresh_token"]},{"type":"string","enum":["password"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/passwordless/otp"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:token-exchange"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:device_code"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-otp"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-recovery-code"]}],"description":"A grant the client may use at the token endpoint. An empty `grant_types` list allows every grant except `password`, which is always opt-in."},"uniqueItems":true},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"ClientCreate","additionalProperties":false},"ClientGrantType":{"anyOf":[{"type":"string","enum":["authorization_code"]},{"type":"string","enum":["client_credentials"]},{"type":"string","enum":["refresh_token"]},{"type":"string","enum":["password"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/passwordless/otp"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:token-exchange"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:device_code"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-otp"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-recovery-code"]}],"description":"A grant the client may use at the token endpoint. An empty `grant_types` list allows every grant except `password`, which is always opt-in."},"ClientMetadataCreate":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"},"ClientUpdate":{"type":"object","properties":{"name":{"type":"string","description":"Human-readable client name shown in consent screens."},"description":{"type":"string","description":"Free-form description for internal admin use."},"callbacks":{"type":"array","items":{"type":"string"},"description":"Whitelist of allowed OAuth `redirect_uri` values."},"logout_urls":{"type":"array","items":{"type":"string"},"description":"Whitelist of allowed `post_logout_redirect_uri` values."},"web_origins":{"type":"array","items":{"type":"string"},"description":"Whitelist of origins (scheme://host[:port], no path) allowed to make cross-origin (CORS) requests to the token and passwordless endpoints. Empty allows any origin."},"refresh_token":{"type":"object","required":["expiration_mode","infinite_token_lifetime","token_lifetime"],"properties":{"expiration_mode":{"enum":["expire","not-expire"]},"infinite_token_lifetime":{"type":"boolean"},"token_lifetime":{"type":"number"}}},"access_token":{"type":"object","properties":{"token_lifetime":{"type":"number","minimum":60,"description":"Access token lifetime in seconds for tokens issued to this client. A resolved audience (Api) token_lifetime takes precedence. Omit to use the tenant default (24h)."}}},"client_uri":{"type":"string","nullable":true},"logo_uri":{"type":"string","nullable":true},"tos_uri":{"type":"string","nullable":true},"policy_uri":{"type":"string","nullable":true},"contacts":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"anyOf":[{"type":"string","enum":["authorization_code"]},{"type":"string","enum":["client_credentials"]},{"type":"string","enum":["refresh_token"]},{"type":"string","enum":["password"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/passwordless/otp"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:token-exchange"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:device_code"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-otp"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-recovery-code"]}],"description":"A grant the client may use at the token endpoint. An empty `grant_types` list allows every grant except `password`, which is always opt-in."},"uniqueItems":true,"description":"Grants this client may use at the token endpoint. Empty allows every grant except `password`, which must be listed to be used."},"response_types":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_method":{"type":"string"},"application_type":{"type":"string"},"software_id":{"type":"string","nullable":true},"software_version":{"type":"string","nullable":true},"frontchannel_logout_uri":{"type":"string","nullable":true},"frontchannel_logout_session_required":{"type":"boolean"},"backchannel_logout_uri":{"type":"string","nullable":true},"backchannel_logout_session_required":{"type":"boolean"},"login_methods":{"anyOf":[{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"},"description":"Order in which the hosted login screen renders the available methods. Entries are Connection resource ids (`connection_xxx`) or the literal `passkey`. Methods not listed keep the built-in order (passwordless, database, social) after the listed ones; ids that no longer resolve are ignored. Listing an `oidc` connection here is also the only way to surface it on the login screen — see resolveLoginMethods()."},"disabled_connections":{"type":"array","items":{"type":"string"},"description":"Connection resource ids (`connection_xxx`) this client does NOT offer. Removing a method here is enforced, not cosmetic: the connection disappears from the login screen AND `/authorize?connection=<id>` is refused for this client, the same as a connection whose `enabled_clients` excludes it. Use it to say \"this app does not do Google\"; use `enabled_clients` on the connection itself to say \"that connection is not for this app\". A connection has to pass both."},"passkey_login_enabled":{"type":"boolean","description":"Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."},"identifier_first":{"type":"boolean","description":"Ask for the email first and only then reveal the methods that apply to it, instead of showing every method up front. Defaults to false."},"remember_last_method":{"type":"boolean","description":"Surface the method the returning user chose last time. Defaults to false."},"passkey_promotion":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["offer"]}],"description":"Invite users to create a passkey right after a successful login with another method. `offer` shows a hosted screen once per `passkey_promotion_snooze_days`, at most `passkey_promotion_max_prompts` times, to users who have no second factor yet; it never blocks the login. Requires `passkey_login_enabled`. Defaults to `off`."},"passkey_promotion_snooze_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."},"passkey_promotion_max_prompts":{"type":"integer","minimum":0,"maximum":10,"description":"How many times a user is offered a passkey over their lifetime. `0` switches the offer off for this tenant without changing `passkey_promotion`. Defaults to 3."},"remember_me":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]}],"description":"Show a \"Remember me on this device\" checkbox on the password and email-code forms. `optional`: unchecked, the session ends when the browser closes; checked, it lasts `remember_me_days`. `off` (the default): no checkbox, every session lasts the built-in 30 days."},"remember_me_days":{"type":"integer","minimum":1,"maximum":365,"description":"How long a session lasts when the user ticked \"Remember me\". Defaults to 30."}},"additionalProperties":false,"description":"Which login methods the hosted login screen offers and in what order. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so a client can flip one flag without restating the rest. Absent everywhere = built-in defaults."},{"type":"null"}]},"mfa_policy":{"anyOf":[{"type":"object","properties":{"mode":{"anyOf":[{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]},{"type":"string","enum":["required"]}]}],"description":"`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."},"allowed_factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."},"remember_device_days":{"type":"integer","minimum":0,"maximum":365,"description":"How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."}},"additionalProperties":false,"description":"Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."},{"type":"null"}]},"recovery_channels":{"anyOf":[{"type":"object","properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."},"default":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."}},"additionalProperties":false,"description":"Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."},{"type":"null"}]},"login_flow":{"anyOf":[{"type":"string"},{"type":"null"}]},"default_audience":{"type":"string","minLength":1,"description":"Audience a client_credentials token request gets when it sends none. An explicit `audience` wins. `null` removes it.","nullable":true},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Free-form client metadata. Replaces the whole object — send the full merged value, not a partial delta. Omit it to leave the metadata untouched."}},"description":"Partial update for a Client. Only the supplied fields are modified. `client_id` and `client_secret` are not editable through this endpoint to prevent accidental rotation; use a dedicated endpoint when secret rotation is added.","additionalProperties":false},"ClientUpdateMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Free-form client metadata. Replaces the whole object — send the full merged value, not a partial delta. Omit it to leave the metadata untouched."},"User":{"type":"object","required":["id","email_verified","phone_verified","logins_count","user_metadata","app_metadata","account","createdAt"],"properties":{"id":{"type":"string","description":"User ID"},"name":{"type":"string","description":"User name","nullable":true},"username":{"type":"string","description":"unique username for this user","nullable":true},"given_name":{"type":"string","description":"Given name","nullable":true},"family_name":{"type":"string","description":"Family name","nullable":true},"middle_name":{"type":"string","description":"Middle name (OIDC §5.1)","nullable":true},"nickname":{"type":"string","description":"Casual name. Distinct from given_name (e.g. \"Mike\" vs \"Michael\"). OIDC §5.1","nullable":true},"email":{"type":"string","description":"User email","nullable":true},"email_verified":{"type":"boolean","description":"true if email is verified","default":false},"email_verified_method":{"anyOf":[{"type":"string","enum":["manual"]},{"type":"string","enum":["verification_flow"]},{"type":"string","enum":["passwordless_otp"]},{"type":"string","enum":["team_invite"]},{"type":"string","enum":["email_change"]},{"type":"string","enum":["federated"]},{"type":"string","enum":["sms_otp"]},{"type":"string","enum":["password_reset"]},{"type":"string","enum":["import"]},{"type":"null"}],"description":"How `email_verified` was last set. `manual` — admin flipped the flag via `POST /user/:id`. `verification_flow` — user clicked the verification link. `passwordless_otp` — user completed passwordless OTP login. `team_invite` — user clicked a team invitation link. `email_change` — user confirmed a self-service email change. `federated` — verified by the external IdP on OAuth callback. `password_reset` — user completed the recovery link that was emailed to them (the `email` channel only). `import` — the user arrived verified from another provider through `POST /user/import` (asserted by whoever ran the migration). `null` — the field was cleared (admin un-verified the email)."},"email_verified_at":{"type":"string","description":"ISO 8601 timestamp of when `email_verified` was last flipped to true.","nullable":true},"email_change_locked_at":{"type":"string","description":"ISO 8601 timestamp of the user's last verified email change. When set, OAuth callbacks will not overwrite `email`/`email_verified` from the federated provider — the manually-chosen email wins.","nullable":true},"email_bounced_at":{"type":"string","description":"ISO 8601 timestamp of the last Postmark hard bounce / spam complaint for this address. When set, the email is treated as undeliverable and no further emails are sent to the user until the address changes.","nullable":true},"welcome_sent_at":{"type":"string","description":"ISO 8601 timestamp of the built-in welcome email send. Set once by the platform; guarantees at most one welcome per user.","nullable":true},"imported_at":{"type":"string","description":"ISO 8601 timestamp of when the user was created by `POST /user/import`. Imported users get none of the built-in new-user emails (verification, welcome, `user.created` notifications); `user.created` webhooks still fire and carry this field.","nullable":true},"passkey_prompted_at":{"type":"string","description":"ISO 8601 timestamp of the last time the hosted \"create a passkey\" offer was shown to this user after a login. Drives the snooze in `login_methods.passkey_promotion_snooze_days`.","nullable":true},"passkey_prompt_count":{"type":"integer","description":"How many times the passkey offer has been shown to this user. Capped by `login_methods.passkey_promotion_max_prompts`; reset by an administrator through `POST /user/:id/passkey-prompt/reset`."},"passkey_prompt_dismissed_at":{"type":"string","description":"ISO 8601 timestamp of the last time the user chose \"Not now\" on the passkey offer, as opposed to closing the tab.","nullable":true},"phone":{"type":"string","description":"contact phone number","nullable":true},"phone_e164":{"type":"boolean","description":"Whether `phone` is a valid E.164 number. Calculated on read, never stored: new writes are rejected unless they normalise, so a `false` here is a legacy row that no SMS can reach — fix the number, or set the account default country and write it again."},"phone_verified":{"type":"boolean","description":"phone is verified"},"phone_verified_method":{"anyOf":[{"type":"string","enum":["manual"]},{"type":"string","enum":["verification_flow"]},{"type":"string","enum":["passwordless_otp"]},{"type":"string","enum":["team_invite"]},{"type":"string","enum":["email_change"]},{"type":"string","enum":["federated"]},{"type":"string","enum":["sms_otp"]},{"type":"string","enum":["password_reset"]},{"type":"string","enum":["import"]},{"type":"null"}],"description":"How `phone_verified` was last set. Same enum as `email_verified_method`, plus `sms_otp` — the user typed a code sent by SMS (`POST /user/:id/verify-phone/start`)."},"phone_verified_at":{"type":"string","description":"ISO 8601 timestamp of when `phone_verified` was last flipped to true.","nullable":true},"country_iso":{"type":"string","description":"country iso code","nullable":true},"birth_date":{"type":"string","description":"user birth_date","nullable":true},"gender":{"type":"string","description":"User gender (free-form, OIDC §5.1)","nullable":true},"zoneinfo":{"type":"string","description":"IANA time-zone name (e.g. \"Europe/Madrid\"). OIDC §5.1 — used for the `zoneinfo` claim.","nullable":true},"locale":{"type":"string","description":"user main language","nullable":true},"region":{"type":"string","description":"customer region","nullable":true},"website":{"type":"string","description":"URL of the User's personal Web page or blog (OIDC §5.1)","nullable":true},"address":{"type":"object","properties":{"formatted":{"type":"string","description":"Full mailing address, formatted for display (may contain newlines)","nullable":true},"street_address":{"type":"string","nullable":true},"locality":{"type":"string","description":"City or locality","nullable":true},"region":{"type":"string","description":"State, province, or region","nullable":true},"postal_code":{"type":"string","nullable":true},"country":{"type":"string","description":"Country name","nullable":true}},"additionalProperties":false,"nullable":true},"picture":{"type":"string","description":"User picture url","nullable":true},"logins_count":{"type":"number","description":"Total number of logins this user has performed."},"last_ip":{"type":"string","description":"Last IP address from which this user logged in","nullable":true},"last_login":{"type":"string","description":"Last date and time this user logged in (ISO_8601 format)","nullable":true},"first_language":{"type":"string","description":"Primary Accept-Language subtag seen at this user's FIRST login (e.g. \"zh\"). Written once and never updated, so a later login whose language differs is visible as `event.stats.language_changed` in actions — a cross-session signal no single request can produce.","nullable":true},"suspended":{"type":"boolean","description":"true while the user is suspended. A suspended user is rejected at every login flow, token grant, session use and management-API call.","default":false},"suspended_at":{"type":"string","description":"ISO 8601 timestamp of when `suspended` was last flipped to true.","nullable":true},"suspended_reason":{"type":"string","description":"Free-form reason recorded when the user was suspended (e.g. \"abuse: RCE payload\").","nullable":true},"user_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"User Metadata","default":{}},"app_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"App Metadata","default":{}},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"UserMetadata","default":{}},"createdAt":{"type":"string","description":"User creation date"},"updatedAt":{"type":"string","description":"User updated date"}},"description":"User"},"UserAddress":{"type":"object","properties":{"formatted":{"type":"string","description":"Full mailing address, formatted for display (may contain newlines)","nullable":true},"street_address":{"type":"string","nullable":true},"locality":{"type":"string","description":"City or locality","nullable":true},"region":{"type":"string","description":"State, province, or region","nullable":true},"postal_code":{"type":"string","nullable":true},"country":{"type":"string","description":"Country name","nullable":true}},"additionalProperties":false,"nullable":true},"UserUserMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"User Metadata","default":{}},"UserAppMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"App Metadata","default":{}},"UserMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"UserMetadata","default":{}},"UserCreate":{"type":"object","properties":{"name":{"type":"string","description":"The user's full name."},"given_name":{"type":"string","description":"The user's given name."},"family_name":{"type":"string","description":"The user's family name."},"email":{"type":"string","description":"The user's email."},"phone":{"type":"string","description":"contact phone number"},"birth_date":{"type":"string","description":"The user's birth date"},"locale":{"type":"string","description":"The user's default locale lang."},"picture":{"type":"string","description":"A URI pointing to the user's picture."},"user_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"User Metadata"},"app_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"App Metadata"}},"description":"UserCreate","additionalProperties":false},"UserUserCreate":{"type":"object","properties":{},"additionalProperties":true,"description":"User Metadata"},"UserAppCreate":{"type":"object","properties":{},"additionalProperties":true,"description":"App Metadata"},"UserUpdate":{"type":"object","properties":{"name":{"type":"string","description":"name"},"given_name":{"type":"string","description":"given name"},"family_name":{"type":"string","description":"family name"},"email":{"type":"string","description":"Contact email. Changing the email to a new value automatically resets `email_verified` to false (and clears `email_verified_method` / `email_verified_at`) unless the same patch sets `email_verified` explicitly. The override is the documented way to import a pre-verified user from another IdP."},"email_verified":{"type":"boolean","description":"Flip the email verification flag. Setting `true` marks the email as verified by the admin (`email_verified_method=manual`). Setting `false` clears the verification metadata."},"phone":{"type":"string","description":"Contact phone number. Changing it to a new value automatically resets `phone_verified` to false (and clears `phone_verified_method` / `phone_verified_at`) unless the same patch sets `phone_verified` explicitly."},"phone_verified":{"type":"boolean","description":"Flip the phone verification flag. Setting `true` marks the phone as verified by the admin (`phone_verified_method=manual`)."},"suspended":{"type":"boolean","description":"Suspend (`true`) or reinstate (`false`) the user. While suspended, every login flow, token grant, session use and management-API call is rejected. Setting `true` auto-stamps `suspended_at`; setting `false` clears `suspended_at` / `suspended_reason`."},"suspended_reason":{"type":"string","maxLength":512,"description":"Free-form reason for the suspension (e.g. \"abuse: RCE payload\"). Stored verbatim for audit purposes."},"country_iso":{"type":"string","description":"user country as iso string"},"birth_date":{"type":"string","description":"user birth_date"},"locale":{"type":"string","description":"user main language","nullable":true},"picture":{"type":"string","description":"User picture url"},"user_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"User defined metadata"},"app_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"App defined metadata"}},"description":"UserUpdate","additionalProperties":false},"UserUserMetadataUpdate":{"type":"object","properties":{},"additionalProperties":true,"description":"User defined metadata"},"UserAppMetadataUpdate":{"type":"object","properties":{},"additionalProperties":true,"description":"App defined metadata"},"AdminFactor":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string"},"type":{"type":"string"},"name":{"type":"string"},"confirmed_at":{"type":"string"},"last_used_at":{"type":"string"},"remaining":{"type":"integer"}}},"Identity":{"type":"object","required":["id","connection","user","identity_id","profile_data","account","createdAt"],"properties":{"id":{"type":"string","description":"Identity ID"},"connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}]},"identity_id":{"type":"string"},"access_token":{"type":"string"},"access_token_secret":{"type":"string"},"refresh_token":{"type":"string"},"access_token_expires_at":{"type":"string","description":"ISO 8601 timestamp when the stored provider `access_token` expires. Stamped from the provider `expires_in` on (re-)authorization and refresh. Absent when the provider issues non-expiring tokens."},"profile_data":{},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"IdentityMetadata","default":{}},"createdAt":{"type":"string","description":"Identity creation date"},"updatedAt":{"type":"string","description":"Identity updated date"}},"description":"Identity"},"IdentityMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"IdentityMetadata","default":{}},"IdentityCreate":{"type":"object","required":["user","identity_id","connection","profile_data"],"properties":{"user":{"type":"string"},"identity_id":{"type":"string"},"connection":{"type":"string"},"access_token":{"type":"string"},"access_token_secret":{"type":"string"},"refresh_token":{"type":"string"},"profile_data":{}}},"Credential":{"type":"object","required":["id","connection","user","account","createdAt"],"properties":{"id":{"type":"string","description":"Credential ID"},"connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}]},"email":{"type":"string","description":"Email login identifier (unique within the connection)."},"username":{"type":"string","description":"Username login identifier (unique within the connection)."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"CredentialMetadata","default":{}},"createdAt":{"type":"string","description":"Credential creation date"},"updatedAt":{"type":"string","description":"Credential updated date"}},"description":"Credential"},"CredentialMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"CredentialMetadata","default":{}},"CredentialsCreate":{"type":"object","required":["user","connection"],"properties":{"user":{"type":"string"},"connection":{"type":"string"},"email":{"type":"string"},"username":{"type":"string"}},"additionalProperties":false},"CredentialsUpdate":{"type":"object","properties":{"email":{"type":"string"},"username":{"type":"string"}},"additionalProperties":false},"Role":{"type":"object","required":["id","name","description","account","createdAt"],"properties":{"id":{"type":"string","description":"Role ID"},"name":{"type":"string","description":"Short, human-readable identifier for the role (e.g. `admin`, `editor`, `billing-manager`). Surfaced in the dashboard role pickers.","example":"admin"},"description":{"type":"string","description":"Free-form explanation of what users in this role are expected to do or have access to. Shown alongside the role in the dashboard."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"RoleMetadata","default":{}},"createdAt":{"type":"string","description":"Role creation date"},"updatedAt":{"type":"string","description":"Role updated date"}},"description":"Role"},"RoleMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"RoleMetadata","default":{}},"RoleCreate":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"Short, human-readable identifier for the role (e.g. `admin`, `editor`).","example":"admin"},"description":{"type":"string","description":"Free-form explanation of what users in this role are expected to do or have access to."},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"RoleCreate","additionalProperties":false},"RoleMetadataCreate":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"},"RoleUpdate":{"type":"object","properties":{"name":{"type":"string","description":"New human-readable identifier for the role."},"description":{"type":"string","description":"New free-form description of what users in this role do."}},"description":"Partial update for a Role. Only the supplied fields are modified.","additionalProperties":false},"RoleMember":{"type":"object","required":["id","role","user","account","createdAt"],"properties":{"id":{"type":"string","description":"RoleMember ID"},"role":{"anyOf":[{"$ref":"#/components/schemas/Role"},{"type":"string"},{}],"description":"Role granted to the user. Returned as an id by default; pass `?expand=role` to inline the full Role object."},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}],"description":"User the role is granted to. Returned as an id by default; pass `?expand=user` to inline the full User object."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"RoleMemberMetadata","default":{}},"createdAt":{"type":"string","description":"RoleMember creation date"},"updatedAt":{"type":"string","description":"RoleMember updated date"}},"description":"RoleMember"},"RoleMemberMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"RoleMemberMetadata","default":{}},"RoleMemberCreate":{"type":"object","required":["users"],"properties":{"users":{"type":"array","items":{"type":"string"},"description":"List of User ids to grant the role to. Existing role memberships are preserved; passing a user that already holds the role is a no-op.","example":["user_6555fd293acc2f0fac0e3452"]}},"additionalProperties":false,"description":"Payload for granting a role to one or more users in a single call. Posted to `POST /role/:role_id/users`."},"Team":{"type":"object","required":["id","name","slug","description","logo_url","account","createdAt"],"properties":{"id":{"type":"string","description":"Team ID"},"name":{"type":"string","description":"Human-readable name of the team. Shown in the dashboard and in team-picker UI.","example":"Acme Engineering"},"slug":{"type":"string","description":"URL-safe identifier auto-derived from `name`. Unique within the tenant; used in routes that address a team by slug.","example":"acme-engineering"},"description":{"type":"string","description":"Optional free-form description of the team.","nullable":true},"logo_url":{"type":"string","description":"Optional URL of an image used as the team avatar/logo.","nullable":true},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"TeamMetadata","default":{}},"createdAt":{"type":"string","description":"Team creation date"},"updatedAt":{"type":"string","description":"Team updated date"}},"description":"Team"},"TeamMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"TeamMetadata","default":{}},"TeamCreate":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"Human-readable name of the team.","example":"Acme Engineering"},"description":{"type":"string","description":"Optional free-form description of the team."},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"TeamCreate","additionalProperties":false},"TeamMetadataCreate":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"},"TeamUpdate":{"type":"object","properties":{"name":{"type":"string","description":"New human-readable name for the team."},"description":{"type":"string","description":"New free-form description for the team."},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Arbitrary key/value pairs attached to the team. Replaces the previous metadata entirely."}},"description":"Partial update for a Team. Only the supplied fields are modified. `slug` is auto-derived from `name` and is not user-editable.","additionalProperties":false},"TeamUpdateMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Arbitrary key/value pairs attached to the team. Replaces the previous metadata entirely."},"TeamMember":{"type":"object","required":["id","user","team","roles","account","createdAt"],"properties":{"id":{"type":"string","description":"TeamMember ID"},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}],"description":"User that belongs to the team. Returned as an id by default; pass `?expand=user` to inline the full User object."},"team":{"anyOf":[{"$ref":"#/components/schemas/Team"},{"type":"string"},{}],"description":"Team the user belongs to. Returned as an id by default; pass `?expand=team` to inline the full Team object."},"roles":{"type":"array","items":{"anyOf":[{"$ref":"#/components/schemas/Role"},{"type":"string"},{}]},"description":"Roles granted to the user *within this team*. Distinct from account-wide RoleMember grants — these only apply in the scope of the team. Each entry is an id by default; pass `?expand=roles` to inline."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"TeamMemberMetadata","default":{}},"createdAt":{"type":"string","description":"TeamMember creation date"},"updatedAt":{"type":"string","description":"TeamMember updated date"}},"description":"TeamMember"},"TeamMemberMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"TeamMemberMetadata","default":{}},"TeamMemberCreate":{"type":"object","required":["users"],"properties":{"users":{"type":"array","items":{"type":"string"},"description":"List of User ids to add as members of the team. Users already in the team are skipped.","example":["user_6555fd293acc2f0fac0e3452"]},"roles":{"type":"array","items":{"type":"string"},"description":"Role ids to grant to the added members within the scope of this team.","default":[],"example":["role_6555fd293acc2f0fac0e3452"]}},"additionalProperties":false,"description":"Payload for adding one or more users to a team in a single call. Posted to `POST /team/:team_id/member`."},"Permission":{"type":"object","required":["value"],"properties":{"value":{"type":"string","description":"Permission identifier as it appears in the `permissions` claim of an access_token (e.g. `read:users`). Use `<verb>:<resource>` by convention.","example":"read:users"},"description":{"type":"string","description":"Human-readable explanation shown in the consent prompt."}},"description":"A permission exposed by an API: a single value that may end up in the `permissions` claim of an access_token, plus a human-readable description shown in the consent prompt."},"PermissionCreate":{"type":"object","required":["value"],"properties":{"value":{"type":"string","description":"Permission identifier as it will appear in the `permissions` claim (e.g. `read:users`). Convention: `<verb>:<resource>`.","example":"read:users"},"description":{"type":"string","description":"Human-readable explanation shown in the consent prompt."}},"additionalProperties":false,"description":"Payload for declaring a single permission inside an Api."},"ApiSigningAlg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign access_tokens issued for this API. Currently only RS256 (the account keystore default) is implemented."},"ApiTokenDialect":{"anyOf":[{"type":"string","enum":["access_token"]},{"type":"string","enum":["access_token_authz"]}],"description":"Shape of access_tokens issued for this API. `access_token` emits the standard OAuth2 token. `access_token_authz` additionally embeds a `permissions` claim listing the permissions granted to the subject for this API."},"Api":{"type":"object","required":["id","name","slug","identifier","permissions","signing_alg","token_dialect","token_lifetime","enforce_policies","allow_offline_access","skip_consent","include_teams_in_access_token","include_roles_in_access_token","include_teams_in_id_token","include_roles_in_id_token","account","createdAt"],"properties":{"id":{"type":"string","description":"Api ID"},"name":{"type":"string","description":"Human-readable name for the API. Shown in the dashboard and consent prompts.","example":"My Backend API"},"slug":{"type":"string","description":"URL-safe identifier auto-derived from `name`. Unique within the tenant."},"identifier":{"type":"string","description":"Audience URL that clients pass as `audience=` when requesting a token for this API. Becomes the `aud` claim in the issued access_token. Immutable after creation and unique within the tenant.","example":"https://api.faable.com"},"description":{"type":"string","description":"Optional human-readable description of the API."},"permissions":{"type":"array","items":{"$ref":"#/components/schemas/Permission"},"description":"Set of permissions (scopes) this API exposes. When a token is issued with `audience=identifier`, the requested `scope` is intersected with this list to populate the `permissions` claim."},"signing_alg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign access_tokens issued for this API. Currently only RS256 (the account keystore default) is implemented."},"token_dialect":{"anyOf":[{"type":"string","enum":["access_token"]},{"type":"string","enum":["access_token_authz"]}],"description":"Shape of access_tokens issued for this API. `access_token` emits the standard OAuth2 token. `access_token_authz` additionally embeds a `permissions` claim listing the permissions granted to the subject for this API."},"token_lifetime":{"type":"integer","description":"Access_token lifetime in seconds. Default 86400 (24h). Applied at token issuance time (overrides the generic default).","minimum":60,"maximum":2592000},"enforce_policies":{"type":"boolean","description":"When true, only scopes that match a permission in `permissions` are forwarded into the access_token. When false, requested scopes are echoed back verbatim with no filtering."},"allow_offline_access":{"type":"boolean","description":"When true, the API is eligible to be the target of a `refresh_token` (i.e. clients can request `offline_access` against it)."},"skip_consent":{"type":"boolean","description":"When true, the consent prompt is skipped for first-party clients (clients owned by the same tenant as the API)."},"include_teams_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `teams` claim listing the slugs of the teams the subject is a member of within the tenant."},"include_roles_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `roles` claim listing the names of the roles the subject holds across their team memberships within the tenant."},"include_teams_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `teams` claim with the subject team slugs. The id_token only sees the flag when the client requested `audience=` so this API is resolved at issuance time."},"include_roles_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `roles` claim with the subject role names. Same audience caveat as `include_teams_in_id_token`."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ApiMetadata","default":{}},"createdAt":{"type":"string","description":"Api creation date"},"updatedAt":{"type":"string","description":"Api updated date"}},"description":"Api"},"ApiMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ApiMetadata","default":{}},"ApiCreate":{"type":"object","required":["name","identifier"],"properties":{"name":{"type":"string","description":"Human-readable name for the API.","example":"My Backend API"},"identifier":{"type":"string","description":"Audience URL clients pass as `audience=` when requesting a token for this API. Must be unique within the tenant and is immutable after creation.","example":"https://api.faable.com"},"description":{"type":"string","description":"Optional human-readable description of the API."},"permissions":{"type":"array","items":{"$ref":"#/components/schemas/PermissionCreate"},"description":"Set of permissions (scopes) this API exposes. Tokens issued with `audience=identifier` will only carry these in their `permissions` claim.","default":[]},"signing_alg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign access_tokens issued for this API. Currently only RS256 (the account keystore default) is implemented."},"token_dialect":{"anyOf":[{"type":"string","enum":["access_token"]},{"type":"string","enum":["access_token_authz"]}],"description":"Shape of access_tokens issued for this API. `access_token` emits the standard OAuth2 token. `access_token_authz` additionally embeds a `permissions` claim listing the permissions granted to the subject for this API."},"token_lifetime":{"type":"integer","description":"Access_token lifetime in seconds. Defaults to 86400 (24h) if omitted.","minimum":60,"maximum":2592000},"enforce_policies":{"type":"boolean","description":"When true, requested scopes are filtered against `permissions` at token issuance."},"allow_offline_access":{"type":"boolean","description":"When true, clients can request `offline_access` and receive a refresh_token for this API."},"skip_consent":{"type":"boolean","description":"When true, skip the consent prompt for first-party clients."},"include_teams_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `teams` claim with the subject team slugs."},"include_roles_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `roles` claim with the subject role names."},"include_teams_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `teams` claim with the subject team slugs."},"include_roles_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `roles` claim with the subject role names."}},"additionalProperties":false,"description":"Payload for creating an Api (a.k.a. Resource Server / Audience). The `identifier` becomes the `aud` claim in tokens issued for this API and cannot be changed after creation."},"ApiUpdate":{"type":"object","properties":{"name":{"type":"string","description":"Human-readable name for the API."},"description":{"type":"string","description":"Optional human-readable description of the API."},"permissions":{"type":"array","items":{"$ref":"#/components/schemas/PermissionCreate"},"description":"Replaces the full permissions list. Tokens issued AFTER the update will use the new list; tokens already in circulation are unchanged."},"signing_alg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign access_tokens issued for this API. Currently only RS256 (the account keystore default) is implemented."},"token_dialect":{"anyOf":[{"type":"string","enum":["access_token"]},{"type":"string","enum":["access_token_authz"]}],"description":"Shape of access_tokens issued for this API. `access_token` emits the standard OAuth2 token. `access_token_authz` additionally embeds a `permissions` claim listing the permissions granted to the subject for this API."},"token_lifetime":{"type":"integer","description":"Access_token lifetime in seconds.","minimum":60,"maximum":2592000},"enforce_policies":{"type":"boolean","description":"When true, requested scopes are filtered against `permissions` at token issuance."},"allow_offline_access":{"type":"boolean","description":"When true, clients can request `offline_access` and receive a refresh_token for this API."},"skip_consent":{"type":"boolean","description":"When true, skip the consent prompt for first-party clients."},"include_teams_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `teams` claim with the subject team slugs."},"include_roles_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `roles` claim with the subject role names."},"include_teams_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `teams` claim with the subject team slugs."},"include_roles_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `roles` claim with the subject role names."}},"additionalProperties":false,"description":"Partial update for an Api. `identifier` is immutable and not present here; recreate the API if you need to change it."},"Action":{"type":"object","required":["id","name","triggers","code","enabled","account","createdAt"],"properties":{"id":{"type":"string","description":"Action ID"},"name":{"type":"string","description":"Action Name","maxLength":200},"triggers":{"type":"array","items":{"enum":["continue","post-login","client-credentials"]},"description":"Triggers this action runs on. Derived from the hooks the code exports (`onExecutePostLogin` → post-login, `onExecuteContinue` → continue, `onExecuteClientCredentials` → client-credentials); read-only."},"code":{"type":"string","description":"JavaScript Code to execute"},"enabled":{"type":"boolean","description":"Is this action active?","default":true},"order":{"type":"number","description":"Execution order: lower runs first; on ties the newest action runs first","default":0},"revision":{"type":"number","description":"Monotonic edit counter — increments on every update. Audit rows stamp the revision that produced each allow/deny (`data.action_revision`); `updatedAt` dates the current revision.","default":1},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ActionMetadata","default":{}},"createdAt":{"type":"string","description":"Action creation date"},"updatedAt":{"type":"string","description":"Action updated date"}},"description":"Action"},"ActionMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ActionMetadata","default":{}},"ActionCreate":{"type":"object","required":["name","code"],"properties":{"name":{"type":"string","description":"Action Name","maxLength":200},"code":{"type":"string","description":"JavaScript code. Must export at least one hook: exports.onExecutePostLogin, exports.onExecuteContinue and/or exports.onExecuteClientCredentials. The triggers are derived from the exported hooks."},"enabled":{"type":"boolean","description":"Is this action active?","default":true},"order":{"type":"number","description":"Execution order: lower runs first; on ties the newest action runs first","default":0}}},"ActionUpdate":{"type":"object","properties":{"name":{"type":"string","description":"Action Name","maxLength":200},"code":{"type":"string","description":"JavaScript code. The triggers are re-derived from the hooks it exports."},"enabled":{"type":"boolean","description":"Is this action active?"},"order":{"type":"number","description":"Execution order"}},"description":"Partial update for an Action. Only the supplied fields are modified; `triggers` is read-only and follows `code`.","additionalProperties":false},"LoginFlow":{"type":"object","required":["id","name","trigger","graph","revision","history","account","createdAt"],"properties":{"id":{"type":"string","description":"LoginFlow ID"},"name":{"type":"string","maxLength":200},"trigger":{"type":"string","enum":["login"]},"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer","description":"Monotonic edit counter, bumped on every update. A publish freezes the graph under the revision it had; a login in flight pins the revision it started on."},"published":{"type":"object","required":["graph","revision","published_at"],"properties":{"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer"},"published_at":{"type":"string"}},"additionalProperties":false},"history":{"type":"array","items":{"type":"object","required":["graph","revision","published_at"],"properties":{"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer"},"published_at":{"type":"string"}},"additionalProperties":false},"description":"Previously published graphs, newest first, for rollback."},"layout":{"type":"object","description":"Node positions in the editor. Presentation only.","additionalProperties":{"type":"object","required":["x","y"],"properties":{"x":{"type":"number"},"y":{"type":"number"}},"additionalProperties":false}},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"LoginFlowMetadata","default":{}},"createdAt":{"type":"string","description":"LoginFlow creation date"},"updatedAt":{"type":"string","description":"LoginFlow updated date"}},"description":"LoginFlow"},"LoginFlowMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"LoginFlowMetadata","default":{}},"LoginFlowCreate":{"type":"object","required":["name","graph"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"layout":{"type":"object","description":"Node positions in the editor. Presentation only.","additionalProperties":{"type":"object","required":["x","y"],"properties":{"x":{"type":"number"},"y":{"type":"number"}},"additionalProperties":false}}},"additionalProperties":false},"LoginFlowUpdate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"layout":{"type":"object","description":"Node positions in the editor. Presentation only.","additionalProperties":{"type":"object","required":["x","y"],"properties":{"x":{"type":"number"},"y":{"type":"number"}},"additionalProperties":false}}},"additionalProperties":false},"NotificationSubscription":{"anyOf":[{"type":"object","required":["id","events","channel","active","config","account","createdAt"],"properties":{"id":{"type":"string","description":"NotificationSubscriptionEmail ID"},"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["email"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["recipients"],"properties":{"recipients":{"type":"array","items":{"type":"string","description":"List of email addresses receiving notifications","format":"email"}}},"additionalProperties":false},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"NotificationSubscriptionEmailMetadata","default":{}},"createdAt":{"type":"string","description":"NotificationSubscriptionEmail creation date"},"updatedAt":{"type":"string","description":"NotificationSubscriptionEmail updated date"}},"description":"NotificationSubscriptionEmail"},{"type":"object","required":["id","events","channel","active","config","account","createdAt"],"properties":{"id":{"type":"string","description":"NotificationSubscriptionWebhook ID"},"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["webhook"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["url","secret"],"properties":{"url":{"type":"string","description":"Webhook URL","format":"uri"},"secret":{"type":"string","description":"Secret used to sign the webhook payload"}},"additionalProperties":false},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"NotificationSubscriptionWebhookMetadata","default":{}},"createdAt":{"type":"string","description":"NotificationSubscriptionWebhook creation date"},"updatedAt":{"type":"string","description":"NotificationSubscriptionWebhook updated date"}},"description":"NotificationSubscriptionWebhook"}],"description":"NotificationSubscription"},"NotificationSubscriptionEmail":{"type":"object","required":["id","events","channel","active","config","account","createdAt"],"properties":{"id":{"type":"string","description":"NotificationSubscriptionEmail ID"},"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["email"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["recipients"],"properties":{"recipients":{"type":"array","items":{"type":"string","description":"List of email addresses receiving notifications","format":"email"}}},"additionalProperties":false},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"NotificationSubscriptionEmailMetadata","default":{}},"createdAt":{"type":"string","description":"NotificationSubscriptionEmail creation date"},"updatedAt":{"type":"string","description":"NotificationSubscriptionEmail updated date"}},"description":"NotificationSubscriptionEmail"},"NotificationSubscriptionEmailConfig":{"type":"object","required":["recipients"],"properties":{"recipients":{"type":"array","items":{"type":"string","description":"List of email addresses receiving notifications","format":"email"}}},"additionalProperties":false},"NotificationSubscriptionEmailMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"NotificationSubscriptionEmailMetadata","default":{}},"NotificationSubscriptionWebhook":{"type":"object","required":["id","events","channel","active","config","account","createdAt"],"properties":{"id":{"type":"string","description":"NotificationSubscriptionWebhook ID"},"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["webhook"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["url","secret"],"properties":{"url":{"type":"string","description":"Webhook URL","format":"uri"},"secret":{"type":"string","description":"Secret used to sign the webhook payload"}},"additionalProperties":false},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"NotificationSubscriptionWebhookMetadata","default":{}},"createdAt":{"type":"string","description":"NotificationSubscriptionWebhook creation date"},"updatedAt":{"type":"string","description":"NotificationSubscriptionWebhook updated date"}},"description":"NotificationSubscriptionWebhook"},"NotificationSubscriptionWebhookConfig":{"type":"object","required":["url","secret"],"properties":{"url":{"type":"string","description":"Webhook URL","format":"uri"},"secret":{"type":"string","description":"Secret used to sign the webhook payload"}},"additionalProperties":false},"NotificationSubscriptionWebhookMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"NotificationSubscriptionWebhookMetadata","default":{}},"NotificationSubscriptionCreate":{"anyOf":[{"type":"object","required":["events","channel","config"],"properties":{"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["email"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["recipients"],"properties":{"recipients":{"type":"array","items":{"type":"string","description":"List of email addresses receiving notifications","format":"email"}}},"additionalProperties":false},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"NotificationSubscriptionEmailCreate","additionalProperties":false},{"type":"object","required":["events","channel","config"],"properties":{"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["webhook"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["url","secret"],"properties":{"url":{"type":"string","description":"Webhook URL","format":"uri"},"secret":{"type":"string","description":"Secret used to sign the webhook payload"}},"additionalProperties":false},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"NotificationSubscriptionWebhookCreate","additionalProperties":false}],"description":"NotificationSubscriptionCreate"},"NotificationSubscriptionEmailCreate":{"type":"object","required":["events","channel","config"],"properties":{"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["email"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["recipients"],"properties":{"recipients":{"type":"array","items":{"type":"string","description":"List of email addresses receiving notifications","format":"email"}}},"additionalProperties":false},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"NotificationSubscriptionEmailCreate","additionalProperties":false},"NotificationSubscriptionEmailConfigCreate":{"type":"object","required":["recipients"],"properties":{"recipients":{"type":"array","items":{"type":"string","description":"List of email addresses receiving notifications","format":"email"}}},"additionalProperties":false},"NotificationSubscriptionEmailMetadataCreate":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"},"NotificationSubscriptionWebhookCreate":{"type":"object","required":["events","channel","config"],"properties":{"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["webhook"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["url","secret"],"properties":{"url":{"type":"string","description":"Webhook URL","format":"uri"},"secret":{"type":"string","description":"Secret used to sign the webhook payload"}},"additionalProperties":false},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"NotificationSubscriptionWebhookCreate","additionalProperties":false},"NotificationSubscriptionWebhookConfigCreate":{"type":"object","required":["url","secret"],"properties":{"url":{"type":"string","description":"Webhook URL","format":"uri"},"secret":{"type":"string","description":"Secret used to sign the webhook payload"}},"additionalProperties":false},"NotificationSubscriptionWebhookMetadataCreate":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"},"NotificationSubscriptionUpdate":{"anyOf":[{"type":"object","properties":{"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["email"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","properties":{"recipients":{"type":"array","items":{"type":"string","description":"List of email addresses receiving notifications","format":"email"}}}},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Metadata update"}}},{"type":"object","properties":{"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["webhook"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","properties":{"url":{"type":"string","description":"Webhook URL","format":"uri"},"secret":{"type":"string","description":"Secret used to sign the webhook payload"}}},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Metadata update"}}}],"description":"NotificationSubscriptionUpdate"},"NotificationSubscriptionEmailMetadataUpdate":{"type":"object","properties":{},"additionalProperties":true,"description":"Metadata update"},"NotificationSubscriptionWebhookMetadataUpdate":{"type":"object","properties":{},"additionalProperties":true,"description":"Metadata update"},"Log":{"type":"object","required":["id","type","status","account","createdAt"],"properties":{"id":{"type":"string","description":"Log ID"},"type":{"type":"string","description":"Log type, e.g. email.user.created"},"status":{"anyOf":[{"type":"string","enum":["success"]},{"type":"string","enum":["failed"]},{"type":"string","enum":["skipped"]},{"type":"string","enum":["info"]}],"description":"Log status"},"message":{"type":"string","description":"Optional log message"},"data":{"description":"Type-specific structured payload"},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}]},"client":{"anyOf":[{"$ref":"#/components/schemas/Client"},{"type":"string"},{}]},"connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"team":{"anyOf":[{"$ref":"#/components/schemas/Team"},{"type":"string"},{}]},"identity":{"anyOf":[{"$ref":"#/components/schemas/Identity"},{"type":"string"},{}]},"ticket":{"type":"string"},"expires_at":{"type":"string","format":"date-time","description":"When this log row will be auto-deleted by the Mongo TTL monitor. Absent on rows recorded without `ttl_seconds`."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"LogMetadata","default":{}},"createdAt":{"type":"string","description":"Log creation date"},"updatedAt":{"type":"string","description":"Log updated date"}},"description":"Log"},"LogMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"LogMetadata","default":{}},"Session":{"type":"object","required":["id","user","sid","status","last_seen_at","expires_at","account","createdAt"],"properties":{"id":{"type":"string","description":"Session ID"},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}]},"sid":{"type":"string","description":"OIDC session id. Every id_token minted for this session carries it as the `sid` claim; a back-channel or front-channel logout names the session with it."},"status":{"anyOf":[{"type":"string","enum":["active"]},{"type":"string","enum":["revoked"]}],"description":"`active` or `revoked`. An active session whose `expires_at` is in the past has expired: nothing can refresh it any more, but the row stays for the history."},"ip":{"type":"string","description":"Client IP at the last authentication."},"user_agent":{"type":"string","description":"Raw `User-Agent` header at the last authentication."},"device_name":{"type":"string","description":"The name a device gave itself when it asked for a code (device authorization grant), e.g. the hostname `faable login` runs on. Absent for browser sessions: a browser does not expose one."},"client":{"anyOf":[{"$ref":"#/components/schemas/Client"},{"type":"string"},{}]},"connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"issuer":{"type":"string","description":"Issuer URL the session was minted on."},"last_seen_at":{"type":"string","format":"date-time","description":"Last authentication or token refresh through this session. Each refresh slides `expires_at` 30 days from here."},"expires_at":{"type":"string","format":"date-time","description":"When the session stops being usable unless something refreshes it first."},"revoked_at":{"type":"string","format":"date-time"},"revoked_reason":{"anyOf":[{"type":"string","enum":["admin"]},{"type":"string","enum":["logout"]},{"type":"string","enum":["user"]}],"description":"`admin`: revoked through the Management API. `logout`: the user signed out. `user`: the user revoked it from another device."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"SessionMetadata","default":{}},"createdAt":{"type":"string","description":"Session creation date"},"updatedAt":{"type":"string","description":"Session updated date"}},"description":"Session"},"SessionMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"SessionMetadata","default":{}},"Customdomain":{"type":"object","required":["id","domain","token","status","attempts","account","createdAt"],"properties":{"id":{"type":"string","description":"Customdomain ID"},"domain":{"type":"string"},"token":{"type":"string"},"status":{"enum":["PENDING","VERIFYING","ACTIVE","FAILED","DEGRADED"]},"attempts":{"type":"number"},"nextCheck":{"type":"string","format":"date-time"},"lastCheck":{"type":"string","format":"date-time"},"errorLog":{"type":"string","description":"User feedback error from last check"},"verifiedAt":{"type":"string","format":"date-time"},"records":{"type":"array","items":{"type":"object","required":["type","host","expected","observed","ok"],"properties":{"type":{"type":"string"},"host":{"type":"string"},"expected":{"type":"string"},"observed":{"type":"array","items":{"type":"string"}},"ok":{"type":"boolean"}}},"description":"Per-record DNS status from the last check: what each record points to now vs. expected"},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"CustomdomainMetadata","default":{}},"createdAt":{"type":"string","description":"Customdomain creation date"},"updatedAt":{"type":"string","description":"Customdomain updated date"}},"description":"Customdomain"},"CustomdomainMetadata":{"type":"object","properties":{},"additionalProperties":true,"description":"CustomdomainMetadata","default":{}},"CustomdomainCreate":{"type":"object","required":["domain"],"properties":{"domain":{"type":"string"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"CustomdomainCreate","additionalProperties":false},"CustomdomainMetadataCreate":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"},"ClientRegistrationRequest":{"type":"object","required":["redirect_uris"],"properties":{"redirect_uris":{"type":"array","items":{"type":"string","format":"uri"},"description":"Array of redirection URI values used by the client (RFC 7591 §2). REQUIRED for any client that uses an authorization grant.","minItems":1},"client_name":{"type":"string","description":"Human-readable name of the client (RFC 7591 §2)"},"client_uri":{"type":"string","description":"URL of the home page of the client"},"logo_uri":{"type":"string"},"tos_uri":{"type":"string"},"policy_uri":{"type":"string"},"contacts":{"type":"array","items":{"type":"string"},"description":"Array of email addresses responsible for the client"},"grant_types":{"type":"array","items":{"type":"string"},"description":"Grant types the client will use. Defaults to [\"authorization_code\"]. Must be a subset of what the server supports."},"response_types":{"type":"array","items":{"type":"string"},"description":"Response types the client will use. Defaults to [\"code\"]."},"token_endpoint_auth_method":{"type":"string","description":"How the client authenticates at the token endpoint. Defaults to \"client_secret_basic\"."},"application_type":{"anyOf":[{"type":"string","enum":["web"]},{"type":"string","enum":["native"]}],"description":"OIDC §2 — application type. Defaults to \"web\"."},"post_logout_redirect_uris":{"type":"array","items":{"type":"string"},"description":"OIDC RP-Initiated Logout — URIs the OP MAY redirect to after end-session."},"scope":{"type":"string","description":"Space-separated list of scope values the client may use."},"software_id":{"type":"string"},"software_version":{"type":"string"},"frontchannel_logout_uri":{"type":"string","description":"OIDC Front-Channel Logout 1.0 — URL the OP loads inside an iframe at end-session to let the RP clear its session."},"frontchannel_logout_session_required":{"type":"boolean","description":"OIDC Front-Channel Logout 1.0 — when true, the OP MUST include `iss` and `sid` parameters when calling the frontchannel_logout_uri."},"backchannel_logout_uri":{"type":"string","description":"OIDC Back-Channel Logout 1.0 — URL the OP POSTs a signed `logout_token` to when a session this client took part in ends."},"backchannel_logout_session_required":{"type":"boolean","description":"OIDC Back-Channel Logout 1.0 — when true, the `logout_token` MUST include a `sid` claim."},"web_origins":{"type":"array","items":{"type":"string"},"description":"Non-standard (Auth0-compatible) — origins (scheme://host[:port], no path) allowed to make cross-origin (CORS) requests to the token and passwordless endpoints. Empty allows any origin."}},"additionalProperties":true},"ClientRegistrationResponse":{"type":"object","required":["client_id","client_secret","client_id_issued_at","client_secret_expires_at","redirect_uris","grant_types","response_types","token_endpoint_auth_method","application_type"],"properties":{"client_id":{"type":"string"},"client_secret":{"type":"string"},"client_id_issued_at":{"type":"number","description":"Unix timestamp (seconds) at which the client_id was issued"},"client_secret_expires_at":{"type":"number","description":"0 if the client_secret does not expire"},"redirect_uris":{"type":"array","items":{"type":"string"}},"client_name":{"type":"string"},"client_uri":{"type":"string"},"logo_uri":{"type":"string"},"tos_uri":{"type":"string"},"policy_uri":{"type":"string"},"contacts":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_method":{"type":"string"},"application_type":{"type":"string"},"post_logout_redirect_uris":{"type":"array","items":{"type":"string"}},"scope":{"type":"string"},"software_id":{"type":"string"},"software_version":{"type":"string"},"frontchannel_logout_uri":{"type":"string"},"frontchannel_logout_session_required":{"type":"boolean"},"backchannel_logout_uri":{"type":"string"},"backchannel_logout_session_required":{"type":"boolean"},"web_origins":{"type":"array","items":{"type":"string"}}},"additionalProperties":true},"AuthAccountCreate":{"type":"object","required":["name","team"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100},"team":{"type":"string"},"logo_src":{"type":"string"},"icon_src":{"type":"string"},"callback_hostnames":{"type":"array","items":{"type":"string"}},"default_connection":{"type":"string"},"tags":{"type":"array","items":{"type":"string","maxLength":50},"maxItems":20},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"AuthAccountCreate","additionalProperties":false},"AuthAccountMetadataCreate":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"},"AccountTransferBody":{"type":"object","required":["to_project","reason"],"properties":{"to_project":{"type":"string","description":"Destination project (`project_<hex>`) or its team id (`team_<hex>`) — both spellings are accepted."},"reason":{"type":"string","minLength":3,"maxLength":500,"description":"Why the tenant is being moved. Recorded in the tenant's log and in ours."},"dry_run":{"type":"boolean","default":false,"description":"Return the plan (what rides along + blockers) without writing."},"allow_over_cap":{"type":"boolean","default":false,"description":"Move even if the destination ends up above the per-project cap on Auth Accounts."}},"additionalProperties":false},"AccountTransferResponse":{"type":"object","required":["moved","account_id","account_name","slug","domain","from_project","to_project","target_tenants_before","moves_with_it","blockers"],"properties":{"moved":{"type":"boolean"},"account_id":{"type":"string"},"account_name":{"type":"string"},"slug":{"type":"string"},"domain":{"type":"string"},"from_project":{"type":"string"},"to_project":{"type":"string"},"target_tenants_before":{"type":"array","items":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}}},"moves_with_it":{"type":"object","additionalProperties":{"type":"number"}},"blockers":{"type":"array","items":{"type":"object","required":["code","detail"],"properties":{"code":{"type":"string"},"detail":{"type":"string"}}}},"tenants_left_in_source":{"type":"number"},"tenants_in_target":{"type":"number"}}},"AccountEmailSenderParams":{"type":"object","required":["account_id"],"properties":{"account_id":{"type":"string"}},"additionalProperties":false},"AccountEmailSenderBody":{"type":"object","required":["address"],"properties":{"address":{"type":"string","format":"email","maxLength":320,"nullable":true}},"additionalProperties":false},"AuthAccountUpdate":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"logo_src":{"type":"string","nullable":true},"icon_src":{"type":"string","nullable":true},"callback_hostnames":{"type":"array","items":{"type":"string"}},"default_connection":{"type":"string","nullable":true},"enabled_locales":{"type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string","maxLength":50},"maxItems":20},"notification_settings":{"type":"object","properties":{"welcome_email_enabled":{"type":"boolean","description":"Send the built-in welcome email on user.created","default":false},"verify_email_auto_send":{"type":"boolean","description":"Send a verification email automatically when a user is created with `email_verified=false` and an email address. The link in the email lands on `GET /verify-email?ticket=...` and flips `email_verified=true` with `email_verified_method=verification_flow`. When `false`, verification emails must be requested explicitly via `POST /user/:id/verify-email/start`.","default":false}}},"email_reply_to":{"type":"string","format":"email","maxLength":320,"nullable":true},"email_from_name":{"type":"string","minLength":1,"maxLength":100,"nullable":true},"welcome_email":{"anyOf":[{"type":"object","properties":{"cta_url":{"type":"string","format":"uri","maxLength":500,"description":"Where the welcome button sends the user. Defaults to `https://<first callback hostname>`. Point it at your app (not your marketing site) so a fresh user lands somewhere useful.","nullable":true},"body":{"type":"string","maxLength":600,"description":"Replaces the default one-line body (\"Your account on … is ready to go.\"). Plain text, sent verbatim in every enabled locale.","nullable":true},"social_links":{"type":"array","items":{"type":"object","required":["label","url"],"properties":{"label":{"type":"string","minLength":1,"maxLength":40},"url":{"type":"string","format":"uri","maxLength":500}},"additionalProperties":false},"maxItems":6,"description":"Rendered as a \"Follow <account>\" block after the button — GitHub, LinkedIn, X, YouTube… Empty or absent hides the block.","nullable":true}},"additionalProperties":false},{"type":"null"}]},"login_methods":{"anyOf":[{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"},"description":"Order in which the hosted login screen renders the available methods. Entries are Connection resource ids (`connection_xxx`) or the literal `passkey`. Methods not listed keep the built-in order (passwordless, database, social) after the listed ones; ids that no longer resolve are ignored. Listing an `oidc` connection here is also the only way to surface it on the login screen — see resolveLoginMethods()."},"disabled_connections":{"type":"array","items":{"type":"string"},"description":"Connection resource ids (`connection_xxx`) this client does NOT offer. Removing a method here is enforced, not cosmetic: the connection disappears from the login screen AND `/authorize?connection=<id>` is refused for this client, the same as a connection whose `enabled_clients` excludes it. Use it to say \"this app does not do Google\"; use `enabled_clients` on the connection itself to say \"that connection is not for this app\". A connection has to pass both."},"passkey_login_enabled":{"type":"boolean","description":"Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."},"identifier_first":{"type":"boolean","description":"Ask for the email first and only then reveal the methods that apply to it, instead of showing every method up front. Defaults to false."},"remember_last_method":{"type":"boolean","description":"Surface the method the returning user chose last time. Defaults to false."},"passkey_promotion":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["offer"]}],"description":"Invite users to create a passkey right after a successful login with another method. `offer` shows a hosted screen once per `passkey_promotion_snooze_days`, at most `passkey_promotion_max_prompts` times, to users who have no second factor yet; it never blocks the login. Requires `passkey_login_enabled`. Defaults to `off`."},"passkey_promotion_snooze_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."},"passkey_promotion_max_prompts":{"type":"integer","minimum":0,"maximum":10,"description":"How many times a user is offered a passkey over their lifetime. `0` switches the offer off for this tenant without changing `passkey_promotion`. Defaults to 3."},"remember_me":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]}],"description":"Show a \"Remember me on this device\" checkbox on the password and email-code forms. `optional`: unchecked, the session ends when the browser closes; checked, it lasts `remember_me_days`. `off` (the default): no checkbox, every session lasts the built-in 30 days."},"remember_me_days":{"type":"integer","minimum":1,"maximum":365,"description":"How long a session lasts when the user ticked \"Remember me\". Defaults to 30."}},"additionalProperties":false,"description":"Which login methods the hosted login screen offers and in what order. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so a client can flip one flag without restating the rest. Absent everywhere = built-in defaults."},{"type":"null"}]},"mfa_policy":{"anyOf":[{"type":"object","properties":{"mode":{"anyOf":[{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]},{"type":"string","enum":["required"]}]}],"description":"`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."},"allowed_factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."},"remember_device_days":{"type":"integer","minimum":0,"maximum":365,"description":"How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."}},"additionalProperties":false,"description":"Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."},{"type":"null"}]},"recovery_channels":{"anyOf":[{"type":"object","properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."},"default":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."}},"additionalProperties":false,"description":"Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."},{"type":"null"}]},"default_country_iso":{"anyOf":[{"type":"string","minLength":2,"maxLength":2},{"type":"null"}]},"webauthn_rp_id":{"type":"string","nullable":true},"logout_confirm_required":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"canonical_host":{"type":"string","nullable":true},"login_flow":{"type":"string","nullable":true}},"description":"AuthAccountUpdate","additionalProperties":false},"FactorSummary":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string"},"type":{"type":"string"},"name":{"type":"string"},"confirmed_at":{"type":"string"},"last_used_at":{"type":"string"},"remaining":{"type":"integer"}}},"OAuthTokenParams":{"type":"object","properties":{"grant_type":{"anyOf":[{"type":"string","enum":["authorization_code"]},{"type":"string","enum":["client_credentials"]},{"type":"string","enum":["refresh_token"]},{"type":"string","enum":["password"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-otp"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-recovery-code"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/passwordless/otp"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:token-exchange"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:device_code"]}],"description":"Grant Type. https://oauth.net/2/grant-types/"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"scope":{"type":"string"},"code":{"type":"string"},"code_verifier":{"type":"string"},"username":{"type":"string"},"password":{"type":"string"},"realm":{"type":"string","description":"For `grant_type=password`: the name of the database connection to sign in against. Defaults to the first database connection offered to the client."},"otp":{"type":"string"},"refresh_token":{"type":"string"},"mfa_token":{"type":"string","description":"Opaque token returned with a `403 mfa_required` response. Present it here with `grant_type=http://auth0.com/oauth/grant-type/mfa-otp` (or `…/mfa-recovery-code`) and the code, to finish a grant a second-factor policy interrupted."},"recovery_code":{"type":"string","description":"One of the user's one-time recovery codes, for `grant_type=http://auth0.com/oauth/grant-type/mfa-recovery-code`."},"subject_token":{"type":"string"},"subject_token_type":{"type":"string"},"device_code":{"type":"string","description":"The device code returned by the authorization server."},"audience":{"type":"string","description":"Auth0-compatible alias for `resource` (RFC 8707). When both are provided they must match."},"resource":{"type":"string","description":"RFC 8707 Resource Indicator. Absolute URI of the target Api, no fragment. Matched against `Api.identifier` in the tenant."}},"additionalProperties":true},"OAuthGrantType":{"anyOf":[{"type":"string","enum":["authorization_code"]},{"type":"string","enum":["client_credentials"]},{"type":"string","enum":["refresh_token"]},{"type":"string","enum":["password"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-otp"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-recovery-code"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/passwordless/otp"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:token-exchange"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:device_code"]}],"description":"Grant Type. https://oauth.net/2/grant-types/"},"UsernamepasswordLoginBody":{"type":"object","required":["username","password","client_id"],"properties":{"username":{"type":"string"},"password":{"type":"string"},"state":{"type":"string","description":"An opaque value the applications adds to the initial request that the authorization server includes when redirecting the back to the application. This value must be used by the application to prevent CSRF attacks."},"response_type":{"type":"string"},"redirect_to":{"type":"string","description":"Same-origin path to return the user to after authentication, instead of the OAuth client redirect. Used by first-party interactive flows (e.g. device-code confirm). Non same-origin values are ignored."},"remember_me":{"type":"boolean","description":"The \"Remember me on this device\" checkbox. Honoured only when the tenant shows one (`login_methods.remember_me: \"optional\"`): `true` keeps the session for `remember_me_days`, `false` ends it when the browser closes."},"client_id":{"type":"string"},"connection":{"type":"string","description":"Deprecated. Use connection_id"},"connection_id":{"type":"string","description":"Which oauth connection to use. Leave empty to use default connection"},"scope":{"type":"string"},"redirect_uri":{"type":"string","description":"The URL to which Faable will redirect the browser after authorization has been granted by the user."},"nonce":{"type":"string","description":"OIDC §3.1.2.1 — string value used to associate a Client session with an ID Token, to mitigate replay attacks. If passed at /authorize, the same value MUST appear in the issued id_token."},"max_age":{"type":"number","description":"OIDC §3.1.2.1 — maximum allowable elapsed time in seconds since the last End-User authentication. If exceeded, the OP re-authenticates and `auth_time` is REQUIRED in the resulting id_token.","minimum":0},"prompt":{"type":"string"},"login_hint":{"type":"string","description":"OIDC §3.1.2.1 — hint about the identifier (email, username) the End-User will sign in with. Pre-fills the hosted login screen and is forwarded to upstream identity providers."},"id_token_hint":{"type":"string","description":"OIDC §3.1.2.1 — an id_token this provider issued earlier for the user the client believes is signed in. Verified (signature, not expiry). With `prompt=none`, a session for a different user answers `login_required`; otherwise it forces a fresh sign-in."},"acr_values":{"type":"string","description":"OIDC §3.1.2.1 — space-separated Authentication Context Class References, in order of preference. `urn:faable:loa:2` asks for a second factor: a session that only has one is challenged (or, with `prompt=none`, answers `interaction_required`)."},"ui_locales":{"type":"string","description":"OIDC §3.1.2.1 — preferred languages for the hosted screens, space-separated BCP47 tags. Forwarded to upstream identity providers."},"audience":{"type":"string","description":"Auth0-compatible alias for `resource` (RFC 8707). When both are sent they must match. Identifier of the Api the client wants to access; persisted in the auth state so the token issued at /oauth/token targets the matching Api resource."},"resource":{"type":"string","description":"RFC 8707 Resource Indicator. MUST be an absolute URI without fragment. When matched against a registered Api in the tenant the issued access_token gets `aud = <api.identifier>`."}},"description":"UsernamepasswordLoginBody","additionalProperties":false},"LoginCallbackBody":{"type":"object","required":["wa","wresult","wctx"],"properties":{"wa":{"type":"string","description":"is always wsignin1.0"},"wresult":{"type":"string","description":"JWT"},"wctx":{"type":"string","description":"Serialized JSON with context"}}},"ErrorCode":{"type":"string","description":"Machine-readable error code. One HTTP status per code.\n\n- `bad_request` (400): The request is malformed. `message` says what is wrong.\n- `validation_error` (400): The body, query or path failed schema validation. `details.issues` lists each failing field.\n- `unauthorized` (401): The request carries no valid credentials.\n- `payment_required` (402): The feature is not included in the current plan.\n- `forbidden` (403): The credentials are valid but do not allow this operation.\n- `not_found` (404): The resource does not exist in this account.\n- `method_not_allowed` (405): The HTTP method is not supported on this path.\n- `conflict` (409): The request conflicts with the current state of the resource.\n- `payload_too_large` (413): The request body exceeds the size limit.\n- `unsupported_media_type` (415): The `Content-Type` is not accepted by this endpoint.\n- `unprocessable_entity` (422): The request is well-formed but cannot be processed.\n- `too_many_requests` (429): Rate limit exceeded. Honour the `Retry-After` header.\n- `internal_error` (500): Unexpected server error. Retry later; the request id is logged.\n- `already_exists` (400): A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n- `invalid_id` (400): The id in the path is not a valid id for this resource.\n- `account_not_found` (404): No Auth Account matches the request (domain, header or token).\n- `invalid_query` (400): The `?query=` FaableQL expression could not be parsed.\n- `search_not_supported` (400): This resource does not support `?search=`.\n- `invalid_expand` (400): An `?expand=` path is not allowed on this resource.\n- `access_denied` (401): The caller may not access this row (ownership or machine-to-machine restriction).\n- `insufficient_scope` (403): The token lacks a scope this operation requires. `message` names it.\n- `team_required` (400): An Auth Account must belong to a project (team).\n- `account_limit_reached` (409): The project already has the maximum number of Auth Accounts.\n- `invalid_project_id` (400): The project id is not of the form `project_<hex>` or `team_<hex>`.\n- `account_transfer_refused` (409): The Auth Account cannot be moved to that project. `details.blockers` lists why.\n- `email_sender_domain_not_allowed` (400): The sender address is not in a domain this platform signs email for (AUTH_EMAIL_SENDER_DOMAINS).\n- `plan_required` (402): The setting requires a higher plan. `message` says which.\n- `invalid_canonical_host` (400): `canonical_host` must be this account's own platform host or one of its verified custom domains.\n- `canonical_host_breaks_passkeys` (400): `canonical_host` is outside the account's current WebAuthn RP ID — every passkey already enrolled would stop working. Set `webauthn_rp_id` in the same request to a suffix that covers both.\n- `not_logged_in` (401): There is no signed-in session for this request.\n- `session_already_revoked` (409): The session was already revoked.\n- `invalid_token` (401): The bearer token is missing, malformed, expired or has no subject.\n- `not_owner` (403): The caller may only perform this operation on their own user.\n- `user_suspended` (403): The user is suspended and cannot sign in or be modified.\n- `invalid_credentials` (400): The email/username or password is incorrect.\n- `login_denied` (401): A post-login Action denied the sign-in. `message` carries the reason it gave. On token grants it is a 403 with `error: invalid_grant`.\n- `signup_denied` (403): A pre-signup Action or an active block denied the sign-up. `message` carries the reason.\n- `client_credentials_denied` (401): An Action denied the client_credentials grant. `message` carries the reason.\n- `m2m_quota_exceeded` (402): The project used the machine-to-machine tokens its plan includes this month. Free is capped; Hobby and Pro are billed per token instead.\n- `signup_disabled` (403): Self-service sign-up is disabled on this connection.\n- `email_taken` (409): Another user in this account already uses that email.\n- `invalid_email` (400): The email address is not valid.\n- `invalid_name` (400): The name contains a link. `message` says which field.\n- `same_email` (400): The new email is the same as the current one.\n- `user_has_no_email` (400): The operation needs an email but the user has none.\n- `password_too_weak` (400): The password does not meet the connection policy. `message` lists each unmet rule.\n- `invalid_password_hash` (400): The imported password hash cannot be read, or its cost is above what a login can afford. `message` names the field.\n- `weak_password_hash` (400): The imported password hash uses a fast digest (MD5, SHA-1, SHA-2) or a cost below the minimum. `message` says which.\n- `invalid_code` (400): The verification code is wrong, expired or already used.\n- `invalid_phone` (400): The phone number is not E.164 and could not be resolved with the account default country. Include the country code.\n- `phone_changed` (400): The phone on the user changed after the code was sent. Start again.\n- `phone_already_verified` (409): The user already has a verified phone; the number cannot be swapped from the screen.\n- `sms_unavailable` (409): This account cannot send SMS right now. The suffix says why: `sms_unavailable:plan`, `:quota`, `:provider`.\n- `sms_failed` (409): The SMS provider rejected the message.\n- `invalid_state` (400): The `state` is missing, expired, or does not describe a resumable step.\n- `state_mismatch` (401): The `state` belongs to another account, session, client or ceremony.\n- `invalid_ticket` (400): The ticket does not exist or is not the kind this endpoint accepts.\n- `ticket_used` (400): The ticket was already consumed.\n- `ticket_expired` (400): The ticket is past its expiry.\n- `ticket_not_found` (404): No ticket with that id belongs to this user.\n- `credential_not_found` (404): The user has no password credential on this connection.\n- `invalid_link` (400): The magic link is malformed.\n- `expired_link` (400): The magic link token was not found or has expired.\n- `invalid_otp` (401): The one-time password is wrong or expired.\n- `invalid_client` (400): The `client_id` in the request does not name a client of this account.\n- `client_not_found` (404): No client with that `client_id`.\n- `client_mismatch` (401): The client belongs to a different account.\n- `invalid_client_metadata` (400): Dynamic client registration rejected a field (RFC 7591). `message` names it.\n- `invalid_connection` (400): The connection does not exist, is disabled, or is not of the type this flow needs.\n- `no_database_connection` (400): The tenant has no database connection, so there is nowhere to store a password. Create one and retry.\n- `connection_required` (400): No connection was given and the client has no `default_connection`.\n- `connection_misconfigured` (400): The connection is missing settings needed to talk to its provider.\n- `passwordless_unavailable` (400): No passwordless connection is configured or enabled for this client.\n- `ambiguous_connection` (400): Several connections match; pass `connection_id`.\n- `invalid_redirect_uri` (400): The redirect URI is not registered for the client.\n- `origin_not_allowed` (403): The request `Origin` is not in the client's Allowed Web Origins.\n- `audience_not_found` (403): The `audience` does not name an API registered in this account.\n- `system_resource` (403): The resource is managed by Faable and cannot be modified or deleted.\n- `invalid_request` (400): A required OAuth parameter is missing or two of them are incompatible. `message` says which.\n- `invalid_device_code` (400): The device or user code is invalid or expired.\n- `provider_error` (400): The upstream identity provider returned an error. The suffix is the provider’s own code, when it sent one: `provider_error:bad_refresh_token` (the user must re-authorize), `provider_error:incorrect_client_credentials` (our configuration, the user can do nothing). `details.provider_error_description` carries the provider’s sentence.\n- `identity_already_linked` (409): That external identity is already linked to another user.\n- `identity_link_denied` (409): That external identity cannot be linked to this user.\n- `identity_conflict` (409): The user already has a linked identity for this connection.\n- `identity_orphaned` (400): The identity pointed at a user that no longer exists; it was removed. Sign in again.\n- `no_provider_token` (400): The identity holds no provider access token.\n- `no_refresh_token` (400): The identity holds no provider refresh token, so it cannot be refreshed.\n- `action_unavailable` (401): The Action that paused this login is disabled or belongs elsewhere.\n- `login_flow_misconfigured` (500): The login flow of this account cannot run. `message` carries the node error.\n- `mfa_required` (403): The login needs a second factor that this flow cannot collect, or one is still pending on the session.\n- `step_up_required` (403): Confirm an existing factor before changing your factors.\n- `interaction_required` (403): An Action asked for a redirect on a non-interactive flow.\n- `passkey_login_disabled` (400): Sign in with a passkey is not enabled for this client.\n- `mfa_pending` (401): A second-factor challenge is pending; answer it before continuing.\n- `mfa_invalid_code` (401): The authenticator code did not verify.\n- `mfa_invalid_recovery_code` (401): The recovery code did not verify.\n- `too_many_attempts` (429): Too many wrong codes. Wait a few minutes and try again.\n- `too_many_login_attempts` (429): Too many failed sign-in attempts for this email or username from this network. Wait a few minutes and try again.\n- `totp_not_allowed` (401): This account does not accept authenticator apps.\n- `no_usable_factor` (400): The user has no confirmed second factor to challenge.\n- `factor_not_found` (400): No factor with that id belongs to this user.\n- `factor_already_confirmed` (400): The factor was already confirmed.\n- `invalid_factor` (400): The factor cannot be verified (no secret material).\n- `session_missing` (401): The session that started this login no longer exists.\n- `passkey_verification_failed` (400): The WebAuthn response could not be verified.\n- `no_passkey_enrolled` (400): The user has no passkey to sign in with.\n- `malformed_credential` (400): The WebAuthn credential is malformed.\n- `unknown_passkey` (401): The passkey is not enrolled for this user.\n- `invalid_passkey` (401): The passkey assertion did not verify.\n- `invite_not_found` (404): No invite with that id in this account.\n- `invite_used` (400): The invite was already accepted.\n- `already_member` (400): The user is already a member of the team.\n- `not_a_member` (404): The user is not a member of the team.\n- `role_not_found` (404): No role with that id in this account.\n- `invalid_role` (400): The role belongs to another account.\n- `flow_not_found` (400): The login flow does not exist in this account.\n- `revision_not_found` (404): The revision is not in the flow history.","enum":["bad_request","validation_error","unauthorized","payment_required","forbidden","not_found","method_not_allowed","conflict","payload_too_large","unsupported_media_type","unprocessable_entity","too_many_requests","internal_error","already_exists","invalid_id","account_not_found","invalid_query","search_not_supported","invalid_expand","access_denied","insufficient_scope","team_required","account_limit_reached","invalid_project_id","account_transfer_refused","email_sender_domain_not_allowed","plan_required","invalid_canonical_host","canonical_host_breaks_passkeys","not_logged_in","session_already_revoked","invalid_token","not_owner","user_suspended","invalid_credentials","login_denied","signup_denied","client_credentials_denied","m2m_quota_exceeded","signup_disabled","email_taken","invalid_email","invalid_name","same_email","user_has_no_email","password_too_weak","invalid_password_hash","weak_password_hash","invalid_code","invalid_phone","phone_changed","phone_already_verified","sms_unavailable","sms_failed","invalid_state","state_mismatch","invalid_ticket","ticket_used","ticket_expired","ticket_not_found","credential_not_found","invalid_link","expired_link","invalid_otp","invalid_client","client_not_found","client_mismatch","invalid_client_metadata","invalid_connection","no_database_connection","connection_required","connection_misconfigured","passwordless_unavailable","ambiguous_connection","invalid_redirect_uri","origin_not_allowed","audience_not_found","system_resource","invalid_request","invalid_device_code","provider_error","identity_already_linked","identity_link_denied","identity_conflict","identity_orphaned","no_provider_token","no_refresh_token","action_unavailable","login_flow_misconfigured","mfa_required","step_up_required","interaction_required","passkey_login_disabled","mfa_pending","mfa_invalid_code","mfa_invalid_recovery_code","too_many_attempts","too_many_login_attempts","totp_not_allowed","no_usable_factor","factor_not_found","factor_already_confirmed","invalid_factor","session_missing","passkey_verification_failed","no_passkey_enrolled","malformed_credential","unknown_passkey","invalid_passkey","invite_not_found","invite_used","already_member","not_a_member","role_not_found","invalid_role","flow_not_found","revision_not_found"]},"ErrorResponse":{"type":"object","description":"Every error response has this shape. Branch on `error_code`, never on `message`: the message is written for a person and may change.","required":["status","message","error_code","docs"],"properties":{"status":{"type":"integer","description":"The HTTP status, repeated."},"message":{"type":"string","description":"Human-readable explanation. May change between releases."},"error_code":{"$ref":"#/components/schemas/ErrorCode","description":"Stable machine-readable code. Some codes carry a detail suffix after a colon (`sms_unavailable:plan`); branch on the prefix."},"docs":{"type":"string","format":"uri","description":"Link to the documentation of this error."},"details":{"type":"object","additionalProperties":true,"description":"Optional structured extras. `validation_error` and `already_exists` carry `fields`; `validation_error` also carries `issues`."}}}}},"paths":{"/webhooks/postmark":{"post":{"responses":{"200":{"description":"Default Response"},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/account/current":{"get":{"operationId":"account/current","summary":"Get current Account","tags":["account"],"description":"Returns the Account resolved from request context (header, hostname, or team id). Use `expand` to populate nested references.","parameters":[{"schema":{"type":"array","items":{"type":"string","maxLength":1000}},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `expand=user&expand=team`)."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"AuthAccount","content":{"application/json":{"schema":{"type":"object","required":["id","name","domain","slug","callback_hostnames","token_signature","token_signing_alg","enabled_locales","notification_settings","createdAt"],"properties":{"id":{"type":"string","description":"AuthAccount ID"},"name":{"type":"string"},"domain":{"type":"string"},"slug":{"type":"string"},"logo_src":{"type":"string","nullable":true},"icon_src":{"type":"string","nullable":true},"callback_hostnames":{"type":"array","items":{"type":"string"}},"token_signature":{"type":"string","description":"Shared HMAC secret. Used when token_signing_alg is an HS* algorithm; ignored for RS*/ES*/PS*."},"token_signing_alg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign tokens issued by this Account. Today only RS256 is implemented."},"default_connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"enabled_locales":{"type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"},"description":"Free-form labels on the account. The dashboard stores the environment as an `env:<name>` tag (e.g. `env:production`, `env:staging`, `env:test`)."},"team":{"type":"string","nullable":true},"notification_settings":{"type":"object","required":["welcome_email_enabled","verify_email_auto_send"],"properties":{"welcome_email_enabled":{"type":"boolean","description":"Send the built-in welcome email on user.created","default":false},"verify_email_auto_send":{"type":"boolean","description":"Send a verification email automatically when a user is created with `email_verified=false` and an email address. The link in the email lands on `GET /verify-email?ticket=...` and flips `email_verified=true` with `email_verified_method=verification_flow`. When `false`, verification emails must be requested explicitly via `POST /user/:id/verify-email/start`.","default":false}},"additionalProperties":false},"email_reply_to":{"type":"string","format":"email","maxLength":320,"description":"Reply-To on every email this tenant sends to its users. Absent = no Reply-To, so a reply goes to the From address — which is a `no-reply@` mailbox nobody reads. Set it to your own support address if you want your users to be able to answer.","nullable":true},"email_from_name":{"type":"string","maxLength":100,"description":"Display name on every email this tenant sends to its users. Absent = the account `name`.","nullable":true},"email_from_address":{"type":"string","description":"Sender address of every email this tenant sends to its users. Absent = the platform default. Set by Faable only.","nullable":true},"welcome_email":{"type":"object","properties":{"cta_url":{"type":"string","format":"uri","maxLength":500,"description":"Where the welcome button sends the user. Defaults to `https://<first callback hostname>`. Point it at your app (not your marketing site) so a fresh user lands somewhere useful.","nullable":true},"body":{"type":"string","maxLength":600,"description":"Replaces the default one-line body (\"Your account on … is ready to go.\"). Plain text, sent verbatim in every enabled locale.","nullable":true},"social_links":{"type":"array","items":{"type":"object","required":["label","url"],"properties":{"label":{"type":"string","minLength":1,"maxLength":40},"url":{"type":"string","format":"uri","maxLength":500}},"additionalProperties":false},"maxItems":6,"description":"Rendered as a \"Follow <account>\" block after the button — GitHub, LinkedIn, X, YouTube… Empty or absent hides the block.","nullable":true}},"additionalProperties":false,"nullable":true},"email_change_verification_mode":{"anyOf":[{"type":"string","enum":["new_only"]},{"type":"string","enum":["old_and_new"]}],"description":"Policy for the user email-change flow. `new_only` (default) sends a single confirmation link to the new email. `old_and_new` requires the user to also click a link sent to the previous email before the swap takes effect — stricter, useful for tenants with higher-risk users."},"email_oauth_sync_policy":{"anyOf":[{"type":"string","enum":["preserve_manual"]},{"type":"string","enum":["always_sync"]}],"description":"Controls what happens to `user.email` on subsequent OAuth/federated logins when the user previously changed their email manually through this auth server (i.e. `user.email_change_locked_at` is set).\n\n- `preserve_manual` (default): the manually-set email wins. The federated provider's email is ignored on re-sync; `email` and `email_verified` are not touched. The identity link stays valid via `provider_user_id`, so the user can still log in with Google/etc.\n- `always_sync`: the federated provider's email is always written back, overwriting any manual change. Useful for tenants whose source of truth for identity is the IdP (corporate SSO, etc.)."},"login_methods":{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"},"description":"Order in which the hosted login screen renders the available methods. Entries are Connection resource ids (`connection_xxx`) or the literal `passkey`. Methods not listed keep the built-in order (passwordless, database, social) after the listed ones; ids that no longer resolve are ignored. Listing an `oidc` connection here is also the only way to surface it on the login screen — see resolveLoginMethods()."},"disabled_connections":{"type":"array","items":{"type":"string"},"description":"Connection resource ids (`connection_xxx`) this client does NOT offer. Removing a method here is enforced, not cosmetic: the connection disappears from the login screen AND `/authorize?connection=<id>` is refused for this client, the same as a connection whose `enabled_clients` excludes it. Use it to say \"this app does not do Google\"; use `enabled_clients` on the connection itself to say \"that connection is not for this app\". A connection has to pass both."},"passkey_login_enabled":{"type":"boolean","description":"Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."},"identifier_first":{"type":"boolean","description":"Ask for the email first and only then reveal the methods that apply to it, instead of showing every method up front. Defaults to false."},"remember_last_method":{"type":"boolean","description":"Surface the method the returning user chose last time. Defaults to false."},"passkey_promotion":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["offer"]}],"description":"Invite users to create a passkey right after a successful login with another method. `offer` shows a hosted screen once per `passkey_promotion_snooze_days`, at most `passkey_promotion_max_prompts` times, to users who have no second factor yet; it never blocks the login. Requires `passkey_login_enabled`. Defaults to `off`."},"passkey_promotion_snooze_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."},"passkey_promotion_max_prompts":{"type":"integer","minimum":0,"maximum":10,"description":"How many times a user is offered a passkey over their lifetime. `0` switches the offer off for this tenant without changing `passkey_promotion`. Defaults to 3."},"remember_me":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]}],"description":"Show a \"Remember me on this device\" checkbox on the password and email-code forms. `optional`: unchecked, the session ends when the browser closes; checked, it lasts `remember_me_days`. `off` (the default): no checkbox, every session lasts the built-in 30 days."},"remember_me_days":{"type":"integer","minimum":1,"maximum":365,"description":"How long a session lasts when the user ticked \"Remember me\". Defaults to 30."}},"additionalProperties":false,"description":"Which login methods the hosted login screen offers and in what order. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so a client can flip one flag without restating the rest. Absent everywhere = built-in defaults."},"login_flow":{"type":"string","description":"The login flow bound to this account (`loginflow_xxx`). Absent = the flow compiled from the settings. A Client may bind its own.","nullable":true},"mfa_policy":{"type":"object","properties":{"mode":{"anyOf":[{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]},{"type":"string","enum":["required"]}]}],"description":"`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."},"allowed_factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."},"remember_device_days":{"type":"integer","minimum":0,"maximum":365,"description":"How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."}},"additionalProperties":false,"description":"Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."},"recovery_channels":{"type":"object","properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."},"default":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."}},"additionalProperties":false,"description":"Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."},"default_country_iso":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 country assumed for phone numbers written without an international prefix (`636647460` → `+34…` with `ES`). Needed before SMS can reach anyone: most people type their number without a prefix.","nullable":true},"webauthn_rp_id":{"type":"string","description":"WebAuthn Relying Party ID for this tenant's passkeys. Defaults to the account domain. Set it to a registrable suffix you own (e.g. `acme.com`) when the login screen is served from more than one host — the RP ID is frozen into every credential at registration, so changing it afterwards invalidates every passkey already enrolled.","nullable":true},"logout_confirm_required":{"type":"boolean","description":"When true, `/logout` asks the End-User to confirm before ending the session unless the request carries a verified `id_token_hint`. Off by default — a plain link to `/logout` is otherwise enough to sign anyone out (logout CSRF)."},"canonical_host":{"type":"string","description":"The one host this tenant's OIDC issuer and discovery document are always minted with, regardless of which host actually served the request — must be the account's own platform host or one of its verified custom domains. `/authorize` and `/logout` 302 here from any other host of the tenant. Unset (the default): every URL uses whatever host served the request, as before this field existed. Changing it can invalidate every WebAuthn passkey already enrolled unless `webauthn_rp_id` already covers the new host.","nullable":true},"createdAt":{"type":"string","description":"AuthAccount creation date"},"updatedAt":{"type":"string","description":"AuthAccount updated date"}},"description":"AuthAccount"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/account":{"post":{"operationId":"account/create","summary":"Create an Account","tags":["account"],"description":"Creates a new Auth Account. The slug is derived from the name and used to build the default `*.auth.faable.link` domain.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","team"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100},"team":{"type":"string"},"logo_src":{"type":"string"},"icon_src":{"type":"string"},"callback_hostnames":{"type":"array","items":{"type":"string"}},"default_connection":{"type":"string"},"tags":{"type":"array","items":{"type":"string","maxLength":50},"maxItems":20},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"AuthAccountCreate","additionalProperties":false}}},"description":"AuthAccountCreate"},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"AuthAccount","content":{"application/json":{"schema":{"type":"object","required":["id","name","domain","slug","callback_hostnames","token_signature","token_signing_alg","enabled_locales","notification_settings","createdAt"],"properties":{"id":{"type":"string","description":"AuthAccount ID"},"name":{"type":"string"},"domain":{"type":"string"},"slug":{"type":"string"},"logo_src":{"type":"string","nullable":true},"icon_src":{"type":"string","nullable":true},"callback_hostnames":{"type":"array","items":{"type":"string"}},"token_signature":{"type":"string","description":"Shared HMAC secret. Used when token_signing_alg is an HS* algorithm; ignored for RS*/ES*/PS*."},"token_signing_alg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign tokens issued by this Account. Today only RS256 is implemented."},"default_connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"enabled_locales":{"type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"},"description":"Free-form labels on the account. The dashboard stores the environment as an `env:<name>` tag (e.g. `env:production`, `env:staging`, `env:test`)."},"team":{"type":"string","nullable":true},"notification_settings":{"type":"object","required":["welcome_email_enabled","verify_email_auto_send"],"properties":{"welcome_email_enabled":{"type":"boolean","description":"Send the built-in welcome email on user.created","default":false},"verify_email_auto_send":{"type":"boolean","description":"Send a verification email automatically when a user is created with `email_verified=false` and an email address. The link in the email lands on `GET /verify-email?ticket=...` and flips `email_verified=true` with `email_verified_method=verification_flow`. When `false`, verification emails must be requested explicitly via `POST /user/:id/verify-email/start`.","default":false}},"additionalProperties":false},"email_reply_to":{"type":"string","format":"email","maxLength":320,"description":"Reply-To on every email this tenant sends to its users. Absent = no Reply-To, so a reply goes to the From address — which is a `no-reply@` mailbox nobody reads. Set it to your own support address if you want your users to be able to answer.","nullable":true},"email_from_name":{"type":"string","maxLength":100,"description":"Display name on every email this tenant sends to its users. Absent = the account `name`.","nullable":true},"email_from_address":{"type":"string","description":"Sender address of every email this tenant sends to its users. Absent = the platform default. Set by Faable only.","nullable":true},"welcome_email":{"type":"object","properties":{"cta_url":{"type":"string","format":"uri","maxLength":500,"description":"Where the welcome button sends the user. Defaults to `https://<first callback hostname>`. Point it at your app (not your marketing site) so a fresh user lands somewhere useful.","nullable":true},"body":{"type":"string","maxLength":600,"description":"Replaces the default one-line body (\"Your account on … is ready to go.\"). Plain text, sent verbatim in every enabled locale.","nullable":true},"social_links":{"type":"array","items":{"type":"object","required":["label","url"],"properties":{"label":{"type":"string","minLength":1,"maxLength":40},"url":{"type":"string","format":"uri","maxLength":500}},"additionalProperties":false},"maxItems":6,"description":"Rendered as a \"Follow <account>\" block after the button — GitHub, LinkedIn, X, YouTube… Empty or absent hides the block.","nullable":true}},"additionalProperties":false,"nullable":true},"email_change_verification_mode":{"anyOf":[{"type":"string","enum":["new_only"]},{"type":"string","enum":["old_and_new"]}],"description":"Policy for the user email-change flow. `new_only` (default) sends a single confirmation link to the new email. `old_and_new` requires the user to also click a link sent to the previous email before the swap takes effect — stricter, useful for tenants with higher-risk users."},"email_oauth_sync_policy":{"anyOf":[{"type":"string","enum":["preserve_manual"]},{"type":"string","enum":["always_sync"]}],"description":"Controls what happens to `user.email` on subsequent OAuth/federated logins when the user previously changed their email manually through this auth server (i.e. `user.email_change_locked_at` is set).\n\n- `preserve_manual` (default): the manually-set email wins. The federated provider's email is ignored on re-sync; `email` and `email_verified` are not touched. The identity link stays valid via `provider_user_id`, so the user can still log in with Google/etc.\n- `always_sync`: the federated provider's email is always written back, overwriting any manual change. Useful for tenants whose source of truth for identity is the IdP (corporate SSO, etc.)."},"login_methods":{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"},"description":"Order in which the hosted login screen renders the available methods. Entries are Connection resource ids (`connection_xxx`) or the literal `passkey`. Methods not listed keep the built-in order (passwordless, database, social) after the listed ones; ids that no longer resolve are ignored. Listing an `oidc` connection here is also the only way to surface it on the login screen — see resolveLoginMethods()."},"disabled_connections":{"type":"array","items":{"type":"string"},"description":"Connection resource ids (`connection_xxx`) this client does NOT offer. Removing a method here is enforced, not cosmetic: the connection disappears from the login screen AND `/authorize?connection=<id>` is refused for this client, the same as a connection whose `enabled_clients` excludes it. Use it to say \"this app does not do Google\"; use `enabled_clients` on the connection itself to say \"that connection is not for this app\". A connection has to pass both."},"passkey_login_enabled":{"type":"boolean","description":"Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."},"identifier_first":{"type":"boolean","description":"Ask for the email first and only then reveal the methods that apply to it, instead of showing every method up front. Defaults to false."},"remember_last_method":{"type":"boolean","description":"Surface the method the returning user chose last time. Defaults to false."},"passkey_promotion":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["offer"]}],"description":"Invite users to create a passkey right after a successful login with another method. `offer` shows a hosted screen once per `passkey_promotion_snooze_days`, at most `passkey_promotion_max_prompts` times, to users who have no second factor yet; it never blocks the login. Requires `passkey_login_enabled`. Defaults to `off`."},"passkey_promotion_snooze_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."},"passkey_promotion_max_prompts":{"type":"integer","minimum":0,"maximum":10,"description":"How many times a user is offered a passkey over their lifetime. `0` switches the offer off for this tenant without changing `passkey_promotion`. Defaults to 3."},"remember_me":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]}],"description":"Show a \"Remember me on this device\" checkbox on the password and email-code forms. `optional`: unchecked, the session ends when the browser closes; checked, it lasts `remember_me_days`. `off` (the default): no checkbox, every session lasts the built-in 30 days."},"remember_me_days":{"type":"integer","minimum":1,"maximum":365,"description":"How long a session lasts when the user ticked \"Remember me\". Defaults to 30."}},"additionalProperties":false,"description":"Which login methods the hosted login screen offers and in what order. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so a client can flip one flag without restating the rest. Absent everywhere = built-in defaults."},"login_flow":{"type":"string","description":"The login flow bound to this account (`loginflow_xxx`). Absent = the flow compiled from the settings. A Client may bind its own.","nullable":true},"mfa_policy":{"type":"object","properties":{"mode":{"anyOf":[{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]},{"type":"string","enum":["required"]}]}],"description":"`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."},"allowed_factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."},"remember_device_days":{"type":"integer","minimum":0,"maximum":365,"description":"How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."}},"additionalProperties":false,"description":"Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."},"recovery_channels":{"type":"object","properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."},"default":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."}},"additionalProperties":false,"description":"Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."},"default_country_iso":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 country assumed for phone numbers written without an international prefix (`636647460` → `+34…` with `ES`). Needed before SMS can reach anyone: most people type their number without a prefix.","nullable":true},"webauthn_rp_id":{"type":"string","description":"WebAuthn Relying Party ID for this tenant's passkeys. Defaults to the account domain. Set it to a registrable suffix you own (e.g. `acme.com`) when the login screen is served from more than one host — the RP ID is frozen into every credential at registration, so changing it afterwards invalidates every passkey already enrolled.","nullable":true},"logout_confirm_required":{"type":"boolean","description":"When true, `/logout` asks the End-User to confirm before ending the session unless the request carries a verified `id_token_hint`. Off by default — a plain link to `/logout` is otherwise enough to sign anyone out (logout CSRF)."},"canonical_host":{"type":"string","description":"The one host this tenant's OIDC issuer and discovery document are always minted with, regardless of which host actually served the request — must be the account's own platform host or one of its verified custom domains. `/authorize` and `/logout` 302 here from any other host of the tenant. Unset (the default): every URL uses whatever host served the request, as before this field existed. Changing it can invalidate every WebAuthn passkey already enrolled unless `webauthn_rp_id` already covers the new host.","nullable":true},"createdAt":{"type":"string","description":"AuthAccount creation date"},"updatedAt":{"type":"string","description":"AuthAccount updated date"}},"description":"AuthAccount"}}}},"400":{"description":"`team_required` — An Auth Account must belong to a project (team).\n\n`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.\n\n`invalid_name` — The name contains a link. `message` says which field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["team_required","already_exists","validation_error","invalid_name"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"409":{"description":"`account_limit_reached` — The project already has the maximum number of Auth Accounts.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_limit_reached"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"get":{"operationId":"account/list","summary":"List Accounts","tags":["account"],"description":"List Accounts owned by the requesting project/team. Supports cursor pagination and `expand`.","parameters":[{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Start from this cursor"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Start from this cursor"},{"schema":{"type":"number"},"in":"query","name":"pageSize","required":false,"description":"Size of the results array"},{"schema":{"type":"array","items":{"type":"string","maxLength":1000}},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `expand=user&expand=team`)."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"PaginatedResponse","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"type":"string","description":"next cursor","nullable":true},"results":{"type":"array","items":{"$ref":"#/components/schemas/AuthAccount"},"description":"List of results"}},"description":"PaginatedResponse","additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/account/{account_id}":{"get":{"operationId":"account/get","summary":"Get an Account","tags":["account"],"description":"Fetch a single Account by its id. Use `expand` to populate id-only fields (e.g. `default_connection`) inline.","parameters":[{"schema":{"type":"array","items":{"type":"string","maxLength":1000}},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `expand=user&expand=team`)."},{"schema":{"type":"string"},"in":"path","name":"account_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"AuthAccount","content":{"application/json":{"schema":{"type":"object","required":["id","name","domain","slug","callback_hostnames","token_signature","token_signing_alg","enabled_locales","notification_settings","createdAt"],"properties":{"id":{"type":"string","description":"AuthAccount ID"},"name":{"type":"string"},"domain":{"type":"string"},"slug":{"type":"string"},"logo_src":{"type":"string","nullable":true},"icon_src":{"type":"string","nullable":true},"callback_hostnames":{"type":"array","items":{"type":"string"}},"token_signature":{"type":"string","description":"Shared HMAC secret. Used when token_signing_alg is an HS* algorithm; ignored for RS*/ES*/PS*."},"token_signing_alg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign tokens issued by this Account. Today only RS256 is implemented."},"default_connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"enabled_locales":{"type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"},"description":"Free-form labels on the account. The dashboard stores the environment as an `env:<name>` tag (e.g. `env:production`, `env:staging`, `env:test`)."},"team":{"type":"string","nullable":true},"notification_settings":{"type":"object","required":["welcome_email_enabled","verify_email_auto_send"],"properties":{"welcome_email_enabled":{"type":"boolean","description":"Send the built-in welcome email on user.created","default":false},"verify_email_auto_send":{"type":"boolean","description":"Send a verification email automatically when a user is created with `email_verified=false` and an email address. The link in the email lands on `GET /verify-email?ticket=...` and flips `email_verified=true` with `email_verified_method=verification_flow`. When `false`, verification emails must be requested explicitly via `POST /user/:id/verify-email/start`.","default":false}},"additionalProperties":false},"email_reply_to":{"type":"string","format":"email","maxLength":320,"description":"Reply-To on every email this tenant sends to its users. Absent = no Reply-To, so a reply goes to the From address — which is a `no-reply@` mailbox nobody reads. Set it to your own support address if you want your users to be able to answer.","nullable":true},"email_from_name":{"type":"string","maxLength":100,"description":"Display name on every email this tenant sends to its users. Absent = the account `name`.","nullable":true},"email_from_address":{"type":"string","description":"Sender address of every email this tenant sends to its users. Absent = the platform default. Set by Faable only.","nullable":true},"welcome_email":{"type":"object","properties":{"cta_url":{"type":"string","format":"uri","maxLength":500,"description":"Where the welcome button sends the user. Defaults to `https://<first callback hostname>`. Point it at your app (not your marketing site) so a fresh user lands somewhere useful.","nullable":true},"body":{"type":"string","maxLength":600,"description":"Replaces the default one-line body (\"Your account on … is ready to go.\"). Plain text, sent verbatim in every enabled locale.","nullable":true},"social_links":{"type":"array","items":{"type":"object","required":["label","url"],"properties":{"label":{"type":"string","minLength":1,"maxLength":40},"url":{"type":"string","format":"uri","maxLength":500}},"additionalProperties":false},"maxItems":6,"description":"Rendered as a \"Follow <account>\" block after the button — GitHub, LinkedIn, X, YouTube… Empty or absent hides the block.","nullable":true}},"additionalProperties":false,"nullable":true},"email_change_verification_mode":{"anyOf":[{"type":"string","enum":["new_only"]},{"type":"string","enum":["old_and_new"]}],"description":"Policy for the user email-change flow. `new_only` (default) sends a single confirmation link to the new email. `old_and_new` requires the user to also click a link sent to the previous email before the swap takes effect — stricter, useful for tenants with higher-risk users."},"email_oauth_sync_policy":{"anyOf":[{"type":"string","enum":["preserve_manual"]},{"type":"string","enum":["always_sync"]}],"description":"Controls what happens to `user.email` on subsequent OAuth/federated logins when the user previously changed their email manually through this auth server (i.e. `user.email_change_locked_at` is set).\n\n- `preserve_manual` (default): the manually-set email wins. The federated provider's email is ignored on re-sync; `email` and `email_verified` are not touched. The identity link stays valid via `provider_user_id`, so the user can still log in with Google/etc.\n- `always_sync`: the federated provider's email is always written back, overwriting any manual change. Useful for tenants whose source of truth for identity is the IdP (corporate SSO, etc.)."},"login_methods":{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"},"description":"Order in which the hosted login screen renders the available methods. Entries are Connection resource ids (`connection_xxx`) or the literal `passkey`. Methods not listed keep the built-in order (passwordless, database, social) after the listed ones; ids that no longer resolve are ignored. Listing an `oidc` connection here is also the only way to surface it on the login screen — see resolveLoginMethods()."},"disabled_connections":{"type":"array","items":{"type":"string"},"description":"Connection resource ids (`connection_xxx`) this client does NOT offer. Removing a method here is enforced, not cosmetic: the connection disappears from the login screen AND `/authorize?connection=<id>` is refused for this client, the same as a connection whose `enabled_clients` excludes it. Use it to say \"this app does not do Google\"; use `enabled_clients` on the connection itself to say \"that connection is not for this app\". A connection has to pass both."},"passkey_login_enabled":{"type":"boolean","description":"Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."},"identifier_first":{"type":"boolean","description":"Ask for the email first and only then reveal the methods that apply to it, instead of showing every method up front. Defaults to false."},"remember_last_method":{"type":"boolean","description":"Surface the method the returning user chose last time. Defaults to false."},"passkey_promotion":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["offer"]}],"description":"Invite users to create a passkey right after a successful login with another method. `offer` shows a hosted screen once per `passkey_promotion_snooze_days`, at most `passkey_promotion_max_prompts` times, to users who have no second factor yet; it never blocks the login. Requires `passkey_login_enabled`. Defaults to `off`."},"passkey_promotion_snooze_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."},"passkey_promotion_max_prompts":{"type":"integer","minimum":0,"maximum":10,"description":"How many times a user is offered a passkey over their lifetime. `0` switches the offer off for this tenant without changing `passkey_promotion`. Defaults to 3."},"remember_me":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]}],"description":"Show a \"Remember me on this device\" checkbox on the password and email-code forms. `optional`: unchecked, the session ends when the browser closes; checked, it lasts `remember_me_days`. `off` (the default): no checkbox, every session lasts the built-in 30 days."},"remember_me_days":{"type":"integer","minimum":1,"maximum":365,"description":"How long a session lasts when the user ticked \"Remember me\". Defaults to 30."}},"additionalProperties":false,"description":"Which login methods the hosted login screen offers and in what order. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so a client can flip one flag without restating the rest. Absent everywhere = built-in defaults."},"login_flow":{"type":"string","description":"The login flow bound to this account (`loginflow_xxx`). Absent = the flow compiled from the settings. A Client may bind its own.","nullable":true},"mfa_policy":{"type":"object","properties":{"mode":{"anyOf":[{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]},{"type":"string","enum":["required"]}]}],"description":"`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."},"allowed_factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."},"remember_device_days":{"type":"integer","minimum":0,"maximum":365,"description":"How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."}},"additionalProperties":false,"description":"Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."},"recovery_channels":{"type":"object","properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."},"default":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."}},"additionalProperties":false,"description":"Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."},"default_country_iso":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 country assumed for phone numbers written without an international prefix (`636647460` → `+34…` with `ES`). Needed before SMS can reach anyone: most people type their number without a prefix.","nullable":true},"webauthn_rp_id":{"type":"string","description":"WebAuthn Relying Party ID for this tenant's passkeys. Defaults to the account domain. Set it to a registrable suffix you own (e.g. `acme.com`) when the login screen is served from more than one host — the RP ID is frozen into every credential at registration, so changing it afterwards invalidates every passkey already enrolled.","nullable":true},"logout_confirm_required":{"type":"boolean","description":"When true, `/logout` asks the End-User to confirm before ending the session unless the request carries a verified `id_token_hint`. Off by default — a plain link to `/logout` is otherwise enough to sign anyone out (logout CSRF)."},"canonical_host":{"type":"string","description":"The one host this tenant's OIDC issuer and discovery document are always minted with, regardless of which host actually served the request — must be the account's own platform host or one of its verified custom domains. `/authorize` and `/logout` 302 here from any other host of the tenant. Unset (the default): every URL uses whatever host served the request, as before this field existed. Changing it can invalidate every WebAuthn passkey already enrolled unless `webauthn_rp_id` already covers the new host.","nullable":true},"createdAt":{"type":"string","description":"AuthAccount creation date"},"updatedAt":{"type":"string","description":"AuthAccount updated date"}},"description":"AuthAccount"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"account/update","summary":"Update an Account","tags":["account"],"description":"Update the Account configuration such as branding (logo and icon URLs).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"logo_src":{"type":"string","nullable":true},"icon_src":{"type":"string","nullable":true},"callback_hostnames":{"type":"array","items":{"type":"string"}},"default_connection":{"type":"string","nullable":true},"enabled_locales":{"type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string","maxLength":50},"maxItems":20},"notification_settings":{"type":"object","properties":{"welcome_email_enabled":{"type":"boolean","description":"Send the built-in welcome email on user.created","default":false},"verify_email_auto_send":{"type":"boolean","description":"Send a verification email automatically when a user is created with `email_verified=false` and an email address. The link in the email lands on `GET /verify-email?ticket=...` and flips `email_verified=true` with `email_verified_method=verification_flow`. When `false`, verification emails must be requested explicitly via `POST /user/:id/verify-email/start`.","default":false}}},"email_reply_to":{"type":"string","format":"email","maxLength":320,"nullable":true},"email_from_name":{"type":"string","minLength":1,"maxLength":100,"nullable":true},"welcome_email":{"anyOf":[{"type":"object","properties":{"cta_url":{"type":"string","format":"uri","maxLength":500,"description":"Where the welcome button sends the user. Defaults to `https://<first callback hostname>`. Point it at your app (not your marketing site) so a fresh user lands somewhere useful.","nullable":true},"body":{"type":"string","maxLength":600,"description":"Replaces the default one-line body (\"Your account on … is ready to go.\"). Plain text, sent verbatim in every enabled locale.","nullable":true},"social_links":{"type":"array","items":{"type":"object","required":["label","url"],"properties":{"label":{"type":"string","minLength":1,"maxLength":40},"url":{"type":"string","format":"uri","maxLength":500}},"additionalProperties":false},"maxItems":6,"description":"Rendered as a \"Follow <account>\" block after the button — GitHub, LinkedIn, X, YouTube… Empty or absent hides the block.","nullable":true}},"additionalProperties":false},{"type":"null"}]},"login_methods":{"anyOf":[{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"},"description":"Order in which the hosted login screen renders the available methods. Entries are Connection resource ids (`connection_xxx`) or the literal `passkey`. Methods not listed keep the built-in order (passwordless, database, social) after the listed ones; ids that no longer resolve are ignored. Listing an `oidc` connection here is also the only way to surface it on the login screen — see resolveLoginMethods()."},"disabled_connections":{"type":"array","items":{"type":"string"},"description":"Connection resource ids (`connection_xxx`) this client does NOT offer. Removing a method here is enforced, not cosmetic: the connection disappears from the login screen AND `/authorize?connection=<id>` is refused for this client, the same as a connection whose `enabled_clients` excludes it. Use it to say \"this app does not do Google\"; use `enabled_clients` on the connection itself to say \"that connection is not for this app\". A connection has to pass both."},"passkey_login_enabled":{"type":"boolean","description":"Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."},"identifier_first":{"type":"boolean","description":"Ask for the email first and only then reveal the methods that apply to it, instead of showing every method up front. Defaults to false."},"remember_last_method":{"type":"boolean","description":"Surface the method the returning user chose last time. Defaults to false."},"passkey_promotion":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["offer"]}],"description":"Invite users to create a passkey right after a successful login with another method. `offer` shows a hosted screen once per `passkey_promotion_snooze_days`, at most `passkey_promotion_max_prompts` times, to users who have no second factor yet; it never blocks the login. Requires `passkey_login_enabled`. Defaults to `off`."},"passkey_promotion_snooze_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."},"passkey_promotion_max_prompts":{"type":"integer","minimum":0,"maximum":10,"description":"How many times a user is offered a passkey over their lifetime. `0` switches the offer off for this tenant without changing `passkey_promotion`. Defaults to 3."},"remember_me":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]}],"description":"Show a \"Remember me on this device\" checkbox on the password and email-code forms. `optional`: unchecked, the session ends when the browser closes; checked, it lasts `remember_me_days`. `off` (the default): no checkbox, every session lasts the built-in 30 days."},"remember_me_days":{"type":"integer","minimum":1,"maximum":365,"description":"How long a session lasts when the user ticked \"Remember me\". Defaults to 30."}},"additionalProperties":false,"description":"Which login methods the hosted login screen offers and in what order. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so a client can flip one flag without restating the rest. Absent everywhere = built-in defaults."},{"type":"null"}]},"mfa_policy":{"anyOf":[{"type":"object","properties":{"mode":{"anyOf":[{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]},{"type":"string","enum":["required"]}]}],"description":"`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."},"allowed_factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."},"remember_device_days":{"type":"integer","minimum":0,"maximum":365,"description":"How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."}},"additionalProperties":false,"description":"Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."},{"type":"null"}]},"recovery_channels":{"anyOf":[{"type":"object","properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."},"default":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."}},"additionalProperties":false,"description":"Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."},{"type":"null"}]},"default_country_iso":{"anyOf":[{"type":"string","minLength":2,"maxLength":2},{"type":"null"}]},"webauthn_rp_id":{"type":"string","nullable":true},"logout_confirm_required":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"canonical_host":{"type":"string","nullable":true},"login_flow":{"type":"string","nullable":true}},"description":"AuthAccountUpdate","additionalProperties":false}}},"description":"AuthAccountUpdate"},"parameters":[{"schema":{"type":"string"},"in":"path","name":"account_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"AuthAccount","content":{"application/json":{"schema":{"type":"object","required":["id","name","domain","slug","callback_hostnames","token_signature","token_signing_alg","enabled_locales","notification_settings","createdAt"],"properties":{"id":{"type":"string","description":"AuthAccount ID"},"name":{"type":"string"},"domain":{"type":"string"},"slug":{"type":"string"},"logo_src":{"type":"string","nullable":true},"icon_src":{"type":"string","nullable":true},"callback_hostnames":{"type":"array","items":{"type":"string"}},"token_signature":{"type":"string","description":"Shared HMAC secret. Used when token_signing_alg is an HS* algorithm; ignored for RS*/ES*/PS*."},"token_signing_alg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign tokens issued by this Account. Today only RS256 is implemented."},"default_connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"enabled_locales":{"type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"},"description":"Free-form labels on the account. The dashboard stores the environment as an `env:<name>` tag (e.g. `env:production`, `env:staging`, `env:test`)."},"team":{"type":"string","nullable":true},"notification_settings":{"type":"object","required":["welcome_email_enabled","verify_email_auto_send"],"properties":{"welcome_email_enabled":{"type":"boolean","description":"Send the built-in welcome email on user.created","default":false},"verify_email_auto_send":{"type":"boolean","description":"Send a verification email automatically when a user is created with `email_verified=false` and an email address. The link in the email lands on `GET /verify-email?ticket=...` and flips `email_verified=true` with `email_verified_method=verification_flow`. When `false`, verification emails must be requested explicitly via `POST /user/:id/verify-email/start`.","default":false}},"additionalProperties":false},"email_reply_to":{"type":"string","format":"email","maxLength":320,"description":"Reply-To on every email this tenant sends to its users. Absent = no Reply-To, so a reply goes to the From address — which is a `no-reply@` mailbox nobody reads. Set it to your own support address if you want your users to be able to answer.","nullable":true},"email_from_name":{"type":"string","maxLength":100,"description":"Display name on every email this tenant sends to its users. Absent = the account `name`.","nullable":true},"email_from_address":{"type":"string","description":"Sender address of every email this tenant sends to its users. Absent = the platform default. Set by Faable only.","nullable":true},"welcome_email":{"type":"object","properties":{"cta_url":{"type":"string","format":"uri","maxLength":500,"description":"Where the welcome button sends the user. Defaults to `https://<first callback hostname>`. Point it at your app (not your marketing site) so a fresh user lands somewhere useful.","nullable":true},"body":{"type":"string","maxLength":600,"description":"Replaces the default one-line body (\"Your account on … is ready to go.\"). Plain text, sent verbatim in every enabled locale.","nullable":true},"social_links":{"type":"array","items":{"type":"object","required":["label","url"],"properties":{"label":{"type":"string","minLength":1,"maxLength":40},"url":{"type":"string","format":"uri","maxLength":500}},"additionalProperties":false},"maxItems":6,"description":"Rendered as a \"Follow <account>\" block after the button — GitHub, LinkedIn, X, YouTube… Empty or absent hides the block.","nullable":true}},"additionalProperties":false,"nullable":true},"email_change_verification_mode":{"anyOf":[{"type":"string","enum":["new_only"]},{"type":"string","enum":["old_and_new"]}],"description":"Policy for the user email-change flow. `new_only` (default) sends a single confirmation link to the new email. `old_and_new` requires the user to also click a link sent to the previous email before the swap takes effect — stricter, useful for tenants with higher-risk users."},"email_oauth_sync_policy":{"anyOf":[{"type":"string","enum":["preserve_manual"]},{"type":"string","enum":["always_sync"]}],"description":"Controls what happens to `user.email` on subsequent OAuth/federated logins when the user previously changed their email manually through this auth server (i.e. `user.email_change_locked_at` is set).\n\n- `preserve_manual` (default): the manually-set email wins. The federated provider's email is ignored on re-sync; `email` and `email_verified` are not touched. The identity link stays valid via `provider_user_id`, so the user can still log in with Google/etc.\n- `always_sync`: the federated provider's email is always written back, overwriting any manual change. Useful for tenants whose source of truth for identity is the IdP (corporate SSO, etc.)."},"login_methods":{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"},"description":"Order in which the hosted login screen renders the available methods. Entries are Connection resource ids (`connection_xxx`) or the literal `passkey`. Methods not listed keep the built-in order (passwordless, database, social) after the listed ones; ids that no longer resolve are ignored. Listing an `oidc` connection here is also the only way to surface it on the login screen — see resolveLoginMethods()."},"disabled_connections":{"type":"array","items":{"type":"string"},"description":"Connection resource ids (`connection_xxx`) this client does NOT offer. Removing a method here is enforced, not cosmetic: the connection disappears from the login screen AND `/authorize?connection=<id>` is refused for this client, the same as a connection whose `enabled_clients` excludes it. Use it to say \"this app does not do Google\"; use `enabled_clients` on the connection itself to say \"that connection is not for this app\". A connection has to pass both."},"passkey_login_enabled":{"type":"boolean","description":"Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."},"identifier_first":{"type":"boolean","description":"Ask for the email first and only then reveal the methods that apply to it, instead of showing every method up front. Defaults to false."},"remember_last_method":{"type":"boolean","description":"Surface the method the returning user chose last time. Defaults to false."},"passkey_promotion":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["offer"]}],"description":"Invite users to create a passkey right after a successful login with another method. `offer` shows a hosted screen once per `passkey_promotion_snooze_days`, at most `passkey_promotion_max_prompts` times, to users who have no second factor yet; it never blocks the login. Requires `passkey_login_enabled`. Defaults to `off`."},"passkey_promotion_snooze_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."},"passkey_promotion_max_prompts":{"type":"integer","minimum":0,"maximum":10,"description":"How many times a user is offered a passkey over their lifetime. `0` switches the offer off for this tenant without changing `passkey_promotion`. Defaults to 3."},"remember_me":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]}],"description":"Show a \"Remember me on this device\" checkbox on the password and email-code forms. `optional`: unchecked, the session ends when the browser closes; checked, it lasts `remember_me_days`. `off` (the default): no checkbox, every session lasts the built-in 30 days."},"remember_me_days":{"type":"integer","minimum":1,"maximum":365,"description":"How long a session lasts when the user ticked \"Remember me\". Defaults to 30."}},"additionalProperties":false,"description":"Which login methods the hosted login screen offers and in what order. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so a client can flip one flag without restating the rest. Absent everywhere = built-in defaults."},"login_flow":{"type":"string","description":"The login flow bound to this account (`loginflow_xxx`). Absent = the flow compiled from the settings. A Client may bind its own.","nullable":true},"mfa_policy":{"type":"object","properties":{"mode":{"anyOf":[{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]},{"type":"string","enum":["required"]}]}],"description":"`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."},"allowed_factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."},"remember_device_days":{"type":"integer","minimum":0,"maximum":365,"description":"How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."}},"additionalProperties":false,"description":"Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."},"recovery_channels":{"type":"object","properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."},"default":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."}},"additionalProperties":false,"description":"Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."},"default_country_iso":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 country assumed for phone numbers written without an international prefix (`636647460` → `+34…` with `ES`). Needed before SMS can reach anyone: most people type their number without a prefix.","nullable":true},"webauthn_rp_id":{"type":"string","description":"WebAuthn Relying Party ID for this tenant's passkeys. Defaults to the account domain. Set it to a registrable suffix you own (e.g. `acme.com`) when the login screen is served from more than one host — the RP ID is frozen into every credential at registration, so changing it afterwards invalidates every passkey already enrolled.","nullable":true},"logout_confirm_required":{"type":"boolean","description":"When true, `/logout` asks the End-User to confirm before ending the session unless the request carries a verified `id_token_hint`. Off by default — a plain link to `/logout` is otherwise enough to sign anyone out (logout CSRF)."},"canonical_host":{"type":"string","description":"The one host this tenant's OIDC issuer and discovery document are always minted with, regardless of which host actually served the request — must be the account's own platform host or one of its verified custom domains. `/authorize` and `/logout` 302 here from any other host of the tenant. Unset (the default): every URL uses whatever host served the request, as before this field existed. Changing it can invalidate every WebAuthn passkey already enrolled unless `webauthn_rp_id` already covers the new host.","nullable":true},"createdAt":{"type":"string","description":"AuthAccount creation date"},"updatedAt":{"type":"string","description":"AuthAccount updated date"}},"description":"AuthAccount"}}}},"400":{"description":"`invalid_canonical_host` — `canonical_host` must be this account's own platform host or one of its verified custom domains.\n\n`canonical_host_breaks_passkeys` — `canonical_host` is outside the account's current WebAuthn RP ID — every passkey already enrolled would stop working. Set `webauthn_rp_id` in the same request to a suffix that covers both.\n\n`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.\n\n`invalid_name` — The name contains a link. `message` says which field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_canonical_host","canonical_host_breaks_passkeys","invalid_id","already_exists","validation_error","invalid_name"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"402":{"description":"`plan_required` — The setting requires a higher plan. `message` says which.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["plan_required"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"delete":{"operationId":"account/delete","summary":"Delete an Account","tags":["account"],"description":"Permanently removes an Account and its associated data. This action cannot be undone.","parameters":[{"schema":{"type":"string"},"in":"path","name":"account_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/account/host/{host}":{"get":{"operationId":"account/getByHost","summary":"Find Account by host","tags":["account"],"description":"Looks up the Account that owns the given hostname, checking both the built-in `*.auth.faable.link` slug and any verified custom domains. Returns only its public fields.","parameters":[{"schema":{"type":"string"},"in":"path","name":"host","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["id","name","domain","slug"],"properties":{"id":{"type":"string","description":"AuthAccount ID"},"name":{"type":"string"},"domain":{"type":"string"},"slug":{"type":"string"},"logo_src":{"type":"string","nullable":true},"icon_src":{"type":"string","nullable":true}},"additionalProperties":false}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/internal/mgmt-token":{"post":{"operationId":"internal/mintMgmtToken","summary":"Mint a tenant management token for a user (internal)","tags":["account"],"description":"Internal, service-to-service. Issues a short-lived Management API token bound to `account_id`, acting for the platform user `user_id`. Only the platform's own client_credentials clients listed in `MGMT_TOKEN_MINTER_CLIENT_IDS` may call it; the caller is responsible for checking that the user may manage that tenant. The target is taken from the body, never from `x-faableauth-account`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["account_id","user_id"],"properties":{"account_id":{"type":"string","pattern":"^account_[0-9a-f]{24}$","description":"The tenant the token will manage."},"user_id":{"type":"string","pattern":"^user_[0-9a-f]{24}$","description":"The platform (root) user the caller acts for. The caller has already checked that this user may manage the tenant."},"ttl":{"type":"integer","minimum":60,"maximum":900,"default":900,"description":"Lifetime in seconds."}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"x-internal":true,"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["access_token","token_type","expires_in"],"properties":{"access_token":{"type":"string"},"token_type":{"type":"string","enum":["Bearer"]},"expires_in":{"type":"integer"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","user_suspended","insufficient_scope"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).\n\n`not_found` — The resource does not exist in this account.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found","not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/recovery-channels/capabilities":{"get":{"operationId":"account/recoveryChannelsCapabilities","summary":"Recovery channel capabilities for this tenant","tags":["account"],"description":"Which recovery channels the platform can send, whether the plan allows the paid ones, this month's SMS usage against the included amount, and the tenant configuration as resolved. Drives the \"Recovery channels\" editor in the dashboard.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["provider","plan","usage","resolved","channels"],"properties":{"provider":{"type":"object","required":["sms","whatsapp"],"properties":{"sms":{"type":"boolean"},"whatsapp":{"type":"boolean"}},"description":"Whether the platform can send on each channel right now."},"plan":{"type":"object","required":["known","allows_messaging"],"properties":{"known":{"type":"boolean","description":"False when billing did not answer. Messaging channels are then treated as not allowed — the failure mode for a paid-per-message channel is \"do not send\", not \"allow\"."},"tier":{"type":"string"},"allows_messaging":{"type":"boolean"}}},"usage":{"type":"object","required":["month","sent","included","metered","hard_ceiling"],"properties":{"month":{"type":"string","description":"YYYY-MM, UTC."},"sent":{"type":"integer"},"included":{"type":"integer"},"metered":{"type":"boolean"},"hard_ceiling":{"type":"integer"}}},"resolved":{"type":"object","required":["enabled","default","visible"],"properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}},"default":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}}}},"channels":{"type":"array","items":{"type":"object","required":["channel","available"],"properties":{"channel":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"available":{"type":"boolean"},"reason":{"type":"string"}}},"description":"Tenant-level availability (configuration × platform × plan). The per-user step — verified phone, enrolled factor — is not applied here."}}}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/internal/usage/sms":{"get":{"operationId":"internal/smsUsage","summary":"SMS sent per tenant in a month (platform)","tags":["account"],"description":"Per-account count of recovery/verification SMS sent in the given month, read from the monthly quota counters. Consumed by the billing usage job. Platform token with `read:usage`.","parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"in":"query","name":"month","required":true,"description":"YYYY-MM, UTC. Defaults to the current month."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["month","rows"],"properties":{"month":{"type":"string"},"rows":{"type":"array","items":{"type":"object","required":["account_id","team","sent"],"properties":{"account_id":{"type":"string"},"team":{"anyOf":[{"type":"string"},{"type":"null"}]},"sent":{"type":"integer"}}}}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/internal/usage/sms/range":{"get":{"operationId":"internal/smsUsageRange","summary":"SMS a project sent in a time range (platform)","tags":["account"],"description":"Sum of the hourly counters of SMS and WhatsApp messages one project handed to the provider between `from` (first full hour after it) and `to`. At most 40 days back. Platform token with `read:usage`.","parameters":[{"schema":{"type":"string"},"in":"query","name":"team","required":true,"description":"The project to read, as `team_<hex>`."},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"from","required":true,"description":"Start of the range — the subscription period start. Counted from the first full hour after it."},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"to","required":false,"description":"End of the range. Defaults to now."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["team","from","to","sent"],"properties":{"team":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"},"sent":{"type":"integer"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/internal/usage/m2m":{"get":{"operationId":"internal/m2mUsage","summary":"M2M tokens served per project in a month (platform)","tags":["account"],"description":"Per-project count of successful client_credentials token responses in the given month (cached ones included), read from the monthly counters. Consumed by the billing usage job. Platform token with `read:usage`.","parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$"},"in":"query","name":"month","required":true,"description":"YYYY-MM, UTC."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["month","rows"],"properties":{"month":{"type":"string"},"rows":{"type":"array","items":{"type":"object","required":["team","tokens"],"properties":{"team":{"type":"string","description":"The project (`team_<hex>`), or the account id for a legacy account without one."},"tokens":{"type":"integer"}}}}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/internal/usage/m2m/range":{"get":{"operationId":"internal/m2mUsageRange","summary":"M2M tokens a project was served in a time range (platform)","tags":["account"],"description":"Sum of the hourly client_credentials counters of one project between `from` (first full hour after it) and `to`. At most 40 days back. Platform token with `read:usage`.","parameters":[{"schema":{"type":"string"},"in":"query","name":"team","required":true,"description":"The project to read, as `team_<hex>`."},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"from","required":true,"description":"Start of the range — the subscription period start. Counted from the first full hour after it."},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"to","required":false,"description":"End of the range. Defaults to now."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["team","from","to","tokens"],"properties":{"team":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"},"tokens":{"type":"integer"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/account/{account_id}/transfer":{"post":{"operationId":"account/transfer","summary":"Move an Account to another project","tags":["account"],"description":"Re-parents an Auth Account (tenant) under another Faable project. Only `Account.team` changes: the tenant keeps its id, slug, `*.auth.faable.link` domain, clients and secrets, and end-user logins are unaffected. `dry_run` returns the plan (what rides along + blockers) without writing. Platform credentials only. See arch/auth/tenant-project-transfer.md.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["to_project","reason"],"properties":{"to_project":{"type":"string","description":"Destination project (`project_<hex>`) or its team id (`team_<hex>`) — both spellings are accepted."},"reason":{"type":"string","minLength":3,"maxLength":500,"description":"Why the tenant is being moved. Recorded in the tenant's log and in ours."},"dry_run":{"type":"boolean","default":false,"description":"Return the plan (what rides along + blockers) without writing."},"allow_over_cap":{"type":"boolean","default":false,"description":"Move even if the destination ends up above the per-project cap on Auth Accounts."}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"account_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["moved","account_id","account_name","slug","domain","from_project","to_project","target_tenants_before","moves_with_it","blockers"],"properties":{"moved":{"type":"boolean"},"account_id":{"type":"string"},"account_name":{"type":"string"},"slug":{"type":"string"},"domain":{"type":"string"},"from_project":{"type":"string"},"to_project":{"type":"string"},"target_tenants_before":{"type":"array","items":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}}},"moves_with_it":{"type":"object","additionalProperties":{"type":"number"}},"blockers":{"type":"array","items":{"type":"object","required":["code","detail"],"properties":{"code":{"type":"string"},"detail":{"type":"string"}}}},"tenants_left_in_source":{"type":"number"},"tenants_in_target":{"type":"number"}}}}}},"400":{"description":"`invalid_project_id` — The project id is not of the form `project_<hex>` or `team_<hex>`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_project_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"409":{"description":"`account_transfer_refused` — The Auth Account cannot be moved to that project. `details.blockers` lists why.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_transfer_refused"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/account/{account_id}/email_sender":{"post":{"operationId":"account/email_sender","summary":"Set an Account's sender address","tags":["account"],"description":"Sets the From address of every email this Auth Account sends to its users. The address must be in a domain the platform signs email for. `null` returns to the platform default. Platform credentials only.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["address"],"properties":{"address":{"type":"string","format":"email","maxLength":320,"nullable":true}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"account_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"AuthAccount","content":{"application/json":{"schema":{"type":"object","required":["id","name","domain","slug","callback_hostnames","token_signature","token_signing_alg","enabled_locales","notification_settings","createdAt"],"properties":{"id":{"type":"string","description":"AuthAccount ID"},"name":{"type":"string"},"domain":{"type":"string"},"slug":{"type":"string"},"logo_src":{"type":"string","nullable":true},"icon_src":{"type":"string","nullable":true},"callback_hostnames":{"type":"array","items":{"type":"string"}},"token_signature":{"type":"string","description":"Shared HMAC secret. Used when token_signing_alg is an HS* algorithm; ignored for RS*/ES*/PS*."},"token_signing_alg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign tokens issued by this Account. Today only RS256 is implemented."},"default_connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"enabled_locales":{"type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"},"description":"Free-form labels on the account. The dashboard stores the environment as an `env:<name>` tag (e.g. `env:production`, `env:staging`, `env:test`)."},"team":{"type":"string","nullable":true},"notification_settings":{"type":"object","required":["welcome_email_enabled","verify_email_auto_send"],"properties":{"welcome_email_enabled":{"type":"boolean","description":"Send the built-in welcome email on user.created","default":false},"verify_email_auto_send":{"type":"boolean","description":"Send a verification email automatically when a user is created with `email_verified=false` and an email address. The link in the email lands on `GET /verify-email?ticket=...` and flips `email_verified=true` with `email_verified_method=verification_flow`. When `false`, verification emails must be requested explicitly via `POST /user/:id/verify-email/start`.","default":false}},"additionalProperties":false},"email_reply_to":{"type":"string","format":"email","maxLength":320,"description":"Reply-To on every email this tenant sends to its users. Absent = no Reply-To, so a reply goes to the From address — which is a `no-reply@` mailbox nobody reads. Set it to your own support address if you want your users to be able to answer.","nullable":true},"email_from_name":{"type":"string","maxLength":100,"description":"Display name on every email this tenant sends to its users. Absent = the account `name`.","nullable":true},"email_from_address":{"type":"string","description":"Sender address of every email this tenant sends to its users. Absent = the platform default. Set by Faable only.","nullable":true},"welcome_email":{"type":"object","properties":{"cta_url":{"type":"string","format":"uri","maxLength":500,"description":"Where the welcome button sends the user. Defaults to `https://<first callback hostname>`. Point it at your app (not your marketing site) so a fresh user lands somewhere useful.","nullable":true},"body":{"type":"string","maxLength":600,"description":"Replaces the default one-line body (\"Your account on … is ready to go.\"). Plain text, sent verbatim in every enabled locale.","nullable":true},"social_links":{"type":"array","items":{"type":"object","required":["label","url"],"properties":{"label":{"type":"string","minLength":1,"maxLength":40},"url":{"type":"string","format":"uri","maxLength":500}},"additionalProperties":false},"maxItems":6,"description":"Rendered as a \"Follow <account>\" block after the button — GitHub, LinkedIn, X, YouTube… Empty or absent hides the block.","nullable":true}},"additionalProperties":false,"nullable":true},"email_change_verification_mode":{"anyOf":[{"type":"string","enum":["new_only"]},{"type":"string","enum":["old_and_new"]}],"description":"Policy for the user email-change flow. `new_only` (default) sends a single confirmation link to the new email. `old_and_new` requires the user to also click a link sent to the previous email before the swap takes effect — stricter, useful for tenants with higher-risk users."},"email_oauth_sync_policy":{"anyOf":[{"type":"string","enum":["preserve_manual"]},{"type":"string","enum":["always_sync"]}],"description":"Controls what happens to `user.email` on subsequent OAuth/federated logins when the user previously changed their email manually through this auth server (i.e. `user.email_change_locked_at` is set).\n\n- `preserve_manual` (default): the manually-set email wins. The federated provider's email is ignored on re-sync; `email` and `email_verified` are not touched. The identity link stays valid via `provider_user_id`, so the user can still log in with Google/etc.\n- `always_sync`: the federated provider's email is always written back, overwriting any manual change. Useful for tenants whose source of truth for identity is the IdP (corporate SSO, etc.)."},"login_methods":{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"},"description":"Order in which the hosted login screen renders the available methods. Entries are Connection resource ids (`connection_xxx`) or the literal `passkey`. Methods not listed keep the built-in order (passwordless, database, social) after the listed ones; ids that no longer resolve are ignored. Listing an `oidc` connection here is also the only way to surface it on the login screen — see resolveLoginMethods()."},"disabled_connections":{"type":"array","items":{"type":"string"},"description":"Connection resource ids (`connection_xxx`) this client does NOT offer. Removing a method here is enforced, not cosmetic: the connection disappears from the login screen AND `/authorize?connection=<id>` is refused for this client, the same as a connection whose `enabled_clients` excludes it. Use it to say \"this app does not do Google\"; use `enabled_clients` on the connection itself to say \"that connection is not for this app\". A connection has to pass both."},"passkey_login_enabled":{"type":"boolean","description":"Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."},"identifier_first":{"type":"boolean","description":"Ask for the email first and only then reveal the methods that apply to it, instead of showing every method up front. Defaults to false."},"remember_last_method":{"type":"boolean","description":"Surface the method the returning user chose last time. Defaults to false."},"passkey_promotion":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["offer"]}],"description":"Invite users to create a passkey right after a successful login with another method. `offer` shows a hosted screen once per `passkey_promotion_snooze_days`, at most `passkey_promotion_max_prompts` times, to users who have no second factor yet; it never blocks the login. Requires `passkey_login_enabled`. Defaults to `off`."},"passkey_promotion_snooze_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."},"passkey_promotion_max_prompts":{"type":"integer","minimum":0,"maximum":10,"description":"How many times a user is offered a passkey over their lifetime. `0` switches the offer off for this tenant without changing `passkey_promotion`. Defaults to 3."},"remember_me":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]}],"description":"Show a \"Remember me on this device\" checkbox on the password and email-code forms. `optional`: unchecked, the session ends when the browser closes; checked, it lasts `remember_me_days`. `off` (the default): no checkbox, every session lasts the built-in 30 days."},"remember_me_days":{"type":"integer","minimum":1,"maximum":365,"description":"How long a session lasts when the user ticked \"Remember me\". Defaults to 30."}},"additionalProperties":false,"description":"Which login methods the hosted login screen offers and in what order. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so a client can flip one flag without restating the rest. Absent everywhere = built-in defaults."},"login_flow":{"type":"string","description":"The login flow bound to this account (`loginflow_xxx`). Absent = the flow compiled from the settings. A Client may bind its own.","nullable":true},"mfa_policy":{"type":"object","properties":{"mode":{"anyOf":[{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]},{"type":"string","enum":["required"]}]}],"description":"`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."},"allowed_factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."},"remember_device_days":{"type":"integer","minimum":0,"maximum":365,"description":"How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."}},"additionalProperties":false,"description":"Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."},"recovery_channels":{"type":"object","properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."},"default":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."}},"additionalProperties":false,"description":"Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."},"default_country_iso":{"type":"string","minLength":2,"maxLength":2,"description":"ISO 3166-1 alpha-2 country assumed for phone numbers written without an international prefix (`636647460` → `+34…` with `ES`). Needed before SMS can reach anyone: most people type their number without a prefix.","nullable":true},"webauthn_rp_id":{"type":"string","description":"WebAuthn Relying Party ID for this tenant's passkeys. Defaults to the account domain. Set it to a registrable suffix you own (e.g. `acme.com`) when the login screen is served from more than one host — the RP ID is frozen into every credential at registration, so changing it afterwards invalidates every passkey already enrolled.","nullable":true},"logout_confirm_required":{"type":"boolean","description":"When true, `/logout` asks the End-User to confirm before ending the session unless the request carries a verified `id_token_hint`. Off by default — a plain link to `/logout` is otherwise enough to sign anyone out (logout CSRF)."},"canonical_host":{"type":"string","description":"The one host this tenant's OIDC issuer and discovery document are always minted with, regardless of which host actually served the request — must be the account's own platform host or one of its verified custom domains. `/authorize` and `/logout` 302 here from any other host of the tenant. Unset (the default): every URL uses whatever host served the request, as before this field existed. Changing it can invalidate every WebAuthn passkey already enrolled unless `webauthn_rp_id` already covers the new host.","nullable":true},"createdAt":{"type":"string","description":"AuthAccount creation date"},"updatedAt":{"type":"string","description":"AuthAccount updated date"}},"description":"AuthAccount"}}}},"400":{"description":"`email_sender_domain_not_allowed` — The sender address is not in a domain this platform signs email for (AUTH_EMAIL_SENDER_DOMAINS).\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["email_sender_domain_not_allowed","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/connection":{"get":{"operationId":"connection/list","summary":"List Connections","tags":["connection"],"description":"List Connections","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"string"},"in":"query","name":"connection_type","required":false,"description":"Exact match on `connection_type`. Equivalent to `?query=connection_type:<value>`."},{"schema":{"type":"string"},"in":"query","name":"enabled","required":false,"description":"Exact match on `enabled`. Equivalent to `?query=enabled:<value>`."},{"schema":{"type":"string"},"in":"query","name":"category","required":false,"description":"Exact match on `category`. Equivalent to `?query=category:<value>`."},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false,"description":"Full-text search across: `connection_name`, `connection_type`."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/Connection"}}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"connection/create","summary":"Create Connection","tags":["connection"],"description":"Create Connection","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["connection_name","connection_type"],"properties":{"connection_name":{"type":"string"},"connection_type":{"enum":["database","custom","github","figma","google_oauth2","microsoft","oidc","passwordless_email"]},"authorize_url":{"type":"string"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"issuer":{"type":"string"},"jwks_url":{"type":"string"},"scope":{"type":"array","items":{"type":"string"}},"enabled":{"type":"boolean"},"claims_mapping":{"type":"object","additionalProperties":{"type":"string"}},"authorize_params":{"type":"object","description":"Authorize params","default":{},"additionalProperties":{"type":"string"}},"email_oauth_sync_policy":{"anyOf":[{"type":"string","enum":["preserve_manual"]},{"type":"string","enum":["always_sync"]}],"description":"Controls what happens to `user.email` on subsequent OAuth/federated logins when the user previously changed their email manually through this auth server (i.e. `user.email_change_locked_at` is set).\n\n- `preserve_manual` (default): the manually-set email wins. The federated provider's email is ignored on re-sync; `email` and `email_verified` are not touched. The identity link stays valid via `provider_user_id`, so the user can still log in with Google/etc.\n- `always_sync`: the federated provider's email is always written back, overwriting any manual change. Useful for tenants whose source of truth for identity is the IdP (corporate SSO, etc.)."},"password_policy":{"type":"object","required":["level"],"properties":{"level":{"anyOf":[{"type":"string","enum":["none"]},{"type":"string","enum":["low"]},{"type":"string","enum":["fair"]},{"type":"string","enum":["good"]},{"type":"string","enum":["excellent"]}]},"min_length":{"type":"integer","minimum":1,"maximum":128},"require_lowercase":{"type":"boolean"},"require_uppercase":{"type":"boolean"},"require_number":{"type":"boolean"},"require_special":{"type":"boolean"}},"description":"Password policy enforced when writing a password through this database connection (password reset and admin set-password). `level` is an Auth0-style preset (none/low/fair/good/excellent); the dashboard expands it into the explicit rule fields when saving. The rule fields override the preset baseline. When unset on the connection, the server applies the `good` default. Does not affect login."},"disable_signup":{"type":"boolean"},"login_identifier":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["username"]},{"type":"string","enum":["email_or_username"]}],"description":"What users of this database connection sign in with: `email`, `username`, or `email_or_username` (email first, then username). The login looks up only the matching field, and the hosted login screen asks for it — validating the address format under `email`. New database connections default to `email`; a connection created before this setting existed behaves as `email_or_username`. Only meaningful on `database` connections."},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"ConnectionCreate","additionalProperties":false}}},"description":"ConnectionCreate"},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Connection","content":{"application/json":{"schema":{"type":"object","required":["id","connection_name","connection_type","authorize_url","token_url","userinfo_url","client_id","client_secret","issuer","jwks_url","response_type","enabled","enabled_clients","scope","authorize_params","is_using_default_credentials","account","createdAt"],"properties":{"id":{"type":"string","description":"Connection ID"},"connection_name":{"type":"string"},"connection_type":{"enum":["database","custom","github","figma","google_oauth2","microsoft","oidc","passwordless_email"]},"authorize_url":{"type":"string","default":null},"token_url":{"type":"string","default":null},"userinfo_url":{"type":"string","default":null},"client_id":{"type":"string","default":null},"client_secret":{"type":"string","default":null},"issuer":{"type":"string","default":null},"jwks_url":{"type":"string","default":null},"response_type":{"type":"string","default":null},"enabled":{"type":"boolean"},"enabled_clients":{"type":"array","items":{"type":"string"},"default":null,"nullable":true},"scope":{"type":"array","items":{"type":"string"},"default":[]},"authorize_params":{"type":"object","description":"Authorize params","default":{},"additionalProperties":{"type":"string"}},"claims_mapping":{"type":"object","additionalProperties":{"type":"string"}},"is_using_default_credentials":{"type":"boolean","readOnly":true,"description":"Derived: true when the connection_type has shared Faable defaults AND the tenant has not set its own client_id/client_secret. Read-only — set by the server on every read."},"email_oauth_sync_policy":{"anyOf":[{"type":"string","enum":["preserve_manual"]},{"type":"string","enum":["always_sync"]}],"description":"Per-connection override of `Account.email_oauth_sync_policy`. When set on a Connection, takes precedence over the Account-level setting for logins through this IdP. When unset (default), the Account-level policy applies. Use this to allow `always_sync` for a trusted IdP (e.g. corporate SSO) while keeping `preserve_manual` for others within the same tenant."},"password_policy":{"type":"object","required":["level"],"properties":{"level":{"anyOf":[{"type":"string","enum":["none"]},{"type":"string","enum":["low"]},{"type":"string","enum":["fair"]},{"type":"string","enum":["good"]},{"type":"string","enum":["excellent"]}]},"min_length":{"type":"integer","minimum":1,"maximum":128},"require_lowercase":{"type":"boolean"},"require_uppercase":{"type":"boolean"},"require_number":{"type":"boolean"},"require_special":{"type":"boolean"}},"description":"Password policy enforced when writing a password through this database connection (password reset and admin set-password). `level` is an Auth0-style preset (none/low/fair/good/excellent); the dashboard expands it into the explicit rule fields when saving. The rule fields override the preset baseline. When unset on the connection, the server applies the `good` default. Does not affect login."},"login_identifier":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["username"]},{"type":"string","enum":["email_or_username"]}],"description":"What users of this database connection sign in with: `email`, `username`, or `email_or_username` (email first, then username). The login looks up only the matching field, and the hosted login screen asks for it — validating the address format under `email`. New database connections default to `email`; a connection created before this setting existed behaves as `email_or_username`. Only meaningful on `database` connections."},"disable_signup":{"type":"boolean","description":"When true, the public self-service signup endpoint (`POST /dbconnections/signup`) rejects new registrations against this database connection with `403 signup_disabled`. Defaults to false (signup allowed). Only meaningful on `database` connections; mirrors Auth0 `disable_signup`."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ConnectionMetadata","default":{}},"createdAt":{"type":"string","description":"Connection creation date"},"updatedAt":{"type":"string","description":"Connection updated date"}},"description":"Connection"}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/connection/{connection_id}":{"get":{"operationId":"connection/get","summary":"Get Connection","tags":["connection"],"description":"Get Connection","parameters":[{"schema":{"type":"string"},"in":"path","name":"connection_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Connection","content":{"application/json":{"schema":{"type":"object","required":["id","connection_name","connection_type","authorize_url","token_url","userinfo_url","client_id","client_secret","issuer","jwks_url","response_type","enabled","enabled_clients","scope","authorize_params","is_using_default_credentials","account","createdAt"],"properties":{"id":{"type":"string","description":"Connection ID"},"connection_name":{"type":"string"},"connection_type":{"enum":["database","custom","github","figma","google_oauth2","microsoft","oidc","passwordless_email"]},"authorize_url":{"type":"string","default":null},"token_url":{"type":"string","default":null},"userinfo_url":{"type":"string","default":null},"client_id":{"type":"string","default":null},"client_secret":{"type":"string","default":null},"issuer":{"type":"string","default":null},"jwks_url":{"type":"string","default":null},"response_type":{"type":"string","default":null},"enabled":{"type":"boolean"},"enabled_clients":{"type":"array","items":{"type":"string"},"default":null,"nullable":true},"scope":{"type":"array","items":{"type":"string"},"default":[]},"authorize_params":{"type":"object","description":"Authorize params","default":{},"additionalProperties":{"type":"string"}},"claims_mapping":{"type":"object","additionalProperties":{"type":"string"}},"is_using_default_credentials":{"type":"boolean","readOnly":true,"description":"Derived: true when the connection_type has shared Faable defaults AND the tenant has not set its own client_id/client_secret. Read-only — set by the server on every read."},"email_oauth_sync_policy":{"anyOf":[{"type":"string","enum":["preserve_manual"]},{"type":"string","enum":["always_sync"]}],"description":"Per-connection override of `Account.email_oauth_sync_policy`. When set on a Connection, takes precedence over the Account-level setting for logins through this IdP. When unset (default), the Account-level policy applies. Use this to allow `always_sync` for a trusted IdP (e.g. corporate SSO) while keeping `preserve_manual` for others within the same tenant."},"password_policy":{"type":"object","required":["level"],"properties":{"level":{"anyOf":[{"type":"string","enum":["none"]},{"type":"string","enum":["low"]},{"type":"string","enum":["fair"]},{"type":"string","enum":["good"]},{"type":"string","enum":["excellent"]}]},"min_length":{"type":"integer","minimum":1,"maximum":128},"require_lowercase":{"type":"boolean"},"require_uppercase":{"type":"boolean"},"require_number":{"type":"boolean"},"require_special":{"type":"boolean"}},"description":"Password policy enforced when writing a password through this database connection (password reset and admin set-password). `level` is an Auth0-style preset (none/low/fair/good/excellent); the dashboard expands it into the explicit rule fields when saving. The rule fields override the preset baseline. When unset on the connection, the server applies the `good` default. Does not affect login."},"login_identifier":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["username"]},{"type":"string","enum":["email_or_username"]}],"description":"What users of this database connection sign in with: `email`, `username`, or `email_or_username` (email first, then username). The login looks up only the matching field, and the hosted login screen asks for it — validating the address format under `email`. New database connections default to `email`; a connection created before this setting existed behaves as `email_or_username`. Only meaningful on `database` connections."},"disable_signup":{"type":"boolean","description":"When true, the public self-service signup endpoint (`POST /dbconnections/signup`) rejects new registrations against this database connection with `403 signup_disabled`. Defaults to false (signup allowed). Only meaningful on `database` connections; mirrors Auth0 `disable_signup`."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ConnectionMetadata","default":{}},"createdAt":{"type":"string","description":"Connection creation date"},"updatedAt":{"type":"string","description":"Connection updated date"}},"description":"Connection"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"connection/update","summary":"Update Connection","tags":["connection"],"description":"Update Connection","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"connection_name":{"type":"string"},"enabled":{"type":"boolean"},"enabled_clients":{"type":"array","items":{"type":"string"}},"scope":{"type":"array","items":{"type":"string"}},"client_id":{"type":"string"},"client_secret":{"type":"string"},"issuer":{"type":"string"},"authorize_url":{"type":"string"},"token_url":{"type":"string"},"userinfo_url":{"type":"string"},"jwks_url":{"type":"string"},"response_type":{"type":"string"},"authorize_params":{"type":"object","description":"Authorize params","default":{},"additionalProperties":{"type":"string"}},"claims_mapping":{"type":"object","additionalProperties":{"type":"string"}},"email_oauth_sync_policy":{"anyOf":[{"type":"string","enum":["preserve_manual"]},{"type":"string","enum":["always_sync"]},{"type":"null"}]},"password_policy":{"anyOf":[{"type":"object","required":["level"],"properties":{"level":{"anyOf":[{"type":"string","enum":["none"]},{"type":"string","enum":["low"]},{"type":"string","enum":["fair"]},{"type":"string","enum":["good"]},{"type":"string","enum":["excellent"]}]},"min_length":{"type":"integer","minimum":1,"maximum":128},"require_lowercase":{"type":"boolean"},"require_uppercase":{"type":"boolean"},"require_number":{"type":"boolean"},"require_special":{"type":"boolean"}},"description":"Password policy enforced when writing a password through this database connection (password reset and admin set-password). `level` is an Auth0-style preset (none/low/fair/good/excellent); the dashboard expands it into the explicit rule fields when saving. The rule fields override the preset baseline. When unset on the connection, the server applies the `good` default. Does not affect login."},{"type":"null"}]},"disable_signup":{"type":"boolean"},"login_identifier":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["username"]},{"type":"string","enum":["email_or_username"]}],"description":"What users of this database connection sign in with: `email`, `username`, or `email_or_username` (email first, then username). The login looks up only the matching field, and the hosted login screen asks for it — validating the address format under `email`. New database connections default to `email`; a connection created before this setting existed behaves as `email_or_username`. Only meaningful on `database` connections."}},"description":"Partial update for a Connection. Only the supplied fields are modified. `connection_type` is intentionally excluded — changing it would orphan every identity already linked to this connection.","additionalProperties":false}}},"description":"Partial update for a Connection. Only the supplied fields are modified. `connection_type` is intentionally excluded — changing it would orphan every identity already linked to this connection."},"parameters":[{"schema":{"type":"string"},"in":"path","name":"connection_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Connection","content":{"application/json":{"schema":{"type":"object","required":["id","connection_name","connection_type","authorize_url","token_url","userinfo_url","client_id","client_secret","issuer","jwks_url","response_type","enabled","enabled_clients","scope","authorize_params","is_using_default_credentials","account","createdAt"],"properties":{"id":{"type":"string","description":"Connection ID"},"connection_name":{"type":"string"},"connection_type":{"enum":["database","custom","github","figma","google_oauth2","microsoft","oidc","passwordless_email"]},"authorize_url":{"type":"string","default":null},"token_url":{"type":"string","default":null},"userinfo_url":{"type":"string","default":null},"client_id":{"type":"string","default":null},"client_secret":{"type":"string","default":null},"issuer":{"type":"string","default":null},"jwks_url":{"type":"string","default":null},"response_type":{"type":"string","default":null},"enabled":{"type":"boolean"},"enabled_clients":{"type":"array","items":{"type":"string"},"default":null,"nullable":true},"scope":{"type":"array","items":{"type":"string"},"default":[]},"authorize_params":{"type":"object","description":"Authorize params","default":{},"additionalProperties":{"type":"string"}},"claims_mapping":{"type":"object","additionalProperties":{"type":"string"}},"is_using_default_credentials":{"type":"boolean","readOnly":true,"description":"Derived: true when the connection_type has shared Faable defaults AND the tenant has not set its own client_id/client_secret. Read-only — set by the server on every read."},"email_oauth_sync_policy":{"anyOf":[{"type":"string","enum":["preserve_manual"]},{"type":"string","enum":["always_sync"]}],"description":"Per-connection override of `Account.email_oauth_sync_policy`. When set on a Connection, takes precedence over the Account-level setting for logins through this IdP. When unset (default), the Account-level policy applies. Use this to allow `always_sync` for a trusted IdP (e.g. corporate SSO) while keeping `preserve_manual` for others within the same tenant."},"password_policy":{"type":"object","required":["level"],"properties":{"level":{"anyOf":[{"type":"string","enum":["none"]},{"type":"string","enum":["low"]},{"type":"string","enum":["fair"]},{"type":"string","enum":["good"]},{"type":"string","enum":["excellent"]}]},"min_length":{"type":"integer","minimum":1,"maximum":128},"require_lowercase":{"type":"boolean"},"require_uppercase":{"type":"boolean"},"require_number":{"type":"boolean"},"require_special":{"type":"boolean"}},"description":"Password policy enforced when writing a password through this database connection (password reset and admin set-password). `level` is an Auth0-style preset (none/low/fair/good/excellent); the dashboard expands it into the explicit rule fields when saving. The rule fields override the preset baseline. When unset on the connection, the server applies the `good` default. Does not affect login."},"login_identifier":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["username"]},{"type":"string","enum":["email_or_username"]}],"description":"What users of this database connection sign in with: `email`, `username`, or `email_or_username` (email first, then username). The login looks up only the matching field, and the hosted login screen asks for it — validating the address format under `email`. New database connections default to `email`; a connection created before this setting existed behaves as `email_or_username`. Only meaningful on `database` connections."},"disable_signup":{"type":"boolean","description":"When true, the public self-service signup endpoint (`POST /dbconnections/signup`) rejects new registrations against this database connection with `403 signup_disabled`. Defaults to false (signup allowed). Only meaningful on `database` connections; mirrors Auth0 `disable_signup`."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ConnectionMetadata","default":{}},"createdAt":{"type":"string","description":"Connection creation date"},"updatedAt":{"type":"string","description":"Connection updated date"}},"description":"Connection"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"delete":{"operationId":"connection/delete","summary":"Delete Connection","tags":["connection"],"description":"Delete Connection","parameters":[{"schema":{"type":"string"},"in":"path","name":"connection_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Connection","content":{"application/json":{"schema":{"type":"object","required":["id","connection_name","connection_type","authorize_url","token_url","userinfo_url","client_id","client_secret","issuer","jwks_url","response_type","enabled","enabled_clients","scope","authorize_params","is_using_default_credentials","account","createdAt"],"properties":{"id":{"type":"string","description":"Connection ID"},"connection_name":{"type":"string"},"connection_type":{"enum":["database","custom","github","figma","google_oauth2","microsoft","oidc","passwordless_email"]},"authorize_url":{"type":"string","default":null},"token_url":{"type":"string","default":null},"userinfo_url":{"type":"string","default":null},"client_id":{"type":"string","default":null},"client_secret":{"type":"string","default":null},"issuer":{"type":"string","default":null},"jwks_url":{"type":"string","default":null},"response_type":{"type":"string","default":null},"enabled":{"type":"boolean"},"enabled_clients":{"type":"array","items":{"type":"string"},"default":null,"nullable":true},"scope":{"type":"array","items":{"type":"string"},"default":[]},"authorize_params":{"type":"object","description":"Authorize params","default":{},"additionalProperties":{"type":"string"}},"claims_mapping":{"type":"object","additionalProperties":{"type":"string"}},"is_using_default_credentials":{"type":"boolean","readOnly":true,"description":"Derived: true when the connection_type has shared Faable defaults AND the tenant has not set its own client_id/client_secret. Read-only — set by the server on every read."},"email_oauth_sync_policy":{"anyOf":[{"type":"string","enum":["preserve_manual"]},{"type":"string","enum":["always_sync"]}],"description":"Per-connection override of `Account.email_oauth_sync_policy`. When set on a Connection, takes precedence over the Account-level setting for logins through this IdP. When unset (default), the Account-level policy applies. Use this to allow `always_sync` for a trusted IdP (e.g. corporate SSO) while keeping `preserve_manual` for others within the same tenant."},"password_policy":{"type":"object","required":["level"],"properties":{"level":{"anyOf":[{"type":"string","enum":["none"]},{"type":"string","enum":["low"]},{"type":"string","enum":["fair"]},{"type":"string","enum":["good"]},{"type":"string","enum":["excellent"]}]},"min_length":{"type":"integer","minimum":1,"maximum":128},"require_lowercase":{"type":"boolean"},"require_uppercase":{"type":"boolean"},"require_number":{"type":"boolean"},"require_special":{"type":"boolean"}},"description":"Password policy enforced when writing a password through this database connection (password reset and admin set-password). `level` is an Auth0-style preset (none/low/fair/good/excellent); the dashboard expands it into the explicit rule fields when saving. The rule fields override the preset baseline. When unset on the connection, the server applies the `good` default. Does not affect login."},"login_identifier":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["username"]},{"type":"string","enum":["email_or_username"]}],"description":"What users of this database connection sign in with: `email`, `username`, or `email_or_username` (email first, then username). The login looks up only the matching field, and the hosted login screen asks for it — validating the address format under `email`. New database connections default to `email`; a connection created before this setting existed behaves as `email_or_username`. Only meaningful on `database` connections."},"disable_signup":{"type":"boolean","description":"When true, the public self-service signup endpoint (`POST /dbconnections/signup`) rejects new registrations against this database connection with `403 signup_disabled`. Defaults to false (signup allowed). Only meaningful on `database` connections; mirrors Auth0 `disable_signup`."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ConnectionMetadata","default":{}},"createdAt":{"type":"string","description":"Connection creation date"},"updatedAt":{"type":"string","description":"Connection updated date"}},"description":"Connection"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/connection-types":{"get":{"operationId":"connection/types","summary":"List supported connection types","tags":["connection"],"description":"Returns the catalog of connection types this server supports, with the presentation metadata a UI needs (label, icon slug, category) and two derived capability flags: whether the provider comes preconfigured and whether Faable ships shared credentials for it. Public: contains no URLs and no credentials. Changes only between server releases — cache it.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ConnectionTypeInfo"}}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/login-options":{"get":{"operationId":"connection/loginOptions","summary":"Resolve the login methods for a client","tags":["connection"],"description":"Returns the ordered list of login methods the hosted login screen should offer for this client, already filtered by `connection.enabled` and `connection.enabled_clients`. `client_id` is the OAuth client_id; when omitted the server resolves one: the account's only client, or its only client with `callbacks` (the ones that could plausibly be behind a login screen), and failing that the first of those two lists in order. `client` comes back `null` only for an account with no clients at all, and `methods` is always resolved against the client that comes back.","parameters":[{"schema":{"type":"string"},"in":"query","name":"client_id","required":false},{"schema":{"type":"string"},"in":"query","name":"state","required":false,"description":"The login state the screen was opened with. When the flow put an identifier-first screen before this one, the identifier the user typed comes back as `identifier`."},{"schema":{"type":"string"},"in":"query","name":"last_used","required":false,"description":"The method key (`passkey` or a connection id) this browser used last time, as remembered by the hosted UI. Honoured only when the tenant enabled `remember_last_method`: that method is moved first and echoed as `last_used`."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["account","client","identifier_first","remember_last_method","remember_me","methods"],"properties":{"account":{"type":"object","required":["id","name","domain","logo_src","icon_src"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"domain":{"type":"string","description":"The tenant's auth host. The login screen talks to this host for every call it makes after the first paint, so it has to come back here: a screen that is served as a static bundle has no other way to learn it, and asking the management API for it is what used to force the hosted UI to hold a platform credential."},"logo_src":{"anyOf":[{"type":"string"},{"type":"null"}]},"icon_src":{"anyOf":[{"type":"string"},{"type":"null"}]}}},"client":{"anyOf":[{"type":"object","required":["id","client_id","name","origins"],"properties":{"id":{"type":"string"},"client_id":{"type":"string"},"name":{"type":"string"},"origins":{"type":"array","items":{"type":"string"},"description":"The http(s) origins this client registered in its `callbacks`, de-duplicated. A hosted screen uses it as the allow-list for a cross-origin `return_to`: the account security page is opened from the tenant's own application and has to offer a way back to it, which `redirect_to` (same-origin paths only) cannot express. Nothing secret — a redirect URI already travels in the query of every `/authorize` call."}}},{"type":"null"}],"description":"The client the methods were resolved for, or `null` when none could be determined (no `client_id` given and the account has zero or several clients)."},"identifier_first":{"type":"boolean"},"remember_last_method":{"type":"boolean"},"remember_me":{"type":"boolean","description":"Render a \"Remember me on this device\" checkbox on the password and email-code forms, and send its value as `remember_me` when posting the credential."},"identifier":{"type":"string","description":"What the user typed on the identifier-first screen, when the flow has one. Prefill the email field with it."},"last_used":{"type":"string","description":"The method key that was moved first because this browser used it last time (`remember_last_method`). Absent otherwise."},"methods":{"type":"array","items":{"$ref":"#/components/schemas/LoginOptionMethod"},"description":"Already filtered and ordered. Render them in this order; do not re-sort or re-filter client-side."}},"additionalProperties":false}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/login/identify":{"post":{"operationId":"login/identify","summary":"Leave the identifier-first screen","tags":["connection"],"description":"Records the identifier the user typed on the hosted `/flow/identify` screen and advances the login to the method chooser. `state` is the id the screen was opened with; it stays valid, so the chooser is reached with the same `?state=`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["state","identifier"],"properties":{"state":{"type":"string"},"identifier":{"type":"string","minLength":1,"maxLength":320}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"}}}}}},"400":{"description":"`invalid_request` — A required OAuth parameter is missing or two of them are incompatible. `message` says which.\n\n`invalid_state` — The `state` is missing, expired, or does not describe a resumable step.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_request","invalid_state","validation_error"]}}}]}}}},"401":{"description":"`state_mismatch` — The `state` belongs to another account, session, client or ceremony.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["state_mismatch"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/client":{"get":{"operationId":"client/list","summary":"List Clients","tags":["client"],"description":"List Clients","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false,"description":"Full-text search across: `name`, `description`, `client_id`."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/Client"}}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"client/create","summary":"Create Client","tags":["client"],"description":"Create Client","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"callbacks":{"type":"array","items":{"type":"string"}},"logout_urls":{"type":"array","items":{"type":"string"}},"web_origins":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"anyOf":[{"type":"string","enum":["authorization_code"]},{"type":"string","enum":["client_credentials"]},{"type":"string","enum":["refresh_token"]},{"type":"string","enum":["password"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/passwordless/otp"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:token-exchange"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:device_code"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-otp"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-recovery-code"]}],"description":"A grant the client may use at the token endpoint. An empty `grant_types` list allows every grant except `password`, which is always opt-in."},"uniqueItems":true},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"ClientCreate","additionalProperties":false}}},"description":"ClientCreate"},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Client","content":{"application/json":{"schema":{"type":"object","required":["id","name","description","client_id","client_secret","callbacks","logout_urls","web_origins","refresh_token","account","createdAt"],"properties":{"id":{"type":"string","description":"Client ID"},"name":{"type":"string"},"description":{"type":"string"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"callbacks":{"type":"array","items":{"type":"string"}},"logout_urls":{"type":"array","items":{"type":"string"}},"web_origins":{"type":"array","items":{"type":"string"},"description":"Origins allowed to make cross-origin (CORS) requests to the token and passwordless endpoints. Empty = any origin."},"refresh_token":{"type":"object","required":["expiration_mode","infinite_token_lifetime","token_lifetime"],"properties":{"expiration_mode":{"enum":["expire","not-expire"]},"infinite_token_lifetime":{"type":"boolean"},"token_lifetime":{"type":"number"}}},"access_token":{"type":"object","properties":{"token_lifetime":{"type":"number","minimum":60,"description":"Access token lifetime in seconds for tokens issued to this client. A resolved audience (Api) token_lifetime takes precedence. Omit to use the tenant default (24h)."}}},"client_uri":{"type":"string","nullable":true},"logo_uri":{"type":"string","nullable":true},"tos_uri":{"type":"string","nullable":true},"policy_uri":{"type":"string","nullable":true},"contacts":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_method":{"type":"string"},"application_type":{"type":"string"},"software_id":{"type":"string","nullable":true},"software_version":{"type":"string","nullable":true},"frontchannel_logout_uri":{"type":"string","nullable":true},"frontchannel_logout_session_required":{"type":"boolean"},"backchannel_logout_uri":{"type":"string","nullable":true},"backchannel_logout_session_required":{"type":"boolean"},"login_methods":{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"},"description":"Order in which the hosted login screen renders the available methods. Entries are Connection resource ids (`connection_xxx`) or the literal `passkey`. Methods not listed keep the built-in order (passwordless, database, social) after the listed ones; ids that no longer resolve are ignored. Listing an `oidc` connection here is also the only way to surface it on the login screen — see resolveLoginMethods()."},"disabled_connections":{"type":"array","items":{"type":"string"},"description":"Connection resource ids (`connection_xxx`) this client does NOT offer. Removing a method here is enforced, not cosmetic: the connection disappears from the login screen AND `/authorize?connection=<id>` is refused for this client, the same as a connection whose `enabled_clients` excludes it. Use it to say \"this app does not do Google\"; use `enabled_clients` on the connection itself to say \"that connection is not for this app\". A connection has to pass both."},"passkey_login_enabled":{"type":"boolean","description":"Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."},"identifier_first":{"type":"boolean","description":"Ask for the email first and only then reveal the methods that apply to it, instead of showing every method up front. Defaults to false."},"remember_last_method":{"type":"boolean","description":"Surface the method the returning user chose last time. Defaults to false."},"passkey_promotion":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["offer"]}],"description":"Invite users to create a passkey right after a successful login with another method. `offer` shows a hosted screen once per `passkey_promotion_snooze_days`, at most `passkey_promotion_max_prompts` times, to users who have no second factor yet; it never blocks the login. Requires `passkey_login_enabled`. Defaults to `off`."},"passkey_promotion_snooze_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."},"passkey_promotion_max_prompts":{"type":"integer","minimum":0,"maximum":10,"description":"How many times a user is offered a passkey over their lifetime. `0` switches the offer off for this tenant without changing `passkey_promotion`. Defaults to 3."},"remember_me":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]}],"description":"Show a \"Remember me on this device\" checkbox on the password and email-code forms. `optional`: unchecked, the session ends when the browser closes; checked, it lasts `remember_me_days`. `off` (the default): no checkbox, every session lasts the built-in 30 days."},"remember_me_days":{"type":"integer","minimum":1,"maximum":365,"description":"How long a session lasts when the user ticked \"Remember me\". Defaults to 30."}},"additionalProperties":false,"description":"Which login methods the hosted login screen offers and in what order. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so a client can flip one flag without restating the rest. Absent everywhere = built-in defaults."},"mfa_policy":{"type":"object","properties":{"mode":{"anyOf":[{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]},{"type":"string","enum":["required"]}]}],"description":"`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."},"allowed_factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."},"remember_device_days":{"type":"integer","minimum":0,"maximum":365,"description":"How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."}},"additionalProperties":false,"description":"Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."},"recovery_channels":{"type":"object","properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."},"default":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."}},"additionalProperties":false,"description":"Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."},"login_flow":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A login flow bound to this client (`loginflow_xxx`), overriding the account's. Absent = inherit."},"default_audience":{"type":"string","description":"Audience used when a client_credentials token request sends none (e.g. `faable:management:<account_id>`). An explicit `audience` in the request wins. Absent = `${iss}/userinfo`.","nullable":true},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ClientMetadata","default":{}},"createdAt":{"type":"string","description":"Client creation date"},"updatedAt":{"type":"string","description":"Client updated date"}},"description":"Client"}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/client/{client_id}":{"get":{"operationId":"client/get","summary":"Get Client","tags":["client"],"description":"Get Client","parameters":[{"schema":{"type":"string"},"in":"path","name":"client_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Client","content":{"application/json":{"schema":{"type":"object","required":["id","name","description","client_id","client_secret","callbacks","logout_urls","web_origins","refresh_token","account","createdAt"],"properties":{"id":{"type":"string","description":"Client ID"},"name":{"type":"string"},"description":{"type":"string"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"callbacks":{"type":"array","items":{"type":"string"}},"logout_urls":{"type":"array","items":{"type":"string"}},"web_origins":{"type":"array","items":{"type":"string"},"description":"Origins allowed to make cross-origin (CORS) requests to the token and passwordless endpoints. Empty = any origin."},"refresh_token":{"type":"object","required":["expiration_mode","infinite_token_lifetime","token_lifetime"],"properties":{"expiration_mode":{"enum":["expire","not-expire"]},"infinite_token_lifetime":{"type":"boolean"},"token_lifetime":{"type":"number"}}},"access_token":{"type":"object","properties":{"token_lifetime":{"type":"number","minimum":60,"description":"Access token lifetime in seconds for tokens issued to this client. A resolved audience (Api) token_lifetime takes precedence. Omit to use the tenant default (24h)."}}},"client_uri":{"type":"string","nullable":true},"logo_uri":{"type":"string","nullable":true},"tos_uri":{"type":"string","nullable":true},"policy_uri":{"type":"string","nullable":true},"contacts":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_method":{"type":"string"},"application_type":{"type":"string"},"software_id":{"type":"string","nullable":true},"software_version":{"type":"string","nullable":true},"frontchannel_logout_uri":{"type":"string","nullable":true},"frontchannel_logout_session_required":{"type":"boolean"},"backchannel_logout_uri":{"type":"string","nullable":true},"backchannel_logout_session_required":{"type":"boolean"},"login_methods":{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"},"description":"Order in which the hosted login screen renders the available methods. Entries are Connection resource ids (`connection_xxx`) or the literal `passkey`. Methods not listed keep the built-in order (passwordless, database, social) after the listed ones; ids that no longer resolve are ignored. Listing an `oidc` connection here is also the only way to surface it on the login screen — see resolveLoginMethods()."},"disabled_connections":{"type":"array","items":{"type":"string"},"description":"Connection resource ids (`connection_xxx`) this client does NOT offer. Removing a method here is enforced, not cosmetic: the connection disappears from the login screen AND `/authorize?connection=<id>` is refused for this client, the same as a connection whose `enabled_clients` excludes it. Use it to say \"this app does not do Google\"; use `enabled_clients` on the connection itself to say \"that connection is not for this app\". A connection has to pass both."},"passkey_login_enabled":{"type":"boolean","description":"Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."},"identifier_first":{"type":"boolean","description":"Ask for the email first and only then reveal the methods that apply to it, instead of showing every method up front. Defaults to false."},"remember_last_method":{"type":"boolean","description":"Surface the method the returning user chose last time. Defaults to false."},"passkey_promotion":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["offer"]}],"description":"Invite users to create a passkey right after a successful login with another method. `offer` shows a hosted screen once per `passkey_promotion_snooze_days`, at most `passkey_promotion_max_prompts` times, to users who have no second factor yet; it never blocks the login. Requires `passkey_login_enabled`. Defaults to `off`."},"passkey_promotion_snooze_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."},"passkey_promotion_max_prompts":{"type":"integer","minimum":0,"maximum":10,"description":"How many times a user is offered a passkey over their lifetime. `0` switches the offer off for this tenant without changing `passkey_promotion`. Defaults to 3."},"remember_me":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]}],"description":"Show a \"Remember me on this device\" checkbox on the password and email-code forms. `optional`: unchecked, the session ends when the browser closes; checked, it lasts `remember_me_days`. `off` (the default): no checkbox, every session lasts the built-in 30 days."},"remember_me_days":{"type":"integer","minimum":1,"maximum":365,"description":"How long a session lasts when the user ticked \"Remember me\". Defaults to 30."}},"additionalProperties":false,"description":"Which login methods the hosted login screen offers and in what order. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so a client can flip one flag without restating the rest. Absent everywhere = built-in defaults."},"mfa_policy":{"type":"object","properties":{"mode":{"anyOf":[{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]},{"type":"string","enum":["required"]}]}],"description":"`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."},"allowed_factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."},"remember_device_days":{"type":"integer","minimum":0,"maximum":365,"description":"How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."}},"additionalProperties":false,"description":"Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."},"recovery_channels":{"type":"object","properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."},"default":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."}},"additionalProperties":false,"description":"Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."},"login_flow":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A login flow bound to this client (`loginflow_xxx`), overriding the account's. Absent = inherit."},"default_audience":{"type":"string","description":"Audience used when a client_credentials token request sends none (e.g. `faable:management:<account_id>`). An explicit `audience` in the request wins. Absent = `${iss}/userinfo`.","nullable":true},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ClientMetadata","default":{}},"createdAt":{"type":"string","description":"Client creation date"},"updatedAt":{"type":"string","description":"Client updated date"}},"description":"Client"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"client/update","summary":"Update Client","tags":["client"],"description":"Update Client","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Human-readable client name shown in consent screens."},"description":{"type":"string","description":"Free-form description for internal admin use."},"callbacks":{"type":"array","items":{"type":"string"},"description":"Whitelist of allowed OAuth `redirect_uri` values."},"logout_urls":{"type":"array","items":{"type":"string"},"description":"Whitelist of allowed `post_logout_redirect_uri` values."},"web_origins":{"type":"array","items":{"type":"string"},"description":"Whitelist of origins (scheme://host[:port], no path) allowed to make cross-origin (CORS) requests to the token and passwordless endpoints. Empty allows any origin."},"refresh_token":{"type":"object","required":["expiration_mode","infinite_token_lifetime","token_lifetime"],"properties":{"expiration_mode":{"enum":["expire","not-expire"]},"infinite_token_lifetime":{"type":"boolean"},"token_lifetime":{"type":"number"}}},"access_token":{"type":"object","properties":{"token_lifetime":{"type":"number","minimum":60,"description":"Access token lifetime in seconds for tokens issued to this client. A resolved audience (Api) token_lifetime takes precedence. Omit to use the tenant default (24h)."}}},"client_uri":{"type":"string","nullable":true},"logo_uri":{"type":"string","nullable":true},"tos_uri":{"type":"string","nullable":true},"policy_uri":{"type":"string","nullable":true},"contacts":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"anyOf":[{"type":"string","enum":["authorization_code"]},{"type":"string","enum":["client_credentials"]},{"type":"string","enum":["refresh_token"]},{"type":"string","enum":["password"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/passwordless/otp"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:token-exchange"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:device_code"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-otp"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-recovery-code"]}],"description":"A grant the client may use at the token endpoint. An empty `grant_types` list allows every grant except `password`, which is always opt-in."},"uniqueItems":true,"description":"Grants this client may use at the token endpoint. Empty allows every grant except `password`, which must be listed to be used."},"response_types":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_method":{"type":"string"},"application_type":{"type":"string"},"software_id":{"type":"string","nullable":true},"software_version":{"type":"string","nullable":true},"frontchannel_logout_uri":{"type":"string","nullable":true},"frontchannel_logout_session_required":{"type":"boolean"},"backchannel_logout_uri":{"type":"string","nullable":true},"backchannel_logout_session_required":{"type":"boolean"},"login_methods":{"anyOf":[{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"},"description":"Order in which the hosted login screen renders the available methods. Entries are Connection resource ids (`connection_xxx`) or the literal `passkey`. Methods not listed keep the built-in order (passwordless, database, social) after the listed ones; ids that no longer resolve are ignored. Listing an `oidc` connection here is also the only way to surface it on the login screen — see resolveLoginMethods()."},"disabled_connections":{"type":"array","items":{"type":"string"},"description":"Connection resource ids (`connection_xxx`) this client does NOT offer. Removing a method here is enforced, not cosmetic: the connection disappears from the login screen AND `/authorize?connection=<id>` is refused for this client, the same as a connection whose `enabled_clients` excludes it. Use it to say \"this app does not do Google\"; use `enabled_clients` on the connection itself to say \"that connection is not for this app\". A connection has to pass both."},"passkey_login_enabled":{"type":"boolean","description":"Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."},"identifier_first":{"type":"boolean","description":"Ask for the email first and only then reveal the methods that apply to it, instead of showing every method up front. Defaults to false."},"remember_last_method":{"type":"boolean","description":"Surface the method the returning user chose last time. Defaults to false."},"passkey_promotion":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["offer"]}],"description":"Invite users to create a passkey right after a successful login with another method. `offer` shows a hosted screen once per `passkey_promotion_snooze_days`, at most `passkey_promotion_max_prompts` times, to users who have no second factor yet; it never blocks the login. Requires `passkey_login_enabled`. Defaults to `off`."},"passkey_promotion_snooze_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."},"passkey_promotion_max_prompts":{"type":"integer","minimum":0,"maximum":10,"description":"How many times a user is offered a passkey over their lifetime. `0` switches the offer off for this tenant without changing `passkey_promotion`. Defaults to 3."},"remember_me":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]}],"description":"Show a \"Remember me on this device\" checkbox on the password and email-code forms. `optional`: unchecked, the session ends when the browser closes; checked, it lasts `remember_me_days`. `off` (the default): no checkbox, every session lasts the built-in 30 days."},"remember_me_days":{"type":"integer","minimum":1,"maximum":365,"description":"How long a session lasts when the user ticked \"Remember me\". Defaults to 30."}},"additionalProperties":false,"description":"Which login methods the hosted login screen offers and in what order. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so a client can flip one flag without restating the rest. Absent everywhere = built-in defaults."},{"type":"null"}]},"mfa_policy":{"anyOf":[{"type":"object","properties":{"mode":{"anyOf":[{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]},{"type":"string","enum":["required"]}]}],"description":"`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."},"allowed_factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."},"remember_device_days":{"type":"integer","minimum":0,"maximum":365,"description":"How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."}},"additionalProperties":false,"description":"Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."},{"type":"null"}]},"recovery_channels":{"anyOf":[{"type":"object","properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."},"default":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."}},"additionalProperties":false,"description":"Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."},{"type":"null"}]},"login_flow":{"anyOf":[{"type":"string"},{"type":"null"}]},"default_audience":{"type":"string","minLength":1,"description":"Audience a client_credentials token request gets when it sends none. An explicit `audience` wins. `null` removes it.","nullable":true},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Free-form client metadata. Replaces the whole object — send the full merged value, not a partial delta. Omit it to leave the metadata untouched."}},"description":"Partial update for a Client. Only the supplied fields are modified. `client_id` and `client_secret` are not editable through this endpoint to prevent accidental rotation; use a dedicated endpoint when secret rotation is added.","additionalProperties":false}}},"description":"Partial update for a Client. Only the supplied fields are modified. `client_id` and `client_secret` are not editable through this endpoint to prevent accidental rotation; use a dedicated endpoint when secret rotation is added."},"parameters":[{"schema":{"type":"string"},"in":"path","name":"client_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Client","content":{"application/json":{"schema":{"type":"object","required":["id","name","description","client_id","client_secret","callbacks","logout_urls","web_origins","refresh_token","account","createdAt"],"properties":{"id":{"type":"string","description":"Client ID"},"name":{"type":"string"},"description":{"type":"string"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"callbacks":{"type":"array","items":{"type":"string"}},"logout_urls":{"type":"array","items":{"type":"string"}},"web_origins":{"type":"array","items":{"type":"string"},"description":"Origins allowed to make cross-origin (CORS) requests to the token and passwordless endpoints. Empty = any origin."},"refresh_token":{"type":"object","required":["expiration_mode","infinite_token_lifetime","token_lifetime"],"properties":{"expiration_mode":{"enum":["expire","not-expire"]},"infinite_token_lifetime":{"type":"boolean"},"token_lifetime":{"type":"number"}}},"access_token":{"type":"object","properties":{"token_lifetime":{"type":"number","minimum":60,"description":"Access token lifetime in seconds for tokens issued to this client. A resolved audience (Api) token_lifetime takes precedence. Omit to use the tenant default (24h)."}}},"client_uri":{"type":"string","nullable":true},"logo_uri":{"type":"string","nullable":true},"tos_uri":{"type":"string","nullable":true},"policy_uri":{"type":"string","nullable":true},"contacts":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_method":{"type":"string"},"application_type":{"type":"string"},"software_id":{"type":"string","nullable":true},"software_version":{"type":"string","nullable":true},"frontchannel_logout_uri":{"type":"string","nullable":true},"frontchannel_logout_session_required":{"type":"boolean"},"backchannel_logout_uri":{"type":"string","nullable":true},"backchannel_logout_session_required":{"type":"boolean"},"login_methods":{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"},"description":"Order in which the hosted login screen renders the available methods. Entries are Connection resource ids (`connection_xxx`) or the literal `passkey`. Methods not listed keep the built-in order (passwordless, database, social) after the listed ones; ids that no longer resolve are ignored. Listing an `oidc` connection here is also the only way to surface it on the login screen — see resolveLoginMethods()."},"disabled_connections":{"type":"array","items":{"type":"string"},"description":"Connection resource ids (`connection_xxx`) this client does NOT offer. Removing a method here is enforced, not cosmetic: the connection disappears from the login screen AND `/authorize?connection=<id>` is refused for this client, the same as a connection whose `enabled_clients` excludes it. Use it to say \"this app does not do Google\"; use `enabled_clients` on the connection itself to say \"that connection is not for this app\". A connection has to pass both."},"passkey_login_enabled":{"type":"boolean","description":"Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."},"identifier_first":{"type":"boolean","description":"Ask for the email first and only then reveal the methods that apply to it, instead of showing every method up front. Defaults to false."},"remember_last_method":{"type":"boolean","description":"Surface the method the returning user chose last time. Defaults to false."},"passkey_promotion":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["offer"]}],"description":"Invite users to create a passkey right after a successful login with another method. `offer` shows a hosted screen once per `passkey_promotion_snooze_days`, at most `passkey_promotion_max_prompts` times, to users who have no second factor yet; it never blocks the login. Requires `passkey_login_enabled`. Defaults to `off`."},"passkey_promotion_snooze_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."},"passkey_promotion_max_prompts":{"type":"integer","minimum":0,"maximum":10,"description":"How many times a user is offered a passkey over their lifetime. `0` switches the offer off for this tenant without changing `passkey_promotion`. Defaults to 3."},"remember_me":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]}],"description":"Show a \"Remember me on this device\" checkbox on the password and email-code forms. `optional`: unchecked, the session ends when the browser closes; checked, it lasts `remember_me_days`. `off` (the default): no checkbox, every session lasts the built-in 30 days."},"remember_me_days":{"type":"integer","minimum":1,"maximum":365,"description":"How long a session lasts when the user ticked \"Remember me\". Defaults to 30."}},"additionalProperties":false,"description":"Which login methods the hosted login screen offers and in what order. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so a client can flip one flag without restating the rest. Absent everywhere = built-in defaults."},"mfa_policy":{"type":"object","properties":{"mode":{"anyOf":[{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]},{"type":"string","enum":["required"]}]}],"description":"`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."},"allowed_factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."},"remember_device_days":{"type":"integer","minimum":0,"maximum":365,"description":"How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."}},"additionalProperties":false,"description":"Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."},"recovery_channels":{"type":"object","properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."},"default":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."}},"additionalProperties":false,"description":"Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."},"login_flow":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A login flow bound to this client (`loginflow_xxx`), overriding the account's. Absent = inherit."},"default_audience":{"type":"string","description":"Audience used when a client_credentials token request sends none (e.g. `faable:management:<account_id>`). An explicit `audience` in the request wins. Absent = `${iss}/userinfo`.","nullable":true},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ClientMetadata","default":{}},"createdAt":{"type":"string","description":"Client creation date"},"updatedAt":{"type":"string","description":"Client updated date"}},"description":"Client"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"delete":{"operationId":"client/delete","summary":"Delete Client","tags":["client"],"description":"Delete Client","parameters":[{"schema":{"type":"string"},"in":"path","name":"client_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Client","content":{"application/json":{"schema":{"type":"object","required":["id","name","description","client_id","client_secret","callbacks","logout_urls","web_origins","refresh_token","account","createdAt"],"properties":{"id":{"type":"string","description":"Client ID"},"name":{"type":"string"},"description":{"type":"string"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"callbacks":{"type":"array","items":{"type":"string"}},"logout_urls":{"type":"array","items":{"type":"string"}},"web_origins":{"type":"array","items":{"type":"string"},"description":"Origins allowed to make cross-origin (CORS) requests to the token and passwordless endpoints. Empty = any origin."},"refresh_token":{"type":"object","required":["expiration_mode","infinite_token_lifetime","token_lifetime"],"properties":{"expiration_mode":{"enum":["expire","not-expire"]},"infinite_token_lifetime":{"type":"boolean"},"token_lifetime":{"type":"number"}}},"access_token":{"type":"object","properties":{"token_lifetime":{"type":"number","minimum":60,"description":"Access token lifetime in seconds for tokens issued to this client. A resolved audience (Api) token_lifetime takes precedence. Omit to use the tenant default (24h)."}}},"client_uri":{"type":"string","nullable":true},"logo_uri":{"type":"string","nullable":true},"tos_uri":{"type":"string","nullable":true},"policy_uri":{"type":"string","nullable":true},"contacts":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_method":{"type":"string"},"application_type":{"type":"string"},"software_id":{"type":"string","nullable":true},"software_version":{"type":"string","nullable":true},"frontchannel_logout_uri":{"type":"string","nullable":true},"frontchannel_logout_session_required":{"type":"boolean"},"backchannel_logout_uri":{"type":"string","nullable":true},"backchannel_logout_session_required":{"type":"boolean"},"login_methods":{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"},"description":"Order in which the hosted login screen renders the available methods. Entries are Connection resource ids (`connection_xxx`) or the literal `passkey`. Methods not listed keep the built-in order (passwordless, database, social) after the listed ones; ids that no longer resolve are ignored. Listing an `oidc` connection here is also the only way to surface it on the login screen — see resolveLoginMethods()."},"disabled_connections":{"type":"array","items":{"type":"string"},"description":"Connection resource ids (`connection_xxx`) this client does NOT offer. Removing a method here is enforced, not cosmetic: the connection disappears from the login screen AND `/authorize?connection=<id>` is refused for this client, the same as a connection whose `enabled_clients` excludes it. Use it to say \"this app does not do Google\"; use `enabled_clients` on the connection itself to say \"that connection is not for this app\". A connection has to pass both."},"passkey_login_enabled":{"type":"boolean","description":"Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."},"identifier_first":{"type":"boolean","description":"Ask for the email first and only then reveal the methods that apply to it, instead of showing every method up front. Defaults to false."},"remember_last_method":{"type":"boolean","description":"Surface the method the returning user chose last time. Defaults to false."},"passkey_promotion":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["offer"]}],"description":"Invite users to create a passkey right after a successful login with another method. `offer` shows a hosted screen once per `passkey_promotion_snooze_days`, at most `passkey_promotion_max_prompts` times, to users who have no second factor yet; it never blocks the login. Requires `passkey_login_enabled`. Defaults to `off`."},"passkey_promotion_snooze_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."},"passkey_promotion_max_prompts":{"type":"integer","minimum":0,"maximum":10,"description":"How many times a user is offered a passkey over their lifetime. `0` switches the offer off for this tenant without changing `passkey_promotion`. Defaults to 3."},"remember_me":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]}],"description":"Show a \"Remember me on this device\" checkbox on the password and email-code forms. `optional`: unchecked, the session ends when the browser closes; checked, it lasts `remember_me_days`. `off` (the default): no checkbox, every session lasts the built-in 30 days."},"remember_me_days":{"type":"integer","minimum":1,"maximum":365,"description":"How long a session lasts when the user ticked \"Remember me\". Defaults to 30."}},"additionalProperties":false,"description":"Which login methods the hosted login screen offers and in what order. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so a client can flip one flag without restating the rest. Absent everywhere = built-in defaults."},"mfa_policy":{"type":"object","properties":{"mode":{"anyOf":[{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]},{"type":"string","enum":["required"]}]}],"description":"`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."},"allowed_factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."},"remember_device_days":{"type":"integer","minimum":0,"maximum":365,"description":"How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."}},"additionalProperties":false,"description":"Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."},"recovery_channels":{"type":"object","properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."},"default":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."}},"additionalProperties":false,"description":"Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."},"login_flow":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A login flow bound to this client (`loginflow_xxx`), overriding the account's. Absent = inherit."},"default_audience":{"type":"string","description":"Audience used when a client_credentials token request sends none (e.g. `faable:management:<account_id>`). An explicit `audience` in the request wins. Absent = `${iss}/userinfo`.","nullable":true},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ClientMetadata","default":{}},"createdAt":{"type":"string","description":"Client creation date"},"updatedAt":{"type":"string","description":"Client updated date"}},"description":"Client"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/client/{client_id}/rotate-secret":{"post":{"operationId":"client/rotateSecret","summary":"Rotate a Client's secret","tags":["client"],"description":"Generates a fresh `client_secret` for the given Client and persists it. The previous secret is invalidated immediately — there is no grace period. Returns the full Client; the dashboard must surface the new secret to the admin once. Existing client_credentials / authorization_code integrations using the old secret will start failing at the token endpoint immediately after this call.","parameters":[{"schema":{"type":"string"},"in":"path","name":"client_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Client","content":{"application/json":{"schema":{"type":"object","required":["id","name","description","client_id","client_secret","callbacks","logout_urls","web_origins","refresh_token","account","createdAt"],"properties":{"id":{"type":"string","description":"Client ID"},"name":{"type":"string"},"description":{"type":"string"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"callbacks":{"type":"array","items":{"type":"string"}},"logout_urls":{"type":"array","items":{"type":"string"}},"web_origins":{"type":"array","items":{"type":"string"},"description":"Origins allowed to make cross-origin (CORS) requests to the token and passwordless endpoints. Empty = any origin."},"refresh_token":{"type":"object","required":["expiration_mode","infinite_token_lifetime","token_lifetime"],"properties":{"expiration_mode":{"enum":["expire","not-expire"]},"infinite_token_lifetime":{"type":"boolean"},"token_lifetime":{"type":"number"}}},"access_token":{"type":"object","properties":{"token_lifetime":{"type":"number","minimum":60,"description":"Access token lifetime in seconds for tokens issued to this client. A resolved audience (Api) token_lifetime takes precedence. Omit to use the tenant default (24h)."}}},"client_uri":{"type":"string","nullable":true},"logo_uri":{"type":"string","nullable":true},"tos_uri":{"type":"string","nullable":true},"policy_uri":{"type":"string","nullable":true},"contacts":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_method":{"type":"string"},"application_type":{"type":"string"},"software_id":{"type":"string","nullable":true},"software_version":{"type":"string","nullable":true},"frontchannel_logout_uri":{"type":"string","nullable":true},"frontchannel_logout_session_required":{"type":"boolean"},"backchannel_logout_uri":{"type":"string","nullable":true},"backchannel_logout_session_required":{"type":"boolean"},"login_methods":{"type":"object","properties":{"order":{"type":"array","items":{"type":"string"},"description":"Order in which the hosted login screen renders the available methods. Entries are Connection resource ids (`connection_xxx`) or the literal `passkey`. Methods not listed keep the built-in order (passwordless, database, social) after the listed ones; ids that no longer resolve are ignored. Listing an `oidc` connection here is also the only way to surface it on the login screen — see resolveLoginMethods()."},"disabled_connections":{"type":"array","items":{"type":"string"},"description":"Connection resource ids (`connection_xxx`) this client does NOT offer. Removing a method here is enforced, not cosmetic: the connection disappears from the login screen AND `/authorize?connection=<id>` is refused for this client, the same as a connection whose `enabled_clients` excludes it. Use it to say \"this app does not do Google\"; use `enabled_clients` on the connection itself to say \"that connection is not for this app\". A connection has to pass both."},"passkey_login_enabled":{"type":"boolean","description":"Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."},"identifier_first":{"type":"boolean","description":"Ask for the email first and only then reveal the methods that apply to it, instead of showing every method up front. Defaults to false."},"remember_last_method":{"type":"boolean","description":"Surface the method the returning user chose last time. Defaults to false."},"passkey_promotion":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["offer"]}],"description":"Invite users to create a passkey right after a successful login with another method. `offer` shows a hosted screen once per `passkey_promotion_snooze_days`, at most `passkey_promotion_max_prompts` times, to users who have no second factor yet; it never blocks the login. Requires `passkey_login_enabled`. Defaults to `off`."},"passkey_promotion_snooze_days":{"type":"integer","minimum":0,"maximum":365,"description":"Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."},"passkey_promotion_max_prompts":{"type":"integer","minimum":0,"maximum":10,"description":"How many times a user is offered a passkey over their lifetime. `0` switches the offer off for this tenant without changing `passkey_promotion`. Defaults to 3."},"remember_me":{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]}],"description":"Show a \"Remember me on this device\" checkbox on the password and email-code forms. `optional`: unchecked, the session ends when the browser closes; checked, it lasts `remember_me_days`. `off` (the default): no checkbox, every session lasts the built-in 30 days."},"remember_me_days":{"type":"integer","minimum":1,"maximum":365,"description":"How long a session lasts when the user ticked \"Remember me\". Defaults to 30."}},"additionalProperties":false,"description":"Which login methods the hosted login screen offers and in what order. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so a client can flip one flag without restating the rest. Absent everywhere = built-in defaults."},"mfa_policy":{"type":"object","properties":{"mode":{"anyOf":[{"anyOf":[{"type":"string","enum":["off"]},{"type":"string","enum":["optional"]},{"type":"string","enum":["required"]}]}],"description":"`off` (default) never challenges. `optional` challenges a user who has enrolled a factor, and lets everyone else in. `required` challenges everyone and sends users with no factor through enrolment during login — it never locks anyone out in place."},"allowed_factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"Which kinds of second factor satisfy the policy. Empty or unset means all of them. A user whose only factor is of a kind not listed here is treated as having none."},"remember_device_days":{"type":"integer","minimum":0,"maximum":365,"description":"How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."}},"additionalProperties":false,"description":"Second-factor policy. Set on the Account as the tenant default; the same object on a Client overrides it **field by field**, so one app can require MFA without changing the rest."},"recovery_channels":{"type":"object","properties":{"enabled":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the tenant has turned on. `email` is always on regardless of this list: it is what every other channel falls back to. A channel listed here is still offered only if the platform can send it, the plan allows it and the user has a verified destination for it."},"default":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."},"visible":{"type":"array","items":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"description":"Channels the recovery screen offers the user to pick from, after they enter their identifier. Empty (the default) keeps today's behaviour: the default channel is used silently and the response never reveals whether the account exists. A non-empty list shows a picker with masked destinations — which does reveal that the account exists and which channels it has, the same trade-off Google and Microsoft make. A tenant decision."}},"additionalProperties":false,"description":"Password-recovery delivery channels. Set on the Account as the tenant default; the same object on a Client overrides it field by field, like `login_methods` and `mfa_policy`."},"login_flow":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A login flow bound to this client (`loginflow_xxx`), overriding the account's. Absent = inherit."},"default_audience":{"type":"string","description":"Audience used when a client_credentials token request sends none (e.g. `faable:management:<account_id>`). An explicit `audience` in the request wins. Absent = `${iss}/userinfo`.","nullable":true},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ClientMetadata","default":{}},"createdAt":{"type":"string","description":"Client creation date"},"updatedAt":{"type":"string","description":"Client updated date"}},"description":"Client"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`client_mismatch` — The client belongs to a different account.\n\n`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["client_mismatch","unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`client_not_found` — No client with that `client_id`.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["client_not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/user":{"get":{"operationId":"user/list","summary":"List Users","tags":["user"],"description":"List Users","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"string"},"in":"query","name":"email","required":false,"description":"Exact match on `email`. Equivalent to `?query=email:<value>`."},{"schema":{"type":"string"},"in":"query","name":"country_iso","required":false,"description":"Exact match on `country_iso`. Equivalent to `?query=country_iso:<value>`."},{"schema":{"type":"string"},"in":"query","name":"locale","required":false,"description":"Exact match on `locale`. Equivalent to `?query=locale:<value>`."},{"schema":{"type":"string"},"in":"query","name":"last_ip","required":false,"description":"Exact match on `last_ip`. Equivalent to `?query=last_ip:<value>`."},{"schema":{"type":"string"},"in":"query","name":"phone","required":false,"description":"Exact match on `phone`. Equivalent to `?query=phone:<value>`."},{"schema":{"type":"string"},"in":"query","name":"name","required":false,"description":"Exact match on `name`. Equivalent to `?query=name:<value>`."},{"schema":{"type":"string"},"in":"query","name":"email_verified","required":false,"description":"Exact match on `email_verified`. Equivalent to `?query=email_verified:<value>`."},{"schema":{"type":"string"},"in":"query","name":"suspended","required":false,"description":"Exact match on `suspended`. Equivalent to `?query=suspended:<value>`."},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false,"description":"Full-text search across: `name`, `email`, `phone`."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/User"}}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"user/create","summary":"Create User","tags":["user"],"description":"Create User","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"The user's full name."},"given_name":{"type":"string","description":"The user's given name."},"family_name":{"type":"string","description":"The user's family name."},"email":{"type":"string","description":"The user's email."},"phone":{"type":"string","description":"contact phone number"},"birth_date":{"type":"string","description":"The user's birth date"},"locale":{"type":"string","description":"The user's default locale lang."},"picture":{"type":"string","description":"A URI pointing to the user's picture."},"user_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"User Metadata"},"app_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"App Metadata"}},"description":"UserCreate","additionalProperties":false}}},"description":"UserCreate"},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User","content":{"application/json":{"schema":{"type":"object","required":["id","email_verified","phone_verified","logins_count","user_metadata","app_metadata","account","createdAt"],"properties":{"id":{"type":"string","description":"User ID"},"name":{"type":"string","description":"User name","nullable":true},"username":{"type":"string","description":"unique username for this user","nullable":true},"given_name":{"type":"string","description":"Given name","nullable":true},"family_name":{"type":"string","description":"Family name","nullable":true},"middle_name":{"type":"string","description":"Middle name (OIDC §5.1)","nullable":true},"nickname":{"type":"string","description":"Casual name. Distinct from given_name (e.g. \"Mike\" vs \"Michael\"). OIDC §5.1","nullable":true},"email":{"type":"string","description":"User email","nullable":true},"email_verified":{"type":"boolean","description":"true if email is verified","default":false},"email_verified_method":{"anyOf":[{"type":"string","enum":["manual"]},{"type":"string","enum":["verification_flow"]},{"type":"string","enum":["passwordless_otp"]},{"type":"string","enum":["team_invite"]},{"type":"string","enum":["email_change"]},{"type":"string","enum":["federated"]},{"type":"string","enum":["sms_otp"]},{"type":"string","enum":["password_reset"]},{"type":"string","enum":["import"]},{"type":"null"}],"description":"How `email_verified` was last set. `manual` — admin flipped the flag via `POST /user/:id`. `verification_flow` — user clicked the verification link. `passwordless_otp` — user completed passwordless OTP login. `team_invite` — user clicked a team invitation link. `email_change` — user confirmed a self-service email change. `federated` — verified by the external IdP on OAuth callback. `password_reset` — user completed the recovery link that was emailed to them (the `email` channel only). `import` — the user arrived verified from another provider through `POST /user/import` (asserted by whoever ran the migration). `null` — the field was cleared (admin un-verified the email)."},"email_verified_at":{"type":"string","description":"ISO 8601 timestamp of when `email_verified` was last flipped to true.","nullable":true},"email_change_locked_at":{"type":"string","description":"ISO 8601 timestamp of the user's last verified email change. When set, OAuth callbacks will not overwrite `email`/`email_verified` from the federated provider — the manually-chosen email wins.","nullable":true},"email_bounced_at":{"type":"string","description":"ISO 8601 timestamp of the last Postmark hard bounce / spam complaint for this address. When set, the email is treated as undeliverable and no further emails are sent to the user until the address changes.","nullable":true},"welcome_sent_at":{"type":"string","description":"ISO 8601 timestamp of the built-in welcome email send. Set once by the platform; guarantees at most one welcome per user.","nullable":true},"imported_at":{"type":"string","description":"ISO 8601 timestamp of when the user was created by `POST /user/import`. Imported users get none of the built-in new-user emails (verification, welcome, `user.created` notifications); `user.created` webhooks still fire and carry this field.","nullable":true},"passkey_prompted_at":{"type":"string","description":"ISO 8601 timestamp of the last time the hosted \"create a passkey\" offer was shown to this user after a login. Drives the snooze in `login_methods.passkey_promotion_snooze_days`.","nullable":true},"passkey_prompt_count":{"type":"integer","description":"How many times the passkey offer has been shown to this user. Capped by `login_methods.passkey_promotion_max_prompts`; reset by an administrator through `POST /user/:id/passkey-prompt/reset`."},"passkey_prompt_dismissed_at":{"type":"string","description":"ISO 8601 timestamp of the last time the user chose \"Not now\" on the passkey offer, as opposed to closing the tab.","nullable":true},"phone":{"type":"string","description":"contact phone number","nullable":true},"phone_e164":{"type":"boolean","description":"Whether `phone` is a valid E.164 number. Calculated on read, never stored: new writes are rejected unless they normalise, so a `false` here is a legacy row that no SMS can reach — fix the number, or set the account default country and write it again."},"phone_verified":{"type":"boolean","description":"phone is verified"},"phone_verified_method":{"anyOf":[{"type":"string","enum":["manual"]},{"type":"string","enum":["verification_flow"]},{"type":"string","enum":["passwordless_otp"]},{"type":"string","enum":["team_invite"]},{"type":"string","enum":["email_change"]},{"type":"string","enum":["federated"]},{"type":"string","enum":["sms_otp"]},{"type":"string","enum":["password_reset"]},{"type":"string","enum":["import"]},{"type":"null"}],"description":"How `phone_verified` was last set. Same enum as `email_verified_method`, plus `sms_otp` — the user typed a code sent by SMS (`POST /user/:id/verify-phone/start`)."},"phone_verified_at":{"type":"string","description":"ISO 8601 timestamp of when `phone_verified` was last flipped to true.","nullable":true},"country_iso":{"type":"string","description":"country iso code","nullable":true},"birth_date":{"type":"string","description":"user birth_date","nullable":true},"gender":{"type":"string","description":"User gender (free-form, OIDC §5.1)","nullable":true},"zoneinfo":{"type":"string","description":"IANA time-zone name (e.g. \"Europe/Madrid\"). OIDC §5.1 — used for the `zoneinfo` claim.","nullable":true},"locale":{"type":"string","description":"user main language","nullable":true},"region":{"type":"string","description":"customer region","nullable":true},"website":{"type":"string","description":"URL of the User's personal Web page or blog (OIDC §5.1)","nullable":true},"address":{"type":"object","properties":{"formatted":{"type":"string","description":"Full mailing address, formatted for display (may contain newlines)","nullable":true},"street_address":{"type":"string","nullable":true},"locality":{"type":"string","description":"City or locality","nullable":true},"region":{"type":"string","description":"State, province, or region","nullable":true},"postal_code":{"type":"string","nullable":true},"country":{"type":"string","description":"Country name","nullable":true}},"additionalProperties":false,"nullable":true},"picture":{"type":"string","description":"User picture url","nullable":true},"logins_count":{"type":"number","description":"Total number of logins this user has performed."},"last_ip":{"type":"string","description":"Last IP address from which this user logged in","nullable":true},"last_login":{"type":"string","description":"Last date and time this user logged in (ISO_8601 format)","nullable":true},"first_language":{"type":"string","description":"Primary Accept-Language subtag seen at this user's FIRST login (e.g. \"zh\"). Written once and never updated, so a later login whose language differs is visible as `event.stats.language_changed` in actions — a cross-session signal no single request can produce.","nullable":true},"suspended":{"type":"boolean","description":"true while the user is suspended. A suspended user is rejected at every login flow, token grant, session use and management-API call.","default":false},"suspended_at":{"type":"string","description":"ISO 8601 timestamp of when `suspended` was last flipped to true.","nullable":true},"suspended_reason":{"type":"string","description":"Free-form reason recorded when the user was suspended (e.g. \"abuse: RCE payload\").","nullable":true},"user_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"User Metadata","default":{}},"app_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"App Metadata","default":{}},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"UserMetadata","default":{}},"createdAt":{"type":"string","description":"User creation date"},"updatedAt":{"type":"string","description":"User updated date"}},"description":"User"}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.\n\n`invalid_phone` — The phone number is not E.164 and could not be resolved with the account default country. Include the country code.\n\n`invalid_name` — The name contains a link. `message` says which field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error","invalid_phone","invalid_name"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/user/{user_id}":{"get":{"operationId":"user/get","summary":"Get User","tags":["user"],"description":"Get User","parameters":[{"schema":{"type":"string"},"in":"path","name":"user_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User","content":{"application/json":{"schema":{"type":"object","required":["id","email_verified","phone_verified","logins_count","user_metadata","app_metadata","account","createdAt"],"properties":{"id":{"type":"string","description":"User ID"},"name":{"type":"string","description":"User name","nullable":true},"username":{"type":"string","description":"unique username for this user","nullable":true},"given_name":{"type":"string","description":"Given name","nullable":true},"family_name":{"type":"string","description":"Family name","nullable":true},"middle_name":{"type":"string","description":"Middle name (OIDC §5.1)","nullable":true},"nickname":{"type":"string","description":"Casual name. Distinct from given_name (e.g. \"Mike\" vs \"Michael\"). OIDC §5.1","nullable":true},"email":{"type":"string","description":"User email","nullable":true},"email_verified":{"type":"boolean","description":"true if email is verified","default":false},"email_verified_method":{"anyOf":[{"type":"string","enum":["manual"]},{"type":"string","enum":["verification_flow"]},{"type":"string","enum":["passwordless_otp"]},{"type":"string","enum":["team_invite"]},{"type":"string","enum":["email_change"]},{"type":"string","enum":["federated"]},{"type":"string","enum":["sms_otp"]},{"type":"string","enum":["password_reset"]},{"type":"string","enum":["import"]},{"type":"null"}],"description":"How `email_verified` was last set. `manual` — admin flipped the flag via `POST /user/:id`. `verification_flow` — user clicked the verification link. `passwordless_otp` — user completed passwordless OTP login. `team_invite` — user clicked a team invitation link. `email_change` — user confirmed a self-service email change. `federated` — verified by the external IdP on OAuth callback. `password_reset` — user completed the recovery link that was emailed to them (the `email` channel only). `import` — the user arrived verified from another provider through `POST /user/import` (asserted by whoever ran the migration). `null` — the field was cleared (admin un-verified the email)."},"email_verified_at":{"type":"string","description":"ISO 8601 timestamp of when `email_verified` was last flipped to true.","nullable":true},"email_change_locked_at":{"type":"string","description":"ISO 8601 timestamp of the user's last verified email change. When set, OAuth callbacks will not overwrite `email`/`email_verified` from the federated provider — the manually-chosen email wins.","nullable":true},"email_bounced_at":{"type":"string","description":"ISO 8601 timestamp of the last Postmark hard bounce / spam complaint for this address. When set, the email is treated as undeliverable and no further emails are sent to the user until the address changes.","nullable":true},"welcome_sent_at":{"type":"string","description":"ISO 8601 timestamp of the built-in welcome email send. Set once by the platform; guarantees at most one welcome per user.","nullable":true},"imported_at":{"type":"string","description":"ISO 8601 timestamp of when the user was created by `POST /user/import`. Imported users get none of the built-in new-user emails (verification, welcome, `user.created` notifications); `user.created` webhooks still fire and carry this field.","nullable":true},"passkey_prompted_at":{"type":"string","description":"ISO 8601 timestamp of the last time the hosted \"create a passkey\" offer was shown to this user after a login. Drives the snooze in `login_methods.passkey_promotion_snooze_days`.","nullable":true},"passkey_prompt_count":{"type":"integer","description":"How many times the passkey offer has been shown to this user. Capped by `login_methods.passkey_promotion_max_prompts`; reset by an administrator through `POST /user/:id/passkey-prompt/reset`."},"passkey_prompt_dismissed_at":{"type":"string","description":"ISO 8601 timestamp of the last time the user chose \"Not now\" on the passkey offer, as opposed to closing the tab.","nullable":true},"phone":{"type":"string","description":"contact phone number","nullable":true},"phone_e164":{"type":"boolean","description":"Whether `phone` is a valid E.164 number. Calculated on read, never stored: new writes are rejected unless they normalise, so a `false` here is a legacy row that no SMS can reach — fix the number, or set the account default country and write it again."},"phone_verified":{"type":"boolean","description":"phone is verified"},"phone_verified_method":{"anyOf":[{"type":"string","enum":["manual"]},{"type":"string","enum":["verification_flow"]},{"type":"string","enum":["passwordless_otp"]},{"type":"string","enum":["team_invite"]},{"type":"string","enum":["email_change"]},{"type":"string","enum":["federated"]},{"type":"string","enum":["sms_otp"]},{"type":"string","enum":["password_reset"]},{"type":"string","enum":["import"]},{"type":"null"}],"description":"How `phone_verified` was last set. Same enum as `email_verified_method`, plus `sms_otp` — the user typed a code sent by SMS (`POST /user/:id/verify-phone/start`)."},"phone_verified_at":{"type":"string","description":"ISO 8601 timestamp of when `phone_verified` was last flipped to true.","nullable":true},"country_iso":{"type":"string","description":"country iso code","nullable":true},"birth_date":{"type":"string","description":"user birth_date","nullable":true},"gender":{"type":"string","description":"User gender (free-form, OIDC §5.1)","nullable":true},"zoneinfo":{"type":"string","description":"IANA time-zone name (e.g. \"Europe/Madrid\"). OIDC §5.1 — used for the `zoneinfo` claim.","nullable":true},"locale":{"type":"string","description":"user main language","nullable":true},"region":{"type":"string","description":"customer region","nullable":true},"website":{"type":"string","description":"URL of the User's personal Web page or blog (OIDC §5.1)","nullable":true},"address":{"type":"object","properties":{"formatted":{"type":"string","description":"Full mailing address, formatted for display (may contain newlines)","nullable":true},"street_address":{"type":"string","nullable":true},"locality":{"type":"string","description":"City or locality","nullable":true},"region":{"type":"string","description":"State, province, or region","nullable":true},"postal_code":{"type":"string","nullable":true},"country":{"type":"string","description":"Country name","nullable":true}},"additionalProperties":false,"nullable":true},"picture":{"type":"string","description":"User picture url","nullable":true},"logins_count":{"type":"number","description":"Total number of logins this user has performed."},"last_ip":{"type":"string","description":"Last IP address from which this user logged in","nullable":true},"last_login":{"type":"string","description":"Last date and time this user logged in (ISO_8601 format)","nullable":true},"first_language":{"type":"string","description":"Primary Accept-Language subtag seen at this user's FIRST login (e.g. \"zh\"). Written once and never updated, so a later login whose language differs is visible as `event.stats.language_changed` in actions — a cross-session signal no single request can produce.","nullable":true},"suspended":{"type":"boolean","description":"true while the user is suspended. A suspended user is rejected at every login flow, token grant, session use and management-API call.","default":false},"suspended_at":{"type":"string","description":"ISO 8601 timestamp of when `suspended` was last flipped to true.","nullable":true},"suspended_reason":{"type":"string","description":"Free-form reason recorded when the user was suspended (e.g. \"abuse: RCE payload\").","nullable":true},"user_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"User Metadata","default":{}},"app_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"App Metadata","default":{}},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"UserMetadata","default":{}},"createdAt":{"type":"string","description":"User creation date"},"updatedAt":{"type":"string","description":"User updated date"}},"description":"User"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"user/update","summary":"Update User","tags":["user"],"description":"Update User","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"name"},"given_name":{"type":"string","description":"given name"},"family_name":{"type":"string","description":"family name"},"email":{"type":"string","description":"Contact email. Changing the email to a new value automatically resets `email_verified` to false (and clears `email_verified_method` / `email_verified_at`) unless the same patch sets `email_verified` explicitly. The override is the documented way to import a pre-verified user from another IdP."},"email_verified":{"type":"boolean","description":"Flip the email verification flag. Setting `true` marks the email as verified by the admin (`email_verified_method=manual`). Setting `false` clears the verification metadata."},"phone":{"type":"string","description":"Contact phone number. Changing it to a new value automatically resets `phone_verified` to false (and clears `phone_verified_method` / `phone_verified_at`) unless the same patch sets `phone_verified` explicitly."},"phone_verified":{"type":"boolean","description":"Flip the phone verification flag. Setting `true` marks the phone as verified by the admin (`phone_verified_method=manual`)."},"suspended":{"type":"boolean","description":"Suspend (`true`) or reinstate (`false`) the user. While suspended, every login flow, token grant, session use and management-API call is rejected. Setting `true` auto-stamps `suspended_at`; setting `false` clears `suspended_at` / `suspended_reason`."},"suspended_reason":{"type":"string","maxLength":512,"description":"Free-form reason for the suspension (e.g. \"abuse: RCE payload\"). Stored verbatim for audit purposes."},"country_iso":{"type":"string","description":"user country as iso string"},"birth_date":{"type":"string","description":"user birth_date"},"locale":{"type":"string","description":"user main language","nullable":true},"picture":{"type":"string","description":"User picture url"},"user_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"User defined metadata"},"app_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"App defined metadata"}},"description":"UserUpdate","additionalProperties":false}}},"description":"UserUpdate"},"parameters":[{"schema":{"type":"string"},"in":"path","name":"user_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User","content":{"application/json":{"schema":{"type":"object","required":["id","email_verified","phone_verified","logins_count","user_metadata","app_metadata","account","createdAt"],"properties":{"id":{"type":"string","description":"User ID"},"name":{"type":"string","description":"User name","nullable":true},"username":{"type":"string","description":"unique username for this user","nullable":true},"given_name":{"type":"string","description":"Given name","nullable":true},"family_name":{"type":"string","description":"Family name","nullable":true},"middle_name":{"type":"string","description":"Middle name (OIDC §5.1)","nullable":true},"nickname":{"type":"string","description":"Casual name. Distinct from given_name (e.g. \"Mike\" vs \"Michael\"). OIDC §5.1","nullable":true},"email":{"type":"string","description":"User email","nullable":true},"email_verified":{"type":"boolean","description":"true if email is verified","default":false},"email_verified_method":{"anyOf":[{"type":"string","enum":["manual"]},{"type":"string","enum":["verification_flow"]},{"type":"string","enum":["passwordless_otp"]},{"type":"string","enum":["team_invite"]},{"type":"string","enum":["email_change"]},{"type":"string","enum":["federated"]},{"type":"string","enum":["sms_otp"]},{"type":"string","enum":["password_reset"]},{"type":"string","enum":["import"]},{"type":"null"}],"description":"How `email_verified` was last set. `manual` — admin flipped the flag via `POST /user/:id`. `verification_flow` — user clicked the verification link. `passwordless_otp` — user completed passwordless OTP login. `team_invite` — user clicked a team invitation link. `email_change` — user confirmed a self-service email change. `federated` — verified by the external IdP on OAuth callback. `password_reset` — user completed the recovery link that was emailed to them (the `email` channel only). `import` — the user arrived verified from another provider through `POST /user/import` (asserted by whoever ran the migration). `null` — the field was cleared (admin un-verified the email)."},"email_verified_at":{"type":"string","description":"ISO 8601 timestamp of when `email_verified` was last flipped to true.","nullable":true},"email_change_locked_at":{"type":"string","description":"ISO 8601 timestamp of the user's last verified email change. When set, OAuth callbacks will not overwrite `email`/`email_verified` from the federated provider — the manually-chosen email wins.","nullable":true},"email_bounced_at":{"type":"string","description":"ISO 8601 timestamp of the last Postmark hard bounce / spam complaint for this address. When set, the email is treated as undeliverable and no further emails are sent to the user until the address changes.","nullable":true},"welcome_sent_at":{"type":"string","description":"ISO 8601 timestamp of the built-in welcome email send. Set once by the platform; guarantees at most one welcome per user.","nullable":true},"imported_at":{"type":"string","description":"ISO 8601 timestamp of when the user was created by `POST /user/import`. Imported users get none of the built-in new-user emails (verification, welcome, `user.created` notifications); `user.created` webhooks still fire and carry this field.","nullable":true},"passkey_prompted_at":{"type":"string","description":"ISO 8601 timestamp of the last time the hosted \"create a passkey\" offer was shown to this user after a login. Drives the snooze in `login_methods.passkey_promotion_snooze_days`.","nullable":true},"passkey_prompt_count":{"type":"integer","description":"How many times the passkey offer has been shown to this user. Capped by `login_methods.passkey_promotion_max_prompts`; reset by an administrator through `POST /user/:id/passkey-prompt/reset`."},"passkey_prompt_dismissed_at":{"type":"string","description":"ISO 8601 timestamp of the last time the user chose \"Not now\" on the passkey offer, as opposed to closing the tab.","nullable":true},"phone":{"type":"string","description":"contact phone number","nullable":true},"phone_e164":{"type":"boolean","description":"Whether `phone` is a valid E.164 number. Calculated on read, never stored: new writes are rejected unless they normalise, so a `false` here is a legacy row that no SMS can reach — fix the number, or set the account default country and write it again."},"phone_verified":{"type":"boolean","description":"phone is verified"},"phone_verified_method":{"anyOf":[{"type":"string","enum":["manual"]},{"type":"string","enum":["verification_flow"]},{"type":"string","enum":["passwordless_otp"]},{"type":"string","enum":["team_invite"]},{"type":"string","enum":["email_change"]},{"type":"string","enum":["federated"]},{"type":"string","enum":["sms_otp"]},{"type":"string","enum":["password_reset"]},{"type":"string","enum":["import"]},{"type":"null"}],"description":"How `phone_verified` was last set. Same enum as `email_verified_method`, plus `sms_otp` — the user typed a code sent by SMS (`POST /user/:id/verify-phone/start`)."},"phone_verified_at":{"type":"string","description":"ISO 8601 timestamp of when `phone_verified` was last flipped to true.","nullable":true},"country_iso":{"type":"string","description":"country iso code","nullable":true},"birth_date":{"type":"string","description":"user birth_date","nullable":true},"gender":{"type":"string","description":"User gender (free-form, OIDC §5.1)","nullable":true},"zoneinfo":{"type":"string","description":"IANA time-zone name (e.g. \"Europe/Madrid\"). OIDC §5.1 — used for the `zoneinfo` claim.","nullable":true},"locale":{"type":"string","description":"user main language","nullable":true},"region":{"type":"string","description":"customer region","nullable":true},"website":{"type":"string","description":"URL of the User's personal Web page or blog (OIDC §5.1)","nullable":true},"address":{"type":"object","properties":{"formatted":{"type":"string","description":"Full mailing address, formatted for display (may contain newlines)","nullable":true},"street_address":{"type":"string","nullable":true},"locality":{"type":"string","description":"City or locality","nullable":true},"region":{"type":"string","description":"State, province, or region","nullable":true},"postal_code":{"type":"string","nullable":true},"country":{"type":"string","description":"Country name","nullable":true}},"additionalProperties":false,"nullable":true},"picture":{"type":"string","description":"User picture url","nullable":true},"logins_count":{"type":"number","description":"Total number of logins this user has performed."},"last_ip":{"type":"string","description":"Last IP address from which this user logged in","nullable":true},"last_login":{"type":"string","description":"Last date and time this user logged in (ISO_8601 format)","nullable":true},"first_language":{"type":"string","description":"Primary Accept-Language subtag seen at this user's FIRST login (e.g. \"zh\"). Written once and never updated, so a later login whose language differs is visible as `event.stats.language_changed` in actions — a cross-session signal no single request can produce.","nullable":true},"suspended":{"type":"boolean","description":"true while the user is suspended. A suspended user is rejected at every login flow, token grant, session use and management-API call.","default":false},"suspended_at":{"type":"string","description":"ISO 8601 timestamp of when `suspended` was last flipped to true.","nullable":true},"suspended_reason":{"type":"string","description":"Free-form reason recorded when the user was suspended (e.g. \"abuse: RCE payload\").","nullable":true},"user_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"User Metadata","default":{}},"app_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"App Metadata","default":{}},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"UserMetadata","default":{}},"createdAt":{"type":"string","description":"User creation date"},"updatedAt":{"type":"string","description":"User updated date"}},"description":"User"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.\n\n`invalid_phone` — The phone number is not E.164 and could not be resolved with the account default country. Include the country code.\n\n`invalid_name` — The name contains a link. `message` says which field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","already_exists","validation_error","invalid_phone","invalid_name"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"delete":{"operationId":"user/delete","summary":"Delete User","tags":["user"],"description":"Delete User","parameters":[{"schema":{"type":"string"},"in":"path","name":"user_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User","content":{"application/json":{"schema":{"type":"object","required":["id","email_verified","phone_verified","logins_count","user_metadata","app_metadata","account","createdAt"],"properties":{"id":{"type":"string","description":"User ID"},"name":{"type":"string","description":"User name","nullable":true},"username":{"type":"string","description":"unique username for this user","nullable":true},"given_name":{"type":"string","description":"Given name","nullable":true},"family_name":{"type":"string","description":"Family name","nullable":true},"middle_name":{"type":"string","description":"Middle name (OIDC §5.1)","nullable":true},"nickname":{"type":"string","description":"Casual name. Distinct from given_name (e.g. \"Mike\" vs \"Michael\"). OIDC §5.1","nullable":true},"email":{"type":"string","description":"User email","nullable":true},"email_verified":{"type":"boolean","description":"true if email is verified","default":false},"email_verified_method":{"anyOf":[{"type":"string","enum":["manual"]},{"type":"string","enum":["verification_flow"]},{"type":"string","enum":["passwordless_otp"]},{"type":"string","enum":["team_invite"]},{"type":"string","enum":["email_change"]},{"type":"string","enum":["federated"]},{"type":"string","enum":["sms_otp"]},{"type":"string","enum":["password_reset"]},{"type":"string","enum":["import"]},{"type":"null"}],"description":"How `email_verified` was last set. `manual` — admin flipped the flag via `POST /user/:id`. `verification_flow` — user clicked the verification link. `passwordless_otp` — user completed passwordless OTP login. `team_invite` — user clicked a team invitation link. `email_change` — user confirmed a self-service email change. `federated` — verified by the external IdP on OAuth callback. `password_reset` — user completed the recovery link that was emailed to them (the `email` channel only). `import` — the user arrived verified from another provider through `POST /user/import` (asserted by whoever ran the migration). `null` — the field was cleared (admin un-verified the email)."},"email_verified_at":{"type":"string","description":"ISO 8601 timestamp of when `email_verified` was last flipped to true.","nullable":true},"email_change_locked_at":{"type":"string","description":"ISO 8601 timestamp of the user's last verified email change. When set, OAuth callbacks will not overwrite `email`/`email_verified` from the federated provider — the manually-chosen email wins.","nullable":true},"email_bounced_at":{"type":"string","description":"ISO 8601 timestamp of the last Postmark hard bounce / spam complaint for this address. When set, the email is treated as undeliverable and no further emails are sent to the user until the address changes.","nullable":true},"welcome_sent_at":{"type":"string","description":"ISO 8601 timestamp of the built-in welcome email send. Set once by the platform; guarantees at most one welcome per user.","nullable":true},"imported_at":{"type":"string","description":"ISO 8601 timestamp of when the user was created by `POST /user/import`. Imported users get none of the built-in new-user emails (verification, welcome, `user.created` notifications); `user.created` webhooks still fire and carry this field.","nullable":true},"passkey_prompted_at":{"type":"string","description":"ISO 8601 timestamp of the last time the hosted \"create a passkey\" offer was shown to this user after a login. Drives the snooze in `login_methods.passkey_promotion_snooze_days`.","nullable":true},"passkey_prompt_count":{"type":"integer","description":"How many times the passkey offer has been shown to this user. Capped by `login_methods.passkey_promotion_max_prompts`; reset by an administrator through `POST /user/:id/passkey-prompt/reset`."},"passkey_prompt_dismissed_at":{"type":"string","description":"ISO 8601 timestamp of the last time the user chose \"Not now\" on the passkey offer, as opposed to closing the tab.","nullable":true},"phone":{"type":"string","description":"contact phone number","nullable":true},"phone_e164":{"type":"boolean","description":"Whether `phone` is a valid E.164 number. Calculated on read, never stored: new writes are rejected unless they normalise, so a `false` here is a legacy row that no SMS can reach — fix the number, or set the account default country and write it again."},"phone_verified":{"type":"boolean","description":"phone is verified"},"phone_verified_method":{"anyOf":[{"type":"string","enum":["manual"]},{"type":"string","enum":["verification_flow"]},{"type":"string","enum":["passwordless_otp"]},{"type":"string","enum":["team_invite"]},{"type":"string","enum":["email_change"]},{"type":"string","enum":["federated"]},{"type":"string","enum":["sms_otp"]},{"type":"string","enum":["password_reset"]},{"type":"string","enum":["import"]},{"type":"null"}],"description":"How `phone_verified` was last set. Same enum as `email_verified_method`, plus `sms_otp` — the user typed a code sent by SMS (`POST /user/:id/verify-phone/start`)."},"phone_verified_at":{"type":"string","description":"ISO 8601 timestamp of when `phone_verified` was last flipped to true.","nullable":true},"country_iso":{"type":"string","description":"country iso code","nullable":true},"birth_date":{"type":"string","description":"user birth_date","nullable":true},"gender":{"type":"string","description":"User gender (free-form, OIDC §5.1)","nullable":true},"zoneinfo":{"type":"string","description":"IANA time-zone name (e.g. \"Europe/Madrid\"). OIDC §5.1 — used for the `zoneinfo` claim.","nullable":true},"locale":{"type":"string","description":"user main language","nullable":true},"region":{"type":"string","description":"customer region","nullable":true},"website":{"type":"string","description":"URL of the User's personal Web page or blog (OIDC §5.1)","nullable":true},"address":{"type":"object","properties":{"formatted":{"type":"string","description":"Full mailing address, formatted for display (may contain newlines)","nullable":true},"street_address":{"type":"string","nullable":true},"locality":{"type":"string","description":"City or locality","nullable":true},"region":{"type":"string","description":"State, province, or region","nullable":true},"postal_code":{"type":"string","nullable":true},"country":{"type":"string","description":"Country name","nullable":true}},"additionalProperties":false,"nullable":true},"picture":{"type":"string","description":"User picture url","nullable":true},"logins_count":{"type":"number","description":"Total number of logins this user has performed."},"last_ip":{"type":"string","description":"Last IP address from which this user logged in","nullable":true},"last_login":{"type":"string","description":"Last date and time this user logged in (ISO_8601 format)","nullable":true},"first_language":{"type":"string","description":"Primary Accept-Language subtag seen at this user's FIRST login (e.g. \"zh\"). Written once and never updated, so a later login whose language differs is visible as `event.stats.language_changed` in actions — a cross-session signal no single request can produce.","nullable":true},"suspended":{"type":"boolean","description":"true while the user is suspended. A suspended user is rejected at every login flow, token grant, session use and management-API call.","default":false},"suspended_at":{"type":"string","description":"ISO 8601 timestamp of when `suspended` was last flipped to true.","nullable":true},"suspended_reason":{"type":"string","description":"Free-form reason recorded when the user was suspended (e.g. \"abuse: RCE payload\").","nullable":true},"user_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"User Metadata","default":{}},"app_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"App Metadata","default":{}},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"UserMetadata","default":{}},"createdAt":{"type":"string","description":"User creation date"},"updatedAt":{"type":"string","description":"User updated date"}},"description":"User"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/user/{user_id}/change-email":{"post":{"operationId":"user/changeEmail","summary":"Start the user's email change flow","tags":["user"],"description":"Initiates the email-change flow for the given user. Validates the new email, ensures it is not already in use in the tenant, revokes any pending change for the same user, creates a Ticket (`type='change_email'`), and sends a verification email to the new address. Re-authentication is not required — an active session/Bearer is enough. The change does NOT take effect until the user clicks the link in the email and confirms via `/change-email-confirm`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["new_email"],"properties":{"new_email":{"type":"string","description":"The email address the user wants to switch to."},"verification_mode":{"anyOf":[{"type":"string","enum":["new_only"]},{"type":"string","enum":["old_and_new"]}],"description":"Override the account-wide policy. Can only raise the bar: passing `old_and_new` forces double verification; `new_only` is ignored if the account requires `old_and_new`."},"redirect_uri":{"type":"string","description":"Where to send the user after they click the link in the verification email. The status of the operation is appended as a `?status=applied|pending_old` query param so the calling app can render the right screen. When omitted, the user lands on the auth host's `/flow/email-change-done` fallback."}},"additionalProperties":false,"description":"Payload for `POST /user/:user_id/change-email`. Triggers the verification flow: a Ticket is created and an email is sent to the new address with a confirmation link."}}},"description":"Payload for `POST /user/:user_id/change-email`. Triggers the verification flow: a Ticket is created and an email is sent to the new address with a confirmation link."},"parameters":[{"schema":{"type":"string"},"in":"path","name":"user_id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["status","ticket_id","verification_mode"],"properties":{"status":{"type":"string","enum":["verification_sent"]},"ticket_id":{"type":"string"},"verification_mode":{"anyOf":[{"type":"string","enum":["new_only"]},{"type":"string","enum":["old_and_new"]}]}}}}}},"400":{"description":"`invalid_email` — The email address is not valid.\n\n`same_email` — The new email is the same as the current one.\n\n`user_has_no_email` — The operation needs an email but the user has none.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_email","same_email","user_has_no_email","validation_error"]}}}]}}}},"401":{"description":"`invalid_token` — The bearer token is missing, malformed, expired or has no subject.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_token"]}}}]}}}},"403":{"description":"`not_owner` — The caller may only perform this operation on their own user.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_owner"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"409":{"description":"`email_taken` — Another user in this account already uses that email.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["email_taken"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/change-email-verify":{"get":{"operationId":"user_change_email_verify","summary":"Verify a change-email ticket and apply the change","tags":["user"],"description":"Entry point for the link sent to the new email. Consumes the ticket, applies the email swap (in `new_only` mode) or issues a secondary verification ticket to the old email (in `old_and_new` mode, first step). Redirects to the `redirect_uri` supplied at flow start with `?status=applied|pending_old` appended; falls back to `/flow/email-change-done` on the auth host when no `redirect_uri` was given.","parameters":[{"schema":{"type":"string"},"in":"query","name":"ticket","required":true}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_ticket` — The ticket does not exist or is not the kind this endpoint accepts.\n\n`ticket_expired` — The ticket is past its expiry.\n\n`ticket_used` — The ticket was already consumed.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_ticket","ticket_expired","ticket_used","validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/user/{user_id}/verify-email/start":{"post":{"operationId":"user/verifyEmailStart","summary":"Start the user's email verification flow","tags":["user"],"description":"Initiates the email-verification flow for the given user. Creates a `verify_email` Ticket and queues the verification email. Idempotent — returns `{status:'already_verified'}` without emitting a ticket if the user is already verified. Re-authentication is not required: an active session/Bearer is enough.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"redirect_uri":{"type":"string","description":"Where to send the user after they click the link. The status of the operation is appended as `?status=verified|already_verified|invalid_link|expired` so the calling app can render the right screen. When omitted, the user lands on the auth host's `/flow/verify-email-done` fallback."}},"additionalProperties":false,"description":"Optional payload for `POST /user/:user_id/verify-email/start`. Trigger a verification email send for the given user."}}},"description":"Optional payload for `POST /user/:user_id/verify-email/start`. Trigger a verification email send for the given user."},"parameters":[{"schema":{"type":"string"},"in":"path","name":"user_id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"anyOf":[{"type":"string","enum":["verification_sent"]},{"type":"string","enum":["already_verified"]}]},"ticket_id":{"type":"string"}}}}}},"400":{"description":"`user_has_no_email` — The operation needs an email but the user has none.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["user_has_no_email","validation_error"]}}}]}}}},"401":{"description":"`invalid_token` — The bearer token is missing, malformed, expired or has no subject.\n\n`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_token","unauthorized"]}}}]}}}},"403":{"description":"`not_owner` — The caller may only perform this operation on their own user.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_owner"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/verify-email":{"get":{"operationId":"user_verify_email_confirm","summary":"Verify a verify-email ticket and mark the user verified","tags":["user"],"description":"Entry point for the link sent to the user. Consumes the ticket, sets `email_verified=true` with `email_verified_method='verification_flow'`. Redirects to the `redirect_uri` supplied at start with `?status=verified|already_verified` appended; falls back to `/flow/verify-email-done` on the auth host when no `redirect_uri` was given. Invalid/expired tickets redirect to the same fallback with `?status=invalid_link` or `?status=expired`.","parameters":[{"schema":{"type":"string"},"in":"query","name":"ticket","required":true}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_ticket` — The ticket does not exist or is not the kind this endpoint accepts.\n\n`ticket_expired` — The ticket is past its expiry.\n\n`ticket_used` — The ticket was already consumed.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_ticket","ticket_expired","ticket_used","validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/user/{user_id}/password-setup":{"post":{"operationId":"user/passwordSetup","summary":"Send a password setup / reset email to the user","tags":["user"],"description":"Ensures the user has a database credential (provisioning a password-less one when missing, e.g. for users created via the management API) and creates a `changepassword` ticket, sending the standard password reset email. The link lands on the tenant's auth domain. Idempotent on the credential; each call emits a fresh ticket.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"connection":{"type":"string","description":"Optional connection_name to disambiguate when the tenant has more than one database connection. Defaults to the tenant database connection."},"channel":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"How to deliver it: `email` (the link) or `sms` / `whatsapp` (a 6-digit code to the verified phone on file). Falls back to the tenant default when not available for this user; the response says which channel was used."}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"user_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["status","credential_id","ticket_id","channel"],"properties":{"status":{"type":"string","enum":["sent"]},"credential_id":{"type":"string"},"ticket_id":{"type":"string"},"channel":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}}}}}},"400":{"description":"`invalid_client` — The `client_id` in the request does not name a client of this account.\n\n`invalid_connection` — The connection does not exist, is disabled, or is not of the type this flow needs.\n\n`no_database_connection` — The tenant has no database connection, so there is nowhere to store a password. Create one and retry.\n\n`user_has_no_email` — The operation needs an email but the user has none.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_client","invalid_connection","no_database_connection","user_has_no_email","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/user/{user_id}/tickets":{"get":{"operationId":"user/listTickets","summary":"List the tickets issued for a user","tags":["user"],"description":"Returns the password-reset / verification / invitation tickets issued for this user, newest first, each with its status and — while it is still usable — the link that was emailed. Intended for handing a recovery link over by another channel when the email does not reach the person. Requires `update:credentials`: the link grants the same account access that setting a password does, and each reveal is recorded in the audit log.","parameters":[{"schema":{"type":"string"},"in":"path","name":"user_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["results"],"properties":{"results":{"type":"array","items":{"type":"object","required":["id","type","status","email","createdAt","expires_at","ttl","channel"],"properties":{"id":{"type":"string"},"type":{"type":"string"},"status":{"anyOf":[{"type":"string","enum":["active"]},{"type":"string","enum":["expired"]},{"type":"string","enum":["used"]}]},"email":{"type":"string"},"createdAt":{"type":"string"},"expires_at":{"type":"string"},"ttl":{"type":"number"},"channel":{"type":"string","description":"How this ticket reaches the person: `email` (the link), `sms` / `whatsapp` (a code), `factor`. Older tickets are `email`."},"link":{"type":"string","description":"The link from the ticket email. Present only while the ticket is usable — a dead link is noise, not a recovery path."}}}}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/user/{user_id}/tickets/{ticket_id}/revoke":{"post":{"operationId":"user/revokeTicket","summary":"Revoke a ticket issued for a user","tags":["user"],"description":"Marks the ticket as consumed so its link stops working immediately. The counterpart of listing links: once a link has been copied out of the dashboard it can end up in the wrong chat window, and the TTL alone is too slow an answer.","parameters":[{"schema":{"type":"string"},"in":"path","name":"user_id","required":true},{"schema":{"type":"string"},"in":"path","name":"ticket_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["status","id"],"properties":{"status":{"anyOf":[{"type":"string","enum":["revoked"]},{"type":"string","enum":["already_used"]}]},"id":{"type":"string"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`ticket_not_found` — No ticket with that id belongs to this user.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["ticket_not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/user/{user_id}/verify-phone/start":{"post":{"operationId":"user/verifyPhoneStart","summary":"Send a verification code to the user's phone","tags":["user"],"description":"Sends a 6-digit code by SMS to the user's phone (optionally setting a new phone first) and returns an opaque `state` to confirm it with. Same authorization as `verify-email/start`: the user themself (session or bearer), an admin of another tenant, or a machine token. Counts against the tenant's monthly SMS allowance. Fails with 409 `sms_unavailable` when no SMS can go out (no provider, plan does not allow it, allowance exhausted).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"phone":{"type":"string","description":"Phone to verify. When given, it replaces the user's phone (normalised to E.164 with the account's `default_country_iso` as fallback) before the code is sent. When omitted, the code goes to the phone already on the user."}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"user_id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["status","destination_masked","state","expires_in"],"properties":{"status":{"type":"string","enum":["sent"]},"destination_masked":{"type":"string"},"state":{"type":"string","description":"Opaque handle to confirm with. It is what lets a tenant backend start the verification (M2M) and send the person to `/flow/verify-phone?state=…` without a session."},"expires_in":{"type":"integer"}}}}}},"400":{"description":"`invalid_phone` — The phone number is not E.164 and could not be resolved with the account default country. Include the country code.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_phone","validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"409":{"description":"`sms_failed` — The SMS provider rejected the message.\n\n`sms_unavailable` — This account cannot send SMS right now. The suffix says why: `sms_unavailable:plan`, `:quota`, `:provider`.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["sms_failed","sms_unavailable"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/verify-phone/confirm":{"post":{"operationId":"user/verifyPhoneConfirm","summary":"Confirm a phone verification code","tags":["user"],"description":"Marks the phone verified (`phone_verified_method: sms_otp`) when the code matches the pending verification identified by `state`. Five wrong codes burn the `state`; start again. No session needed: the `state` plus the code are the proof.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["state","code"],"properties":{"state":{"type":"string"},"code":{"type":"string","minLength":4,"maxLength":12}},"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["status","user_id"],"properties":{"status":{"type":"string","enum":["verified"]},"user_id":{"type":"string"}}}}}},"400":{"description":"`invalid_code` — The verification code is wrong, expired or already used.\n\n`invalid_state` — The `state` is missing, expired, or does not describe a resumable step.\n\n`phone_changed` — The phone on the user changed after the code was sent. Start again.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_code","invalid_state","phone_changed","validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/verify-phone/pending":{"get":{"operationId":"user/verifyPhonePending","summary":"What the verification screen should say","tags":["user"],"description":"The masked destination of a pending verification and how long its code is still good for. The screen needs this to say \"we sent a code to ···· 628\": the `destination_masked` of `start` goes to whoever called it, not to the person typing the code.","parameters":[{"schema":{"type":"string"},"in":"query","name":"state","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["destination_masked","expires_in","can_change"],"properties":{"destination_masked":{"type":"string"},"expires_in":{"type":"integer"},"can_change":{"type":"boolean","description":"Whether the screen may offer \"this is not my number\". False once the phone is verified: from then on the `state` must not be able to point it somewhere else."},"default_country":{"type":"string","description":"The account's default country (ISO 3166-1 alpha-2), so the screen can preselect the right dial code when the person corrects their number."}}}}}},"400":{"description":"`invalid_state` — The `state` is missing, expired, or does not describe a resumable step.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_state","validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/verify-phone/restart":{"post":{"operationId":"user/verifyPhoneRestart","summary":"Correct the number and send a new code","tags":["user"],"description":"Replaces the phone of a pending verification and sends a fresh code, returning a new `state`; the old one stops working. No session needed — the `state` is the proof — which is why it refuses with 409 `phone_already_verified` once the person has a verified phone: from that point a leaked `state` must not be able to move the number. Counts against the monthly SMS allowance like `start`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["state","phone"],"properties":{"state":{"type":"string"},"phone":{"type":"string","minLength":4}},"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["status","destination_masked","state","expires_in"],"properties":{"status":{"type":"string","enum":["sent"]},"destination_masked":{"type":"string"},"state":{"type":"string","description":"Opaque handle to confirm with. It is what lets a tenant backend start the verification (M2M) and send the person to `/flow/verify-phone?state=…` without a session."},"expires_in":{"type":"integer"}}}}}},"400":{"description":"`invalid_phone` — The phone number is not E.164 and could not be resolved with the account default country. Include the country code.\n\n`invalid_state` — The `state` is missing, expired, or does not describe a resumable step.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_phone","invalid_state","validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"409":{"description":"`phone_already_verified` — The user already has a verified phone; the number cannot be swapped from the screen.\n\n`sms_failed` — The SMS provider rejected the message.\n\n`sms_unavailable` — This account cannot send SMS right now. The suffix says why: `sms_unavailable:plan`, `:quota`, `:provider`.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["phone_already_verified","sms_failed","sms_unavailable"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/user/{user_id}/factors":{"get":{"operationId":"user/factors","summary":"List a user's second factors","tags":["user"],"description":"Management view of the security methods a user has enrolled. Carries no secret material — TOTP seeds and recovery-code hashes are redacted by the store, so this endpoint cannot be used to impersonate the user.","parameters":[{"schema":{"type":"string"},"in":"path","name":"user_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AdminFactor"}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/user/{user_id}/factors/{factor_id}":{"delete":{"operationId":"user/deleteFactor","summary":"Remove one of a user's second factors","tags":["user"],"description":"The support path out of a lockout: with the factor gone, a `required` policy sends the user through enrolment on their next login instead of refusing it.","parameters":[{"schema":{"type":"string"},"in":"path","name":"user_id","required":true},{"schema":{"type":"string"},"in":"path","name":"factor_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["deleted"],"properties":{"deleted":{"type":"boolean"}}}}}},"400":{"description":"`factor_not_found` — No factor with that id belongs to this user.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["factor_not_found","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/user/{user_id}/passkey-prompt/reset":{"post":{"operationId":"user/resetPasskeyPrompt","summary":"Reset a user's passkey offer counter","tags":["user"],"description":"The post-login \"create a passkey\" offer stops after `login_methods.passkey_promotion_max_prompts` times and snoozes between them. This clears both, so the user is offered a passkey again on their next login — for \"why does it keep asking\" and \"please ask me again\" alike.","parameters":[{"schema":{"type":"string"},"in":"path","name":"user_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["reset"],"properties":{"reset":{"type":"boolean"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/user/import":{"post":{"operationId":"user/import","summary":"Import users, with their password hashes","tags":["user"],"description":"Creates up to 500 users per request, for migrations from another provider. Each row can carry a `password_hash` exported from that provider (bcrypt, scrypt, Firebase scrypt, PBKDF2, argon2): the user signs in with their existing password and the hash is upgraded to argon2id on that first login. Fast digests (MD5, SHA-1, SHA-2) and low costs are refused per row with `weak_password_hash`. Rows are matched by email, so the same file can be sent twice: existing users are `skipped` unless `update_existing`. A bad row never fails the batch: each row gets its own `status` and `error`. Imported users get none of the built-in new-user emails. The response never contains a hash, and neither does the audit log.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["users"],"properties":{"users":{"type":"array","items":{"type":"object","required":["email"],"properties":{"email":{"type":"string","minLength":3,"description":"The user's email."},"email_verified":{"type":"boolean","description":"The email was verified at the previous provider. Recorded with `email_verified_method: import`."},"name":{"type":"string"},"given_name":{"type":"string"},"family_name":{"type":"string"},"phone":{"type":"string"},"birth_date":{"type":"string"},"locale":{"type":"string"},"picture":{"type":"string"},"user_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"User Metadata"},"app_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"App Metadata"},"password_hash":{"anyOf":[{"type":"string","minLength":1,"maxLength":1024,"description":"A self-describing hash: bcrypt (`$2a$`/`$2b$`/`$2y$`), argon2 PHC (`$argon2id$…`), scrypt PHC (`$scrypt$ln=…`), PBKDF2 (`$pbkdf2-sha256$i=…` or Django `pbkdf2_sha256$…`)."},{"type":"object","required":["algorithm","hash"],"properties":{"algorithm":{"type":"string","description":"`bcrypt`, `argon2id`, `argon2i`, `argon2d`, `scrypt`, `pbkdf2-sha256`, `pbkdf2-sha512` or `firebase-scrypt`."},"hash":{"type":"string","minLength":1,"maxLength":1024,"description":"The hash, or its base64 digest when `salt` is given."},"salt":{"type":"string","maxLength":512,"description":"Base64 salt."},"params":{"type":"object","description":"Algorithm parameters: `iterations` (pbkdf2); `N`, `r`, `p` (scrypt); `signer_key`, `salt_separator`, `rounds`, `mem_cost` (firebase-scrypt).","additionalProperties":{"anyOf":[{"type":"number"},{"type":"string","maxLength":512}]}}},"additionalProperties":false}],"description":"A password hash exported from another provider. Verified on the first login and upgraded to argon2id. Fast digests (MD5, SHA-1, SHA-2) and low costs are refused with `weak_password_hash`."},"identities":{"type":"array","items":{"type":"object","required":["connection","identity_id"],"properties":{"connection":{"type":"string","description":"connection_name of the social/enterprise connection."},"identity_id":{"type":"string","description":"The user's id at the provider (GitHub id, Google `sub`…), so their first social login finds this user instead of creating a new one."},"profile_data":{"type":"object","description":"Provider profile, stored as-is.","additionalProperties":{}}},"additionalProperties":false},"maxItems":20}},"additionalProperties":false},"minItems":1,"maxItems":500},"connection":{"type":"string","description":"connection_name of the database connection the password hashes go to. Defaults to the tenant database connection."},"update_existing":{"type":"boolean","description":"Set the password hash and identities of users that already exist (matched by email) instead of skipping them. Their profile is never overwritten.","default":false},"dry_run":{"type":"boolean","description":"Validate every row and resolve every match without writing anything.","default":false}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["summary","results"],"properties":{"summary":{"type":"object","required":["total","created","updated","skipped","error"],"properties":{"total":{"type":"number"},"created":{"type":"number"},"updated":{"type":"number"},"skipped":{"type":"number"},"error":{"type":"number"}}},"results":{"type":"array","items":{"type":"object","required":["index","email","status"],"properties":{"index":{"type":"number","description":"Position of the row in `users`."},"email":{"type":"string"},"status":{"anyOf":[{"type":"string","enum":["created"]},{"type":"string","enum":["updated"]},{"type":"string","enum":["skipped"]},{"type":"string","enum":["error"]}]},"user_id":{"type":"string"},"skipped_identities":{"type":"array","items":{"type":"string"},"description":"Connections named in `identities` that this tenant does not have (e.g. Apple from a Firebase export). The user was imported without them."},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/user/export":{"get":{"operationId":"user/export","summary":"Export users, optionally with their password hashes","tags":["user"],"description":"Pages through every user of the tenant, 500 at a time, in the row shape `POST /user/import` takes. With `include_hashes` each row carries its `password_hash` (needs the `read:password_hashes` scope, is audited as `credentials.exported` and notifies its subscribers). Provider access and refresh tokens of linked identities are never exported. Available on every plan.","parameters":[{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"`next_cursor` of the previous page."},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":500},"in":"query","name":"limit","required":false},{"schema":{"type":"string"},"in":"query","name":"connection","required":false,"description":"connection_name of the database connection whose password hashes are exported. Defaults to the tenant database connection."},{"schema":{"type":"boolean","default":false},"in":"query","name":"include_hashes","required":false,"description":"Include `password_hash`. Needs the `read:password_hashes` scope, is written to the audit log and notifies the `credentials.exported` subscribers."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["users","next_cursor","connection"],"properties":{"users":{"type":"array","items":{"type":"object","required":["user_id","email","email_verified","password_hash_status"],"properties":{"user_id":{"type":"string"},"email":{"anyOf":[{"type":"string"},{"type":"null"}]},"email_verified":{"type":"boolean"},"name":{"type":"string"},"given_name":{"type":"string"},"family_name":{"type":"string"},"phone":{"type":"string"},"birth_date":{"type":"string"},"locale":{"type":"string"},"picture":{"type":"string"},"user_metadata":{"type":"object","additionalProperties":{}},"app_metadata":{"type":"object","additionalProperties":{}},"password_hash_status":{"anyOf":[{"type":"string","enum":["set"]},{"type":"string","enum":["not_set"]},{"type":"string","enum":["no_credential"]},{"type":"string","enum":["legacy_unexportable"]}],"description":"`legacy_unexportable`: the password is still stored in a pre-2026 format that is not exported; it upgrades on the next login."},"password_hash":{"type":"string","description":"Self-describing hash (argon2id PHC for passwords set on Faable; the original format for imported ones not yet upgraded). Only with `include_hashes`."},"identities":{"type":"array","items":{"type":"object","required":["connection","identity_id"],"properties":{"connection":{"type":"string"},"identity_id":{"type":"string"},"profile_data":{"type":"object","additionalProperties":{}}}}}}}},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}]},"connection":{"anyOf":[{"type":"string"},{"type":"null"}]}}}}}},"400":{"description":"`invalid_connection` — The connection does not exist, is disabled, or is not of the type this flow needs.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_connection","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/identity":{"get":{"operationId":"identity/list","summary":"List Identitys","tags":["identity"],"description":"List Identitys","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"string"},"in":"query","name":"user","required":false,"description":"Exact match on `user`. Equivalent to `?query=user:<value>`."},{"schema":{"type":"string"},"in":"query","name":"connection","required":false,"description":"Exact match on `connection`. Equivalent to `?query=connection:<value>`."},{"schema":{"type":"string"},"in":"query","name":"identity_id","required":false,"description":"Exact match on `identity_id`. Equivalent to `?query=identity_id:<value>`."},{"schema":{"type":"string"},"in":"query","name":"provider","required":false,"description":"Exact match on `provider`. Equivalent to `?query=provider:<value>`."},{"schema":{"type":"string"},"in":"query","name":"created_at","required":false,"description":"Exact match on `created_at`. Equivalent to `?query=created_at:<value>`."},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"},{"schema":{"type":"array","items":{"type":"string","enum":["user","connection"],"description":"Allowed expand paths on `identity`: `user`, `connection`."},"maxItems":2},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `?expand=user`)."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/Identity"}}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"identity/create","summary":"Create Identity","tags":["identity"],"description":"Create Identity","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["user","identity_id","connection","profile_data"],"properties":{"user":{"type":"string"},"identity_id":{"type":"string"},"connection":{"type":"string"},"access_token":{"type":"string"},"access_token_secret":{"type":"string"},"refresh_token":{"type":"string"},"profile_data":{}}}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Identity","content":{"application/json":{"schema":{"type":"object","required":["id","connection","user","identity_id","profile_data","account","createdAt"],"properties":{"id":{"type":"string","description":"Identity ID"},"connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}]},"identity_id":{"type":"string"},"access_token":{"type":"string"},"access_token_secret":{"type":"string"},"refresh_token":{"type":"string"},"access_token_expires_at":{"type":"string","description":"ISO 8601 timestamp when the stored provider `access_token` expires. Stamped from the provider `expires_in` on (re-)authorization and refresh. Absent when the provider issues non-expiring tokens."},"profile_data":{},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"IdentityMetadata","default":{}},"createdAt":{"type":"string","description":"Identity creation date"},"updatedAt":{"type":"string","description":"Identity updated date"}},"description":"Identity"}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/identity/{identity_id}":{"get":{"operationId":"identity/get","summary":"Get Identity","tags":["identity"],"description":"Get Identity","parameters":[{"schema":{"type":"array","items":{"type":"string","enum":["user","connection"],"description":"Allowed expand paths on `identity`: `user`, `connection`."},"maxItems":2},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `?expand=user`)."},{"schema":{"type":"string"},"in":"path","name":"identity_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Identity","content":{"application/json":{"schema":{"type":"object","required":["id","connection","user","identity_id","profile_data","account","createdAt"],"properties":{"id":{"type":"string","description":"Identity ID"},"connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}]},"identity_id":{"type":"string"},"access_token":{"type":"string"},"access_token_secret":{"type":"string"},"refresh_token":{"type":"string"},"access_token_expires_at":{"type":"string","description":"ISO 8601 timestamp when the stored provider `access_token` expires. Stamped from the provider `expires_in` on (re-)authorization and refresh. Absent when the provider issues non-expiring tokens."},"profile_data":{},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"IdentityMetadata","default":{}},"createdAt":{"type":"string","description":"Identity creation date"},"updatedAt":{"type":"string","description":"Identity updated date"}},"description":"Identity"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"delete":{"operationId":"identity/delete","summary":"Delete Identity","tags":["identity"],"description":"Delete Identity","parameters":[{"schema":{"type":"string"},"in":"path","name":"identity_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Identity","content":{"application/json":{"schema":{"type":"object","required":["id","connection","user","identity_id","profile_data","account","createdAt"],"properties":{"id":{"type":"string","description":"Identity ID"},"connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}]},"identity_id":{"type":"string"},"access_token":{"type":"string"},"access_token_secret":{"type":"string"},"refresh_token":{"type":"string"},"access_token_expires_at":{"type":"string","description":"ISO 8601 timestamp when the stored provider `access_token` expires. Stamped from the provider `expires_in` on (re-)authorization and refresh. Absent when the provider issues non-expiring tokens."},"profile_data":{},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"IdentityMetadata","default":{}},"createdAt":{"type":"string","description":"Identity creation date"},"updatedAt":{"type":"string","description":"Identity updated date"}},"description":"Identity"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/identity/{identity_id}/provider-token":{"get":{"operationId":"identity/getProviderToken","summary":"Get a guaranteed-valid provider access_token","tags":["identity"],"description":"Returns the identity's upstream provider `access_token`, refreshing it transparently via the stored `refresh_token` when it is (about to be) expired. Pass `?fresh=true` to force a refresh. Fails with 400 when the token is expired and no `refresh_token` is available (the user must re-authorize).","parameters":[{"schema":{"type":"boolean"},"in":"query","name":"fresh","required":false,"description":"Force a refresh against the provider even when the stored token still looks valid."},{"schema":{"type":"string"},"in":"path","name":"identity_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["access_token","refreshed"],"properties":{"access_token":{"type":"string"},"access_token_expires_at":{"type":"string"},"refreshed":{"type":"boolean"}}}}}},"400":{"description":"`connection_misconfigured` — The connection is missing settings needed to talk to its provider.\n\n`no_provider_token` — The identity holds no provider access token.\n\n`no_refresh_token` — The identity holds no provider refresh token, so it cannot be refreshed.\n\n`provider_error` — The upstream identity provider returned an error. The suffix is the provider’s own code, when it sent one: `provider_error:bad_refresh_token` (the user must re-authorize), `provider_error:incorrect_client_credentials` (our configuration, the user can do nothing). `details.provider_error_description` carries the provider’s sentence.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["connection_misconfigured","no_provider_token","no_refresh_token","provider_error","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/credentials":{"get":{"operationId":"credentials/list","summary":"List Credentialss","tags":["credentials"],"description":"List Credentialss","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"string"},"in":"query","name":"user","required":false,"description":"Exact match on `user`. Equivalent to `?query=user:<value>`."},{"schema":{"type":"string"},"in":"query","name":"connection","required":false,"description":"Exact match on `connection`. Equivalent to `?query=connection:<value>`."},{"schema":{"type":"string"},"in":"query","name":"email","required":false,"description":"Exact match on `email`. Equivalent to `?query=email:<value>`."},{"schema":{"type":"string"},"in":"query","name":"username","required":false,"description":"Exact match on `username`. Equivalent to `?query=username:<value>`."},{"schema":{"type":"string"},"in":"query","name":"created_at","required":false,"description":"Exact match on `created_at`. Equivalent to `?query=created_at:<value>`."},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"},{"schema":{"type":"array","items":{"type":"string","enum":["user","connection"],"description":"Allowed expand paths on `credentials`: `user`, `connection`."},"maxItems":2},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `?expand=user`)."},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false,"description":"Full-text search across: `email`, `username`."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/Credential"}}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"credentials/create","summary":"Create Credentials","tags":["credentials"],"description":"Create Credentials","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["user","connection"],"properties":{"user":{"type":"string"},"connection":{"type":"string"},"email":{"type":"string"},"username":{"type":"string"}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Credential","content":{"application/json":{"schema":{"type":"object","required":["id","connection","user","account","createdAt"],"properties":{"id":{"type":"string","description":"Credential ID"},"connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}]},"email":{"type":"string","description":"Email login identifier (unique within the connection)."},"username":{"type":"string","description":"Username login identifier (unique within the connection)."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"CredentialMetadata","default":{}},"createdAt":{"type":"string","description":"Credential creation date"},"updatedAt":{"type":"string","description":"Credential updated date"}},"description":"Credential"}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/credentials/{credentials_id}":{"get":{"operationId":"credentials/get","summary":"Get Credentials","tags":["credentials"],"description":"Get Credentials","parameters":[{"schema":{"type":"array","items":{"type":"string","enum":["user","connection"],"description":"Allowed expand paths on `credentials`: `user`, `connection`."},"maxItems":2},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `?expand=user`)."},{"schema":{"type":"string"},"in":"path","name":"credentials_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Credential","content":{"application/json":{"schema":{"type":"object","required":["id","connection","user","account","createdAt"],"properties":{"id":{"type":"string","description":"Credential ID"},"connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}]},"email":{"type":"string","description":"Email login identifier (unique within the connection)."},"username":{"type":"string","description":"Username login identifier (unique within the connection)."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"CredentialMetadata","default":{}},"createdAt":{"type":"string","description":"Credential creation date"},"updatedAt":{"type":"string","description":"Credential updated date"}},"description":"Credential"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"credentials/update","summary":"Update Credentials","tags":["credentials"],"description":"Update Credentials","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"username":{"type":"string"}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"credentials_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Credential","content":{"application/json":{"schema":{"type":"object","required":["id","connection","user","account","createdAt"],"properties":{"id":{"type":"string","description":"Credential ID"},"connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}]},"email":{"type":"string","description":"Email login identifier (unique within the connection)."},"username":{"type":"string","description":"Username login identifier (unique within the connection)."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"CredentialMetadata","default":{}},"createdAt":{"type":"string","description":"Credential creation date"},"updatedAt":{"type":"string","description":"Credential updated date"}},"description":"Credential"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"delete":{"operationId":"credentials/delete","summary":"Delete Credentials","tags":["credentials"],"description":"Delete Credentials","parameters":[{"schema":{"type":"string"},"in":"path","name":"credentials_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Credential","content":{"application/json":{"schema":{"type":"object","required":["id","connection","user","account","createdAt"],"properties":{"id":{"type":"string","description":"Credential ID"},"connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}]},"email":{"type":"string","description":"Email login identifier (unique within the connection)."},"username":{"type":"string","description":"Username login identifier (unique within the connection)."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"CredentialMetadata","default":{}},"createdAt":{"type":"string","description":"Credential creation date"},"updatedAt":{"type":"string","description":"Credential updated date"}},"description":"Credential"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/credentials/{credentials_id}/set-password":{"post":{"operationId":"credentials/setPassword","summary":"Set a credential's password","tags":["credentials"],"description":"Sets or rotates the password for a database credential. Send exactly one of `password` (plaintext, hashed with argon2id) or `password_hash` (a hash exported from another provider: bcrypt, scrypt, Firebase scrypt, PBKDF2 or argon2 — verified on the first login and upgraded to argon2id). The previous password is invalidated immediately. A plaintext password records a `credential.password_changed` audit entry (initiated_by=admin); the confirmation email is only sent when an EXISTING password is rotated. An imported hash records `credential.password_imported` with its algorithm and sends no email. Neither the password nor the hash is ever returned or logged.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"password":{"type":"string","description":"New plaintext password (hashed on write)."},"password_hash":{"anyOf":[{"type":"string","minLength":1,"maxLength":1024,"description":"A self-describing hash: bcrypt (`$2a$`/`$2b$`/`$2y$`), argon2 PHC (`$argon2id$…`), scrypt PHC (`$scrypt$ln=…`), PBKDF2 (`$pbkdf2-sha256$i=…` or Django `pbkdf2_sha256$…`)."},{"type":"object","required":["algorithm","hash"],"properties":{"algorithm":{"type":"string","description":"`bcrypt`, `argon2id`, `argon2i`, `argon2d`, `scrypt`, `pbkdf2-sha256`, `pbkdf2-sha512` or `firebase-scrypt`."},"hash":{"type":"string","minLength":1,"maxLength":1024,"description":"The hash, or its base64 digest when `salt` is given."},"salt":{"type":"string","maxLength":512,"description":"Base64 salt."},"params":{"type":"object","description":"Algorithm parameters: `iterations` (pbkdf2); `N`, `r`, `p` (scrypt); `signer_key`, `salt_separator`, `rounds`, `mem_cost` (firebase-scrypt).","additionalProperties":{"anyOf":[{"type":"number"},{"type":"string","maxLength":512}]}}},"additionalProperties":false}],"description":"A password hash exported from another provider. Verified on the first login and upgraded to argon2id. Fast digests (MD5, SHA-1, SHA-2) and low costs are refused with `weak_password_hash`."}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"credentials_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Credential","content":{"application/json":{"schema":{"type":"object","required":["id","connection","user","account","createdAt"],"properties":{"id":{"type":"string","description":"Credential ID"},"connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}]},"email":{"type":"string","description":"Email login identifier (unique within the connection)."},"username":{"type":"string","description":"Username login identifier (unique within the connection)."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"CredentialMetadata","default":{}},"createdAt":{"type":"string","description":"Credential creation date"},"updatedAt":{"type":"string","description":"Credential updated date"}},"description":"Credential"}}}},"400":{"description":"`bad_request` — The request is malformed. `message` says what is wrong.\n\n`invalid_password_hash` — The imported password hash cannot be read, or its cost is above what a login can afford. `message` names the field.\n\n`weak_password_hash` — The imported password hash uses a fast digest (MD5, SHA-1, SHA-2) or a cost below the minimum. `message` says which.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["bad_request","invalid_password_hash","weak_password_hash","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/role":{"get":{"operationId":"role/list","summary":"List Roles","tags":["role"],"description":"List Roles","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false,"description":"Full-text search across: `name`, `description`."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/Role"}}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"role/create","summary":"Create Role","tags":["role"],"description":"Create Role","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"Short, human-readable identifier for the role (e.g. `admin`, `editor`).","example":"admin"},"description":{"type":"string","description":"Free-form explanation of what users in this role are expected to do or have access to."},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"RoleCreate","additionalProperties":false}}},"description":"RoleCreate"},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Role","content":{"application/json":{"schema":{"type":"object","required":["id","name","description","account","createdAt"],"properties":{"id":{"type":"string","description":"Role ID"},"name":{"type":"string","description":"Short, human-readable identifier for the role (e.g. `admin`, `editor`, `billing-manager`). Surfaced in the dashboard role pickers.","example":"admin"},"description":{"type":"string","description":"Free-form explanation of what users in this role are expected to do or have access to. Shown alongside the role in the dashboard."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"RoleMetadata","default":{}},"createdAt":{"type":"string","description":"Role creation date"},"updatedAt":{"type":"string","description":"Role updated date"}},"description":"Role"}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/role/{role_id}":{"get":{"operationId":"role/get","summary":"Get Role","tags":["role"],"description":"Get Role","parameters":[{"schema":{"type":"string"},"in":"path","name":"role_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Role","content":{"application/json":{"schema":{"type":"object","required":["id","name","description","account","createdAt"],"properties":{"id":{"type":"string","description":"Role ID"},"name":{"type":"string","description":"Short, human-readable identifier for the role (e.g. `admin`, `editor`, `billing-manager`). Surfaced in the dashboard role pickers.","example":"admin"},"description":{"type":"string","description":"Free-form explanation of what users in this role are expected to do or have access to. Shown alongside the role in the dashboard."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"RoleMetadata","default":{}},"createdAt":{"type":"string","description":"Role creation date"},"updatedAt":{"type":"string","description":"Role updated date"}},"description":"Role"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"role/update","summary":"Update Role","tags":["role"],"description":"Update Role","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"New human-readable identifier for the role."},"description":{"type":"string","description":"New free-form description of what users in this role do."}},"description":"Partial update for a Role. Only the supplied fields are modified.","additionalProperties":false}}},"description":"Partial update for a Role. Only the supplied fields are modified."},"parameters":[{"schema":{"type":"string"},"in":"path","name":"role_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Role","content":{"application/json":{"schema":{"type":"object","required":["id","name","description","account","createdAt"],"properties":{"id":{"type":"string","description":"Role ID"},"name":{"type":"string","description":"Short, human-readable identifier for the role (e.g. `admin`, `editor`, `billing-manager`). Surfaced in the dashboard role pickers.","example":"admin"},"description":{"type":"string","description":"Free-form explanation of what users in this role are expected to do or have access to. Shown alongside the role in the dashboard."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"RoleMetadata","default":{}},"createdAt":{"type":"string","description":"Role creation date"},"updatedAt":{"type":"string","description":"Role updated date"}},"description":"Role"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"delete":{"operationId":"role/delete","summary":"Delete Role","tags":["role"],"description":"Delete Role","parameters":[{"schema":{"type":"string"},"in":"path","name":"role_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Role","content":{"application/json":{"schema":{"type":"object","required":["id","name","description","account","createdAt"],"properties":{"id":{"type":"string","description":"Role ID"},"name":{"type":"string","description":"Short, human-readable identifier for the role (e.g. `admin`, `editor`, `billing-manager`). Surfaced in the dashboard role pickers.","example":"admin"},"description":{"type":"string","description":"Free-form explanation of what users in this role are expected to do or have access to. Shown alongside the role in the dashboard."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"RoleMetadata","default":{}},"createdAt":{"type":"string","description":"Role creation date"},"updatedAt":{"type":"string","description":"Role updated date"}},"description":"Role"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/role/{role_id}/users":{"get":{"operationId":"role/users","summary":"List Users with Role","tags":["role"],"description":"List Users with Role","parameters":[{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Start from this cursor"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Start from this cursor"},{"schema":{"type":"number"},"in":"query","name":"pageSize","required":false,"description":"Size of the results array"},{"schema":{"type":"string"},"in":"path","name":"role_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"PaginatedResponse","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"type":"string","description":"next cursor","nullable":true},"results":{"type":"array","items":{"$ref":"#/components/schemas/RoleMember"},"description":"List of results"}},"description":"PaginatedResponse","additionalProperties":false}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`role_not_found` — No role with that id in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["role_not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"role/assign_users","summary":"Assign Users to Role","tags":["role"],"description":"Assign Users to Role","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["users"],"properties":{"users":{"type":"array","items":{"type":"string"},"description":"List of User ids to grant the role to. Existing role memberships are preserved; passing a user that already holds the role is a no-op.","example":["user_6555fd293acc2f0fac0e3452"]}},"additionalProperties":false,"description":"Payload for granting a role to one or more users in a single call. Posted to `POST /role/:role_id/users`."}}},"description":"Payload for granting a role to one or more users in a single call. Posted to `POST /role/:role_id/users`."},"parameters":[{"schema":{"type":"string"},"in":"path","name":"role_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_role` — The role belongs to another account.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_role","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`role_not_found` — No role with that id in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["role_not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/rolemember":{"get":{"operationId":"rolemember/list","summary":"List Rolemembers","tags":["rolemember"],"description":"List Rolemembers","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"},{"schema":{"type":"array","items":{"type":"string","enum":["user","role"],"description":"Allowed expand paths on `rolemember`: `user`, `role`."},"maxItems":2},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `?expand=user`)."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/RoleMember"}}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"rolemember/create","summary":"Create Rolemember","tags":["rolemember"],"description":"Create Rolemember","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["users"],"properties":{"users":{"type":"array","items":{"type":"string"},"description":"List of User ids to grant the role to. Existing role memberships are preserved; passing a user that already holds the role is a no-op.","example":["user_6555fd293acc2f0fac0e3452"]}},"additionalProperties":false,"description":"Payload for granting a role to one or more users in a single call. Posted to `POST /role/:role_id/users`."}}},"description":"Payload for granting a role to one or more users in a single call. Posted to `POST /role/:role_id/users`."},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"RoleMember","content":{"application/json":{"schema":{"type":"object","required":["id","role","user","account","createdAt"],"properties":{"id":{"type":"string","description":"RoleMember ID"},"role":{"anyOf":[{"$ref":"#/components/schemas/Role"},{"type":"string"},{}],"description":"Role granted to the user. Returned as an id by default; pass `?expand=role` to inline the full Role object."},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}],"description":"User the role is granted to. Returned as an id by default; pass `?expand=user` to inline the full User object."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"RoleMemberMetadata","default":{}},"createdAt":{"type":"string","description":"RoleMember creation date"},"updatedAt":{"type":"string","description":"RoleMember updated date"}},"description":"RoleMember"}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/rolemember/{rolemember_id}":{"get":{"operationId":"rolemember/get","summary":"Get Rolemember","tags":["rolemember"],"description":"Get Rolemember","parameters":[{"schema":{"type":"array","items":{"type":"string","enum":["user","role"],"description":"Allowed expand paths on `rolemember`: `user`, `role`."},"maxItems":2},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `?expand=user`)."},{"schema":{"type":"string"},"in":"path","name":"rolemember_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"RoleMember","content":{"application/json":{"schema":{"type":"object","required":["id","role","user","account","createdAt"],"properties":{"id":{"type":"string","description":"RoleMember ID"},"role":{"anyOf":[{"$ref":"#/components/schemas/Role"},{"type":"string"},{}],"description":"Role granted to the user. Returned as an id by default; pass `?expand=role` to inline the full Role object."},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}],"description":"User the role is granted to. Returned as an id by default; pass `?expand=user` to inline the full User object."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"RoleMemberMetadata","default":{}},"createdAt":{"type":"string","description":"RoleMember creation date"},"updatedAt":{"type":"string","description":"RoleMember updated date"}},"description":"RoleMember"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"delete":{"operationId":"rolemember/delete","summary":"Delete Rolemember","tags":["rolemember"],"description":"Delete Rolemember","parameters":[{"schema":{"type":"string"},"in":"path","name":"rolemember_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"RoleMember","content":{"application/json":{"schema":{"type":"object","required":["id","role","user","account","createdAt"],"properties":{"id":{"type":"string","description":"RoleMember ID"},"role":{"anyOf":[{"$ref":"#/components/schemas/Role"},{"type":"string"},{}],"description":"Role granted to the user. Returned as an id by default; pass `?expand=role` to inline the full Role object."},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}],"description":"User the role is granted to. Returned as an id by default; pass `?expand=user` to inline the full User object."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"RoleMemberMetadata","default":{}},"createdAt":{"type":"string","description":"RoleMember creation date"},"updatedAt":{"type":"string","description":"RoleMember updated date"}},"description":"RoleMember"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/team":{"get":{"operationId":"team/list","summary":"List Teams","tags":["team"],"description":"List Teams","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false,"description":"Full-text search across: `name`, `slug`."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/Team"}}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"team/create","summary":"Create Team","tags":["team"],"description":"Create Team","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","description":"Human-readable name of the team.","example":"Acme Engineering"},"description":{"type":"string","description":"Optional free-form description of the team."},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"TeamCreate","additionalProperties":false}}},"description":"TeamCreate"},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Team","content":{"application/json":{"schema":{"type":"object","required":["id","name","slug","description","logo_url","account","createdAt"],"properties":{"id":{"type":"string","description":"Team ID"},"name":{"type":"string","description":"Human-readable name of the team. Shown in the dashboard and in team-picker UI.","example":"Acme Engineering"},"slug":{"type":"string","description":"URL-safe identifier auto-derived from `name`. Unique within the tenant; used in routes that address a team by slug.","example":"acme-engineering"},"description":{"type":"string","description":"Optional free-form description of the team.","nullable":true},"logo_url":{"type":"string","description":"Optional URL of an image used as the team avatar/logo.","nullable":true},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"TeamMetadata","default":{}},"createdAt":{"type":"string","description":"Team creation date"},"updatedAt":{"type":"string","description":"Team updated date"}},"description":"Team"}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/team/{team_id}":{"get":{"operationId":"team/get","summary":"Get Team","tags":["team"],"description":"Get Team","parameters":[{"schema":{"type":"string"},"in":"path","name":"team_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Team","content":{"application/json":{"schema":{"type":"object","required":["id","name","slug","description","logo_url","account","createdAt"],"properties":{"id":{"type":"string","description":"Team ID"},"name":{"type":"string","description":"Human-readable name of the team. Shown in the dashboard and in team-picker UI.","example":"Acme Engineering"},"slug":{"type":"string","description":"URL-safe identifier auto-derived from `name`. Unique within the tenant; used in routes that address a team by slug.","example":"acme-engineering"},"description":{"type":"string","description":"Optional free-form description of the team.","nullable":true},"logo_url":{"type":"string","description":"Optional URL of an image used as the team avatar/logo.","nullable":true},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"TeamMetadata","default":{}},"createdAt":{"type":"string","description":"Team creation date"},"updatedAt":{"type":"string","description":"Team updated date"}},"description":"Team"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"team/update","summary":"Update Team","tags":["team"],"description":"Update Team","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"New human-readable name for the team."},"description":{"type":"string","description":"New free-form description for the team."},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Arbitrary key/value pairs attached to the team. Replaces the previous metadata entirely."}},"description":"Partial update for a Team. Only the supplied fields are modified. `slug` is auto-derived from `name` and is not user-editable.","additionalProperties":false}}},"description":"Partial update for a Team. Only the supplied fields are modified. `slug` is auto-derived from `name` and is not user-editable."},"parameters":[{"schema":{"type":"string"},"in":"path","name":"team_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Team","content":{"application/json":{"schema":{"type":"object","required":["id","name","slug","description","logo_url","account","createdAt"],"properties":{"id":{"type":"string","description":"Team ID"},"name":{"type":"string","description":"Human-readable name of the team. Shown in the dashboard and in team-picker UI.","example":"Acme Engineering"},"slug":{"type":"string","description":"URL-safe identifier auto-derived from `name`. Unique within the tenant; used in routes that address a team by slug.","example":"acme-engineering"},"description":{"type":"string","description":"Optional free-form description of the team.","nullable":true},"logo_url":{"type":"string","description":"Optional URL of an image used as the team avatar/logo.","nullable":true},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"TeamMetadata","default":{}},"createdAt":{"type":"string","description":"Team creation date"},"updatedAt":{"type":"string","description":"Team updated date"}},"description":"Team"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"delete":{"operationId":"team/delete","summary":"Delete Team","tags":["team"],"description":"Delete Team","parameters":[{"schema":{"type":"string"},"in":"path","name":"team_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Team","content":{"application/json":{"schema":{"type":"object","required":["id","name","slug","description","logo_url","account","createdAt"],"properties":{"id":{"type":"string","description":"Team ID"},"name":{"type":"string","description":"Human-readable name of the team. Shown in the dashboard and in team-picker UI.","example":"Acme Engineering"},"slug":{"type":"string","description":"URL-safe identifier auto-derived from `name`. Unique within the tenant; used in routes that address a team by slug.","example":"acme-engineering"},"description":{"type":"string","description":"Optional free-form description of the team.","nullable":true},"logo_url":{"type":"string","description":"Optional URL of an image used as the team avatar/logo.","nullable":true},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"TeamMetadata","default":{}},"createdAt":{"type":"string","description":"Team creation date"},"updatedAt":{"type":"string","description":"Team updated date"}},"description":"Team"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/teammember":{"get":{"operationId":"teammember/list","summary":"List Teammembers","tags":["teammember"],"description":"List Teammembers","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"string"},"in":"query","name":"user","required":false,"description":"Exact match on `user`. Equivalent to `?query=user:<value>`."},{"schema":{"type":"string"},"in":"query","name":"team","required":false,"description":"Exact match on `team`. Equivalent to `?query=team:<value>`."},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"},{"schema":{"type":"array","items":{"type":"string","enum":["user","team","roles"],"description":"Allowed expand paths on `teammember`: `user`, `team`, `roles`."},"maxItems":3},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `?expand=user`)."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/TeamMember"}}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"teammember/create","summary":"Create Teammember","tags":["teammember"],"description":"Create Teammember","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["users"],"properties":{"users":{"type":"array","items":{"type":"string"},"description":"List of User ids to add as members of the team. Users already in the team are skipped.","example":["user_6555fd293acc2f0fac0e3452"]},"roles":{"type":"array","items":{"type":"string"},"description":"Role ids to grant to the added members within the scope of this team.","default":[],"example":["role_6555fd293acc2f0fac0e3452"]}},"additionalProperties":false,"description":"Payload for adding one or more users to a team in a single call. Posted to `POST /team/:team_id/member`."}}},"description":"Payload for adding one or more users to a team in a single call. Posted to `POST /team/:team_id/member`."},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"TeamMember","content":{"application/json":{"schema":{"type":"object","required":["id","user","team","roles","account","createdAt"],"properties":{"id":{"type":"string","description":"TeamMember ID"},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}],"description":"User that belongs to the team. Returned as an id by default; pass `?expand=user` to inline the full User object."},"team":{"anyOf":[{"$ref":"#/components/schemas/Team"},{"type":"string"},{}],"description":"Team the user belongs to. Returned as an id by default; pass `?expand=team` to inline the full Team object."},"roles":{"type":"array","items":{"anyOf":[{"$ref":"#/components/schemas/Role"},{"type":"string"},{}]},"description":"Roles granted to the user *within this team*. Distinct from account-wide RoleMember grants — these only apply in the scope of the team. Each entry is an id by default; pass `?expand=roles` to inline."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"TeamMemberMetadata","default":{}},"createdAt":{"type":"string","description":"TeamMember creation date"},"updatedAt":{"type":"string","description":"TeamMember updated date"}},"description":"TeamMember"}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/teammember/{teammember_id}":{"get":{"operationId":"teammember/get","summary":"Get Teammember","tags":["teammember"],"description":"Get Teammember","parameters":[{"schema":{"type":"array","items":{"type":"string","enum":["user","team","roles"],"description":"Allowed expand paths on `teammember`: `user`, `team`, `roles`."},"maxItems":3},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `?expand=user`)."},{"schema":{"type":"string"},"in":"path","name":"teammember_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"TeamMember","content":{"application/json":{"schema":{"type":"object","required":["id","user","team","roles","account","createdAt"],"properties":{"id":{"type":"string","description":"TeamMember ID"},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}],"description":"User that belongs to the team. Returned as an id by default; pass `?expand=user` to inline the full User object."},"team":{"anyOf":[{"$ref":"#/components/schemas/Team"},{"type":"string"},{}],"description":"Team the user belongs to. Returned as an id by default; pass `?expand=team` to inline the full Team object."},"roles":{"type":"array","items":{"anyOf":[{"$ref":"#/components/schemas/Role"},{"type":"string"},{}]},"description":"Roles granted to the user *within this team*. Distinct from account-wide RoleMember grants — these only apply in the scope of the team. Each entry is an id by default; pass `?expand=roles` to inline."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"TeamMemberMetadata","default":{}},"createdAt":{"type":"string","description":"TeamMember creation date"},"updatedAt":{"type":"string","description":"TeamMember updated date"}},"description":"TeamMember"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"delete":{"operationId":"teammember/delete","summary":"Delete Teammember","tags":["teammember"],"description":"Delete Teammember","parameters":[{"schema":{"type":"string"},"in":"path","name":"teammember_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"TeamMember","content":{"application/json":{"schema":{"type":"object","required":["id","user","team","roles","account","createdAt"],"properties":{"id":{"type":"string","description":"TeamMember ID"},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}],"description":"User that belongs to the team. Returned as an id by default; pass `?expand=user` to inline the full User object."},"team":{"anyOf":[{"$ref":"#/components/schemas/Team"},{"type":"string"},{}],"description":"Team the user belongs to. Returned as an id by default; pass `?expand=team` to inline the full Team object."},"roles":{"type":"array","items":{"anyOf":[{"$ref":"#/components/schemas/Role"},{"type":"string"},{}]},"description":"Roles granted to the user *within this team*. Distinct from account-wide RoleMember grants — these only apply in the scope of the team. Each entry is an id by default; pass `?expand=roles` to inline."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"TeamMemberMetadata","default":{}},"createdAt":{"type":"string","description":"TeamMember creation date"},"updatedAt":{"type":"string","description":"TeamMember updated date"}},"description":"TeamMember"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/team/{team_id}/member":{"get":{"operationId":"team/listMembers","summary":"List Team Members","tags":["team"],"description":"List all Users that belong to the given Team in the current Account. Supports `?expand=user,team,roles` to inline referenced rows.","parameters":[{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Start from this cursor"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Start from this cursor"},{"schema":{"type":"number"},"in":"query","name":"pageSize","required":false,"description":"Size of the results array"},{"schema":{"type":"array","items":{"type":"string","enum":["user","team","roles"],"description":"Allowed expand paths: `user`, `team`, `roles`."}},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Accepts repeated params (`expand=user&expand=roles`) or comma-separated (`expand=user,roles`)."},{"schema":{"type":"string"},"in":"path","name":"team_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"PaginatedResponse","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"type":"string","description":"next cursor","nullable":true},"results":{"type":"array","items":{"$ref":"#/components/schemas/TeamMember"},"description":"List of results"}},"description":"PaginatedResponse","additionalProperties":false}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"team/addMembers","summary":"Add Users to a Team","tags":["team"],"description":"Adds one or more Users of this tenant as members of the given Team, with the same roles. Users already in the team are skipped; `already_member` only when all of them are. A user or team that is not in this tenant is a 404. Returns the TeamMember record of the first user in `users`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["users"],"properties":{"users":{"type":"array","items":{"type":"string"},"description":"List of User ids to add as members of the team. Users already in the team are skipped.","example":["user_6555fd293acc2f0fac0e3452"]},"roles":{"type":"array","items":{"type":"string"},"description":"Role ids to grant to the added members within the scope of this team.","default":[],"example":["role_6555fd293acc2f0fac0e3452"]}},"additionalProperties":false,"description":"Payload for adding one or more users to a team in a single call. Posted to `POST /team/:team_id/member`."}}},"description":"Payload for adding one or more users to a team in a single call. Posted to `POST /team/:team_id/member`."},"parameters":[{"schema":{"type":"string"},"in":"path","name":"team_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"TeamMember","content":{"application/json":{"schema":{"type":"object","required":["id","user","team","roles","account","createdAt"],"properties":{"id":{"type":"string","description":"TeamMember ID"},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}],"description":"User that belongs to the team. Returned as an id by default; pass `?expand=user` to inline the full User object."},"team":{"anyOf":[{"$ref":"#/components/schemas/Team"},{"type":"string"},{}],"description":"Team the user belongs to. Returned as an id by default; pass `?expand=team` to inline the full Team object."},"roles":{"type":"array","items":{"anyOf":[{"$ref":"#/components/schemas/Role"},{"type":"string"},{}]},"description":"Roles granted to the user *within this team*. Distinct from account-wide RoleMember grants — these only apply in the scope of the team. Each entry is an id by default; pass `?expand=roles` to inline."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"TeamMemberMetadata","default":{}},"createdAt":{"type":"string","description":"TeamMember creation date"},"updatedAt":{"type":"string","description":"TeamMember updated date"}},"description":"TeamMember"}}}},"400":{"description":"`already_member` — The user is already a member of the team.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_member","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/team/{team_id}/member/check/{user_id}":{"get":{"operationId":"team/checkMember","summary":"Check User is member of a Team","tags":["team"],"description":"Returns the TeamMember record if the User belongs to the given Team, or 404 otherwise.","parameters":[{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Start from this cursor"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Start from this cursor"},{"schema":{"type":"number"},"in":"query","name":"pageSize","required":false,"description":"Size of the results array"},{"schema":{"type":"string"},"in":"path","name":"team_id","required":true},{"schema":{"type":"string"},"in":"path","name":"user_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"TeamMember","content":{"application/json":{"schema":{"type":"object","required":["id","user","team","roles","account","createdAt"],"properties":{"id":{"type":"string","description":"TeamMember ID"},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}],"description":"User that belongs to the team. Returned as an id by default; pass `?expand=user` to inline the full User object."},"team":{"anyOf":[{"$ref":"#/components/schemas/Team"},{"type":"string"},{}],"description":"Team the user belongs to. Returned as an id by default; pass `?expand=team` to inline the full Team object."},"roles":{"type":"array","items":{"anyOf":[{"$ref":"#/components/schemas/Role"},{"type":"string"},{}]},"description":"Roles granted to the user *within this team*. Distinct from account-wide RoleMember grants — these only apply in the scope of the team. Each entry is an id by default; pass `?expand=roles` to inline."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"TeamMemberMetadata","default":{}},"createdAt":{"type":"string","description":"TeamMember creation date"},"updatedAt":{"type":"string","description":"TeamMember updated date"}},"description":"TeamMember"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_a_member` — The user is not a member of the team.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_a_member","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/team/{team_id}/invite":{"post":{"operationId":"team/invite","summary":"Invite a user to a Team by email","tags":["team"],"description":"Invites a user by email. `mode='auto'` (default) adds the user directly when the email already corresponds to a user in this tenant and falls back to a ticketed invite + email otherwise. `mode='invite'` always issues a ticket + email even when the user exists. Re-inviting the same email to the same team invalidates the previous pending invite.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","description":"Email of the user to invite to the team.","example":"someone@example.com"},"roles":{"type":"array","items":{"type":"string"},"description":"Role ids granted to the resulting TeamMember within the scope of this team.","default":[]},"mode":{"anyOf":[{"type":"string","enum":["auto"]},{"type":"string","enum":["invite"]}],"description":"`auto` adds the user directly if they already exist in the tenant and only falls back to a ticketed invite when the email is unknown. `invite` always issues a ticket + email regardless of whether the user exists."},"redirect_uri":{"type":"string","description":"Where to redirect the invitee after a successful accept. Defaults to `/`."}},"additionalProperties":false,"description":"Payload for inviting a user to a team by email. `mode='auto'` (default) adds the user immediately if they already exist in the tenant and falls back to a ticketed invite otherwise. `mode='invite'` always issues a ticket + email."}}},"description":"Payload for inviting a user to a team by email. `mode='auto'` (default) adds the user immediately if they already exist in the tenant and falls back to a ticketed invite otherwise. `mode='invite'` always issues a ticket + email."},"parameters":[{"schema":{"type":"string"},"in":"path","name":"team_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"anyOf":[{"type":"string","enum":["invited"]},{"type":"string","enum":["added"]}],"description":"`invited` when a ticket + email was issued, `added` when the user was added directly (auto mode + existing user)."},"ticket_id":{"type":"string","description":"ID of the issued invite ticket (when status=invited)."},"member":{"type":"object","required":["id","user","team","roles","account","createdAt"],"properties":{"id":{"type":"string","description":"TeamMember ID"},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}],"description":"User that belongs to the team. Returned as an id by default; pass `?expand=user` to inline the full User object."},"team":{"anyOf":[{"$ref":"#/components/schemas/Team"},{"type":"string"},{}],"description":"Team the user belongs to. Returned as an id by default; pass `?expand=team` to inline the full Team object."},"roles":{"type":"array","items":{"anyOf":[{"$ref":"#/components/schemas/Role"},{"type":"string"},{}]},"description":"Roles granted to the user *within this team*. Distinct from account-wide RoleMember grants — these only apply in the scope of the team. Each entry is an id by default; pass `?expand=roles` to inline."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"TeamMemberMetadata","default":{}},"createdAt":{"type":"string","description":"TeamMember creation date"},"updatedAt":{"type":"string","description":"TeamMember updated date"}},"description":"TeamMember"}},"additionalProperties":false}}}},"400":{"description":"`invite_used` — The invite was already accepted.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invite_used","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"get":{"operationId":"team/listInvites","summary":"List pending invites for a Team","tags":["team"],"description":"Returns all pending (not consumed, not expired) team invites for the given team. Accepts a FaableQL `query` parameter on the `email` field.","parameters":[{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"FaableQL filter. Supported fields: `email`. Example: `?query=email:foo@bar.com`"},{"schema":{"type":"string"},"in":"path","name":"team_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"type":"object","required":["id","email","team","roles","mode","expires_at","account","createdAt"],"properties":{"id":{"type":"string","description":"TeamInvite ID"},"email":{"type":"string","description":"Email address that was invited"},"team":{"anyOf":[{"$ref":"#/components/schemas/Team"},{"type":"string"},{}],"description":"Team the invitee will be added to upon acceptance."},"inviter":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}],"description":"User that created the invitation, if known."},"roles":{"type":"array","items":{"type":"string"},"description":"Role ids that will be applied to the new TeamMember on acceptance."},"mode":{"anyOf":[{"type":"string","enum":["auto"]},{"type":"string","enum":["invite"]}],"description":"`auto` adds the user directly if they already exist in the tenant and only falls back to a ticketed invite when the email is unknown. `invite` always issues a ticket + email regardless of whether the user exists."},"redirect_uri":{"type":"string","description":"URI to redirect the invitee to after a successful accept."},"expires_at":{"type":"string","description":"ISO 8601 timestamp at which the invite stops being valid."},"consume_date":{"type":"string","description":"ISO 8601 timestamp at which the invite was accepted. Absent for pending invites."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"TeamInviteMetadata","default":{}},"createdAt":{"type":"string","description":"TeamInvite creation date"},"updatedAt":{"type":"string","description":"TeamInvite updated date"}},"description":"TeamInvite"}}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/team/{team_id}/invite/{ticket_id}":{"delete":{"operationId":"team/revokeInvite","summary":"Revoke a pending team invite","tags":["team"],"description":"Marks a pending team invite as consumed so it can no longer be accepted. Returns 400 if the invite is already consumed and 404 if it does not exist or belongs to a different team.","parameters":[{"schema":{"type":"string"},"in":"path","name":"team_id","required":true},{"schema":{"type":"string"},"in":"path","name":"ticket_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["revoked"]}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`invite_not_found` — No invite with that id in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invite_not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/invite-verify":{"get":{"operationId":"team_invite_verify","summary":"Verify a team-invite ticket and add the user to the team","tags":["team"],"description":"Entry point for the link sent to the invitee, which carries `token` — the invite's own secret. `ticket` (the invite id) is accepted only for invites issued before invites had a secret. Consumes the ticket, creates the user if needed (with `email_verified=true` — the click proves ownership), and adds them to the team with the invited roles. Then redirects to the `redirect_uri` supplied at invite time with `?status=accepted` appended; falls back to `/flow/team-invite-done` on the auth host when no `redirect_uri` was given. Idempotent if the user is already a member.","parameters":[{"schema":{"type":"string","minLength":1},"in":"query","name":"token","required":false},{"schema":{"type":"string","minLength":1},"in":"query","name":"ticket","required":false}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_ticket` — The ticket does not exist or is not the kind this endpoint accepts.\n\n`ticket_expired` — The ticket is past its expiry.\n\n`ticket_used` — The ticket was already consumed.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_ticket","ticket_expired","ticket_used","validation_error"]}}}]}}}},"404":{"description":"`invite_not_found` — No invite with that id in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invite_not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/api":{"get":{"operationId":"api/list","summary":"List Apis","tags":["api"],"description":"List Apis","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"string"},"in":"query","name":"name","required":false,"description":"Exact match on `name`. Equivalent to `?query=name:<value>`."},{"schema":{"type":"string"},"in":"query","name":"slug","required":false,"description":"Exact match on `slug`. Equivalent to `?query=slug:<value>`."},{"schema":{"type":"string"},"in":"query","name":"identifier","required":false,"description":"Exact match on `identifier`. Equivalent to `?query=identifier:<value>`."},{"schema":{"type":"string"},"in":"query","name":"token_dialect","required":false,"description":"Exact match on `token_dialect`. Equivalent to `?query=token_dialect:<value>`."},{"schema":{"type":"string"},"in":"query","name":"enforce_policies","required":false,"description":"Exact match on `enforce_policies`. Equivalent to `?query=enforce_policies:<value>`."},{"schema":{"type":"string"},"in":"query","name":"allow_offline_access","required":false,"description":"Exact match on `allow_offline_access`. Equivalent to `?query=allow_offline_access:<value>`."},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false,"description":"Full-text search across: `name`, `description`, `identifier`, `slug`."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/Api"}}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"api/create","summary":"Create Api","tags":["api"],"description":"Create Api","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","identifier"],"properties":{"name":{"type":"string","description":"Human-readable name for the API.","example":"My Backend API"},"identifier":{"type":"string","description":"Audience URL clients pass as `audience=` when requesting a token for this API. Must be unique within the tenant and is immutable after creation.","example":"https://api.faable.com"},"description":{"type":"string","description":"Optional human-readable description of the API."},"permissions":{"type":"array","items":{"$ref":"#/components/schemas/PermissionCreate"},"description":"Set of permissions (scopes) this API exposes. Tokens issued with `audience=identifier` will only carry these in their `permissions` claim.","default":[]},"signing_alg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign access_tokens issued for this API. Currently only RS256 (the account keystore default) is implemented."},"token_dialect":{"anyOf":[{"type":"string","enum":["access_token"]},{"type":"string","enum":["access_token_authz"]}],"description":"Shape of access_tokens issued for this API. `access_token` emits the standard OAuth2 token. `access_token_authz` additionally embeds a `permissions` claim listing the permissions granted to the subject for this API."},"token_lifetime":{"type":"integer","description":"Access_token lifetime in seconds. Defaults to 86400 (24h) if omitted.","minimum":60,"maximum":2592000},"enforce_policies":{"type":"boolean","description":"When true, requested scopes are filtered against `permissions` at token issuance."},"allow_offline_access":{"type":"boolean","description":"When true, clients can request `offline_access` and receive a refresh_token for this API."},"skip_consent":{"type":"boolean","description":"When true, skip the consent prompt for first-party clients."},"include_teams_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `teams` claim with the subject team slugs."},"include_roles_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `roles` claim with the subject role names."},"include_teams_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `teams` claim with the subject team slugs."},"include_roles_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `roles` claim with the subject role names."}},"additionalProperties":false,"description":"Payload for creating an Api (a.k.a. Resource Server / Audience). The `identifier` becomes the `aud` claim in tokens issued for this API and cannot be changed after creation."}}},"description":"Payload for creating an Api (a.k.a. Resource Server / Audience). The `identifier` becomes the `aud` claim in tokens issued for this API and cannot be changed after creation."},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Api","content":{"application/json":{"schema":{"type":"object","required":["id","name","slug","identifier","permissions","signing_alg","token_dialect","token_lifetime","enforce_policies","allow_offline_access","skip_consent","include_teams_in_access_token","include_roles_in_access_token","include_teams_in_id_token","include_roles_in_id_token","account","createdAt"],"properties":{"id":{"type":"string","description":"Api ID"},"name":{"type":"string","description":"Human-readable name for the API. Shown in the dashboard and consent prompts.","example":"My Backend API"},"slug":{"type":"string","description":"URL-safe identifier auto-derived from `name`. Unique within the tenant."},"identifier":{"type":"string","description":"Audience URL that clients pass as `audience=` when requesting a token for this API. Becomes the `aud` claim in the issued access_token. Immutable after creation and unique within the tenant.","example":"https://api.faable.com"},"description":{"type":"string","description":"Optional human-readable description of the API."},"permissions":{"type":"array","items":{"$ref":"#/components/schemas/Permission"},"description":"Set of permissions (scopes) this API exposes. When a token is issued with `audience=identifier`, the requested `scope` is intersected with this list to populate the `permissions` claim."},"signing_alg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign access_tokens issued for this API. Currently only RS256 (the account keystore default) is implemented."},"token_dialect":{"anyOf":[{"type":"string","enum":["access_token"]},{"type":"string","enum":["access_token_authz"]}],"description":"Shape of access_tokens issued for this API. `access_token` emits the standard OAuth2 token. `access_token_authz` additionally embeds a `permissions` claim listing the permissions granted to the subject for this API."},"token_lifetime":{"type":"integer","description":"Access_token lifetime in seconds. Default 86400 (24h). Applied at token issuance time (overrides the generic default).","minimum":60,"maximum":2592000},"enforce_policies":{"type":"boolean","description":"When true, only scopes that match a permission in `permissions` are forwarded into the access_token. When false, requested scopes are echoed back verbatim with no filtering."},"allow_offline_access":{"type":"boolean","description":"When true, the API is eligible to be the target of a `refresh_token` (i.e. clients can request `offline_access` against it)."},"skip_consent":{"type":"boolean","description":"When true, the consent prompt is skipped for first-party clients (clients owned by the same tenant as the API)."},"include_teams_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `teams` claim listing the slugs of the teams the subject is a member of within the tenant."},"include_roles_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `roles` claim listing the names of the roles the subject holds across their team memberships within the tenant."},"include_teams_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `teams` claim with the subject team slugs. The id_token only sees the flag when the client requested `audience=` so this API is resolved at issuance time."},"include_roles_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `roles` claim with the subject role names. Same audience caveat as `include_teams_in_id_token`."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ApiMetadata","default":{}},"createdAt":{"type":"string","description":"Api creation date"},"updatedAt":{"type":"string","description":"Api updated date"}},"description":"Api"}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/api/{api_id}":{"get":{"operationId":"api/get","summary":"Get Api","tags":["api"],"description":"Get Api","parameters":[{"schema":{"type":"string"},"in":"path","name":"api_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Api","content":{"application/json":{"schema":{"type":"object","required":["id","name","slug","identifier","permissions","signing_alg","token_dialect","token_lifetime","enforce_policies","allow_offline_access","skip_consent","include_teams_in_access_token","include_roles_in_access_token","include_teams_in_id_token","include_roles_in_id_token","account","createdAt"],"properties":{"id":{"type":"string","description":"Api ID"},"name":{"type":"string","description":"Human-readable name for the API. Shown in the dashboard and consent prompts.","example":"My Backend API"},"slug":{"type":"string","description":"URL-safe identifier auto-derived from `name`. Unique within the tenant."},"identifier":{"type":"string","description":"Audience URL that clients pass as `audience=` when requesting a token for this API. Becomes the `aud` claim in the issued access_token. Immutable after creation and unique within the tenant.","example":"https://api.faable.com"},"description":{"type":"string","description":"Optional human-readable description of the API."},"permissions":{"type":"array","items":{"$ref":"#/components/schemas/Permission"},"description":"Set of permissions (scopes) this API exposes. When a token is issued with `audience=identifier`, the requested `scope` is intersected with this list to populate the `permissions` claim."},"signing_alg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign access_tokens issued for this API. Currently only RS256 (the account keystore default) is implemented."},"token_dialect":{"anyOf":[{"type":"string","enum":["access_token"]},{"type":"string","enum":["access_token_authz"]}],"description":"Shape of access_tokens issued for this API. `access_token` emits the standard OAuth2 token. `access_token_authz` additionally embeds a `permissions` claim listing the permissions granted to the subject for this API."},"token_lifetime":{"type":"integer","description":"Access_token lifetime in seconds. Default 86400 (24h). Applied at token issuance time (overrides the generic default).","minimum":60,"maximum":2592000},"enforce_policies":{"type":"boolean","description":"When true, only scopes that match a permission in `permissions` are forwarded into the access_token. When false, requested scopes are echoed back verbatim with no filtering."},"allow_offline_access":{"type":"boolean","description":"When true, the API is eligible to be the target of a `refresh_token` (i.e. clients can request `offline_access` against it)."},"skip_consent":{"type":"boolean","description":"When true, the consent prompt is skipped for first-party clients (clients owned by the same tenant as the API)."},"include_teams_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `teams` claim listing the slugs of the teams the subject is a member of within the tenant."},"include_roles_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `roles` claim listing the names of the roles the subject holds across their team memberships within the tenant."},"include_teams_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `teams` claim with the subject team slugs. The id_token only sees the flag when the client requested `audience=` so this API is resolved at issuance time."},"include_roles_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `roles` claim with the subject role names. Same audience caveat as `include_teams_in_id_token`."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ApiMetadata","default":{}},"createdAt":{"type":"string","description":"Api creation date"},"updatedAt":{"type":"string","description":"Api updated date"}},"description":"Api"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"api/update","summary":"Update Api","tags":["api"],"description":"Update Api","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Human-readable name for the API."},"description":{"type":"string","description":"Optional human-readable description of the API."},"permissions":{"type":"array","items":{"$ref":"#/components/schemas/PermissionCreate"},"description":"Replaces the full permissions list. Tokens issued AFTER the update will use the new list; tokens already in circulation are unchanged."},"signing_alg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign access_tokens issued for this API. Currently only RS256 (the account keystore default) is implemented."},"token_dialect":{"anyOf":[{"type":"string","enum":["access_token"]},{"type":"string","enum":["access_token_authz"]}],"description":"Shape of access_tokens issued for this API. `access_token` emits the standard OAuth2 token. `access_token_authz` additionally embeds a `permissions` claim listing the permissions granted to the subject for this API."},"token_lifetime":{"type":"integer","description":"Access_token lifetime in seconds.","minimum":60,"maximum":2592000},"enforce_policies":{"type":"boolean","description":"When true, requested scopes are filtered against `permissions` at token issuance."},"allow_offline_access":{"type":"boolean","description":"When true, clients can request `offline_access` and receive a refresh_token for this API."},"skip_consent":{"type":"boolean","description":"When true, skip the consent prompt for first-party clients."},"include_teams_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `teams` claim with the subject team slugs."},"include_roles_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `roles` claim with the subject role names."},"include_teams_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `teams` claim with the subject team slugs."},"include_roles_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `roles` claim with the subject role names."}},"additionalProperties":false,"description":"Partial update for an Api. `identifier` is immutable and not present here; recreate the API if you need to change it."}}},"description":"Partial update for an Api. `identifier` is immutable and not present here; recreate the API if you need to change it."},"parameters":[{"schema":{"type":"string"},"in":"path","name":"api_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Api","content":{"application/json":{"schema":{"type":"object","required":["id","name","slug","identifier","permissions","signing_alg","token_dialect","token_lifetime","enforce_policies","allow_offline_access","skip_consent","include_teams_in_access_token","include_roles_in_access_token","include_teams_in_id_token","include_roles_in_id_token","account","createdAt"],"properties":{"id":{"type":"string","description":"Api ID"},"name":{"type":"string","description":"Human-readable name for the API. Shown in the dashboard and consent prompts.","example":"My Backend API"},"slug":{"type":"string","description":"URL-safe identifier auto-derived from `name`. Unique within the tenant."},"identifier":{"type":"string","description":"Audience URL that clients pass as `audience=` when requesting a token for this API. Becomes the `aud` claim in the issued access_token. Immutable after creation and unique within the tenant.","example":"https://api.faable.com"},"description":{"type":"string","description":"Optional human-readable description of the API."},"permissions":{"type":"array","items":{"$ref":"#/components/schemas/Permission"},"description":"Set of permissions (scopes) this API exposes. When a token is issued with `audience=identifier`, the requested `scope` is intersected with this list to populate the `permissions` claim."},"signing_alg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign access_tokens issued for this API. Currently only RS256 (the account keystore default) is implemented."},"token_dialect":{"anyOf":[{"type":"string","enum":["access_token"]},{"type":"string","enum":["access_token_authz"]}],"description":"Shape of access_tokens issued for this API. `access_token` emits the standard OAuth2 token. `access_token_authz` additionally embeds a `permissions` claim listing the permissions granted to the subject for this API."},"token_lifetime":{"type":"integer","description":"Access_token lifetime in seconds. Default 86400 (24h). Applied at token issuance time (overrides the generic default).","minimum":60,"maximum":2592000},"enforce_policies":{"type":"boolean","description":"When true, only scopes that match a permission in `permissions` are forwarded into the access_token. When false, requested scopes are echoed back verbatim with no filtering."},"allow_offline_access":{"type":"boolean","description":"When true, the API is eligible to be the target of a `refresh_token` (i.e. clients can request `offline_access` against it)."},"skip_consent":{"type":"boolean","description":"When true, the consent prompt is skipped for first-party clients (clients owned by the same tenant as the API)."},"include_teams_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `teams` claim listing the slugs of the teams the subject is a member of within the tenant."},"include_roles_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `roles` claim listing the names of the roles the subject holds across their team memberships within the tenant."},"include_teams_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `teams` claim with the subject team slugs. The id_token only sees the flag when the client requested `audience=` so this API is resolved at issuance time."},"include_roles_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `roles` claim with the subject role names. Same audience caveat as `include_teams_in_id_token`."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ApiMetadata","default":{}},"createdAt":{"type":"string","description":"Api creation date"},"updatedAt":{"type":"string","description":"Api updated date"}},"description":"Api"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"delete":{"operationId":"api/delete","summary":"Delete Api","tags":["api"],"description":"Delete Api","parameters":[{"schema":{"type":"string"},"in":"path","name":"api_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Api","content":{"application/json":{"schema":{"type":"object","required":["id","name","slug","identifier","permissions","signing_alg","token_dialect","token_lifetime","enforce_policies","allow_offline_access","skip_consent","include_teams_in_access_token","include_roles_in_access_token","include_teams_in_id_token","include_roles_in_id_token","account","createdAt"],"properties":{"id":{"type":"string","description":"Api ID"},"name":{"type":"string","description":"Human-readable name for the API. Shown in the dashboard and consent prompts.","example":"My Backend API"},"slug":{"type":"string","description":"URL-safe identifier auto-derived from `name`. Unique within the tenant."},"identifier":{"type":"string","description":"Audience URL that clients pass as `audience=` when requesting a token for this API. Becomes the `aud` claim in the issued access_token. Immutable after creation and unique within the tenant.","example":"https://api.faable.com"},"description":{"type":"string","description":"Optional human-readable description of the API."},"permissions":{"type":"array","items":{"$ref":"#/components/schemas/Permission"},"description":"Set of permissions (scopes) this API exposes. When a token is issued with `audience=identifier`, the requested `scope` is intersected with this list to populate the `permissions` claim."},"signing_alg":{"anyOf":[{"type":"string","enum":["RS256"]}],"description":"JWA algorithm used to sign access_tokens issued for this API. Currently only RS256 (the account keystore default) is implemented."},"token_dialect":{"anyOf":[{"type":"string","enum":["access_token"]},{"type":"string","enum":["access_token_authz"]}],"description":"Shape of access_tokens issued for this API. `access_token` emits the standard OAuth2 token. `access_token_authz` additionally embeds a `permissions` claim listing the permissions granted to the subject for this API."},"token_lifetime":{"type":"integer","description":"Access_token lifetime in seconds. Default 86400 (24h). Applied at token issuance time (overrides the generic default).","minimum":60,"maximum":2592000},"enforce_policies":{"type":"boolean","description":"When true, only scopes that match a permission in `permissions` are forwarded into the access_token. When false, requested scopes are echoed back verbatim with no filtering."},"allow_offline_access":{"type":"boolean","description":"When true, the API is eligible to be the target of a `refresh_token` (i.e. clients can request `offline_access` against it)."},"skip_consent":{"type":"boolean","description":"When true, the consent prompt is skipped for first-party clients (clients owned by the same tenant as the API)."},"include_teams_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `teams` claim listing the slugs of the teams the subject is a member of within the tenant."},"include_roles_in_access_token":{"type":"boolean","description":"When true, access_tokens issued for this API carry a `roles` claim listing the names of the roles the subject holds across their team memberships within the tenant."},"include_teams_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `teams` claim with the subject team slugs. The id_token only sees the flag when the client requested `audience=` so this API is resolved at issuance time."},"include_roles_in_id_token":{"type":"boolean","description":"When true, id_tokens issued together with an access_token for this API carry a `roles` claim with the subject role names. Same audience caveat as `include_teams_in_id_token`."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ApiMetadata","default":{}},"createdAt":{"type":"string","description":"Api creation date"},"updatedAt":{"type":"string","description":"Api updated date"}},"description":"Api"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/account/keys":{"get":{"operationId":"account/getKeys","summary":"Get Account signing keys info","tags":["account"],"description":"Returns metadata for the Account signing keys: the current production kid, the kid queued for next rotation, and all kids currently published in the JWK Set. Does not expose private key material.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["production_kid","next_in_queue_kid","kids_active"],"properties":{"production_kid":{"type":"string","description":"kid currently used to sign new tokens"},"next_in_queue_kid":{"type":"string","description":"kid that will be promoted on the next rotation"},"kids_active":{"type":"array","items":{"type":"string"},"description":"All kids currently published in the JWK Set"}},"additionalProperties":false}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/account/keys/rotate":{"post":{"operationId":"account/rotateKeys","summary":"Rotate Account signing keys","tags":["account"],"description":"Promotes the queued key to production and generates a fresh queued key. The previously-active key is retained inside the JWK Set so tokens already issued remain verifiable via `/.well-known/jwks.json`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["rotated_at","previous_production_kid","production_kid","next_in_queue_kid","kids_active"],"properties":{"rotated_at":{"type":"string","description":"ISO 8601 timestamp of the rotation"},"previous_production_kid":{"type":"string","description":"kid that was active before rotation. Tokens signed with it are still verifiable via /.well-known/jwks.json"},"production_kid":{"type":"string","description":"kid now used to sign new tokens"},"next_in_queue_kid":{"type":"string","description":"kid that will be promoted on the next rotation"},"kids_active":{"type":"array","items":{"type":"string"},"description":"All kids currently published in the JWK Set"}},"additionalProperties":false}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/action":{"get":{"operationId":"action/list","summary":"List Actions","tags":["action"],"description":"List Actions","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/Action"}}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"action/create","summary":"Create Action","tags":["action"],"description":"Create Action","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","code"],"properties":{"name":{"type":"string","description":"Action Name","maxLength":200},"code":{"type":"string","description":"JavaScript code. Must export at least one hook: exports.onExecutePostLogin, exports.onExecuteContinue and/or exports.onExecuteClientCredentials. The triggers are derived from the exported hooks."},"enabled":{"type":"boolean","description":"Is this action active?","default":true},"order":{"type":"number","description":"Execution order: lower runs first; on ties the newest action runs first","default":0}}}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Action","content":{"application/json":{"schema":{"type":"object","required":["id","name","triggers","code","enabled","account","createdAt"],"properties":{"id":{"type":"string","description":"Action ID"},"name":{"type":"string","description":"Action Name","maxLength":200},"triggers":{"type":"array","items":{"enum":["continue","post-login","client-credentials"]},"description":"Triggers this action runs on. Derived from the hooks the code exports (`onExecutePostLogin` → post-login, `onExecuteContinue` → continue, `onExecuteClientCredentials` → client-credentials); read-only."},"code":{"type":"string","description":"JavaScript Code to execute"},"enabled":{"type":"boolean","description":"Is this action active?","default":true},"order":{"type":"number","description":"Execution order: lower runs first; on ties the newest action runs first","default":0},"revision":{"type":"number","description":"Monotonic edit counter — increments on every update. Audit rows stamp the revision that produced each allow/deny (`data.action_revision`); `updatedAt` dates the current revision.","default":1},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ActionMetadata","default":{}},"createdAt":{"type":"string","description":"Action creation date"},"updatedAt":{"type":"string","description":"Action updated date"}},"description":"Action"}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/action/{action_id}":{"get":{"operationId":"action/get","summary":"Get Action","tags":["action"],"description":"Get Action","parameters":[{"schema":{"type":"string"},"in":"path","name":"action_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Action","content":{"application/json":{"schema":{"type":"object","required":["id","name","triggers","code","enabled","account","createdAt"],"properties":{"id":{"type":"string","description":"Action ID"},"name":{"type":"string","description":"Action Name","maxLength":200},"triggers":{"type":"array","items":{"enum":["continue","post-login","client-credentials"]},"description":"Triggers this action runs on. Derived from the hooks the code exports (`onExecutePostLogin` → post-login, `onExecuteContinue` → continue, `onExecuteClientCredentials` → client-credentials); read-only."},"code":{"type":"string","description":"JavaScript Code to execute"},"enabled":{"type":"boolean","description":"Is this action active?","default":true},"order":{"type":"number","description":"Execution order: lower runs first; on ties the newest action runs first","default":0},"revision":{"type":"number","description":"Monotonic edit counter — increments on every update. Audit rows stamp the revision that produced each allow/deny (`data.action_revision`); `updatedAt` dates the current revision.","default":1},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ActionMetadata","default":{}},"createdAt":{"type":"string","description":"Action creation date"},"updatedAt":{"type":"string","description":"Action updated date"}},"description":"Action"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"action/update","summary":"Update Action","tags":["action"],"description":"Update Action","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Action Name","maxLength":200},"code":{"type":"string","description":"JavaScript code. The triggers are re-derived from the hooks it exports."},"enabled":{"type":"boolean","description":"Is this action active?"},"order":{"type":"number","description":"Execution order"}},"description":"Partial update for an Action. Only the supplied fields are modified; `triggers` is read-only and follows `code`.","additionalProperties":false}}},"description":"Partial update for an Action. Only the supplied fields are modified; `triggers` is read-only and follows `code`."},"parameters":[{"schema":{"type":"string"},"in":"path","name":"action_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Action","content":{"application/json":{"schema":{"type":"object","required":["id","name","triggers","code","enabled","account","createdAt"],"properties":{"id":{"type":"string","description":"Action ID"},"name":{"type":"string","description":"Action Name","maxLength":200},"triggers":{"type":"array","items":{"enum":["continue","post-login","client-credentials"]},"description":"Triggers this action runs on. Derived from the hooks the code exports (`onExecutePostLogin` → post-login, `onExecuteContinue` → continue, `onExecuteClientCredentials` → client-credentials); read-only."},"code":{"type":"string","description":"JavaScript Code to execute"},"enabled":{"type":"boolean","description":"Is this action active?","default":true},"order":{"type":"number","description":"Execution order: lower runs first; on ties the newest action runs first","default":0},"revision":{"type":"number","description":"Monotonic edit counter — increments on every update. Audit rows stamp the revision that produced each allow/deny (`data.action_revision`); `updatedAt` dates the current revision.","default":1},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ActionMetadata","default":{}},"createdAt":{"type":"string","description":"Action creation date"},"updatedAt":{"type":"string","description":"Action updated date"}},"description":"Action"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"delete":{"operationId":"action/delete","summary":"Delete Action","tags":["action"],"description":"Delete Action","parameters":[{"schema":{"type":"string"},"in":"path","name":"action_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Action","content":{"application/json":{"schema":{"type":"object","required":["id","name","triggers","code","enabled","account","createdAt"],"properties":{"id":{"type":"string","description":"Action ID"},"name":{"type":"string","description":"Action Name","maxLength":200},"triggers":{"type":"array","items":{"enum":["continue","post-login","client-credentials"]},"description":"Triggers this action runs on. Derived from the hooks the code exports (`onExecutePostLogin` → post-login, `onExecuteContinue` → continue, `onExecuteClientCredentials` → client-credentials); read-only."},"code":{"type":"string","description":"JavaScript Code to execute"},"enabled":{"type":"boolean","description":"Is this action active?","default":true},"order":{"type":"number","description":"Execution order: lower runs first; on ties the newest action runs first","default":0},"revision":{"type":"number","description":"Monotonic edit counter — increments on every update. Audit rows stamp the revision that produced each allow/deny (`data.action_revision`); `updatedAt` dates the current revision.","default":1},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"ActionMetadata","default":{}},"createdAt":{"type":"string","description":"Action creation date"},"updatedAt":{"type":"string","description":"Action updated date"}},"description":"Action"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/loginflow/resolved":{"get":{"operationId":"loginflow/resolved","summary":"The graph this account (or client) runs right now","tags":["loginflow"],"description":"What a login actually walks: the published graph of the bound flow, or the graph compiled from the settings when nothing is bound or published. With `client_id`, resolved for that client (its own flow, else the account's, else compiled from its overrides). The bound flow, if any, comes along with its draft so an editor can show both.","parameters":[{"schema":{"type":"string"},"in":"query","name":"client_id","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["source","flow_id","revision","graph","bound"],"properties":{"source":{"anyOf":[{"type":"string","enum":["compiled"]},{"type":"string","enum":["published"]}]},"flow_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"revision":{"type":"integer"},"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"bound":{"anyOf":[{"type":"object","required":["flow_id","bound_to","name","revision","published_revision","draft","history"],"properties":{"flow_id":{"type":"string"},"bound_to":{"anyOf":[{"type":"string","enum":["account"]},{"type":"string","enum":["client"]}]},"name":{"type":"string"},"revision":{"type":"integer"},"published_revision":{"anyOf":[{"type":"integer"},{"type":"null"}]},"draft":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"layout":{"type":"object","description":"Node positions in the editor. Presentation only.","additionalProperties":{"type":"object","required":["x","y"],"properties":{"x":{"type":"number"},"y":{"type":"number"}},"additionalProperties":false}},"history":{"type":"array","items":{"type":"integer"}}}},{"type":"null"}]}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/loginflow/materialize":{"post":{"operationId":"loginflow/materialize","summary":"Start a custom flow from what runs today","tags":["loginflow"],"description":"Creates a login flow whose draft is the graph the account (or, with `client_id`, that client) currently runs — compiled from its settings — and binds it. Nothing changes for logins until the flow is published: a bound flow with no published graph keeps running the compiled one. Never a blank canvas: customising always starts from the tenant's own behaviour.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"name":{"type":"string","maxLength":200}}}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"LoginFlow","content":{"application/json":{"schema":{"type":"object","required":["id","name","trigger","graph","revision","history","account","createdAt"],"properties":{"id":{"type":"string","description":"LoginFlow ID"},"name":{"type":"string","maxLength":200},"trigger":{"type":"string","enum":["login"]},"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer","description":"Monotonic edit counter, bumped on every update. A publish freezes the graph under the revision it had; a login in flight pins the revision it started on."},"published":{"type":"object","required":["graph","revision","published_at"],"properties":{"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer"},"published_at":{"type":"string"}},"additionalProperties":false},"history":{"type":"array","items":{"type":"object","required":["graph","revision","published_at"],"properties":{"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer"},"published_at":{"type":"string"}},"additionalProperties":false},"description":"Previously published graphs, newest first, for rollback."},"layout":{"type":"object","description":"Node positions in the editor. Presentation only.","additionalProperties":{"type":"object","required":["x","y"],"properties":{"x":{"type":"number"},"y":{"type":"number"}},"additionalProperties":false}},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"LoginFlowMetadata","default":{}},"createdAt":{"type":"string","description":"LoginFlow creation date"},"updatedAt":{"type":"string","description":"LoginFlow updated date"}},"description":"LoginFlow"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/loginflow/{loginflow_id}/validate":{"post":{"operationId":"loginflow/validate","summary":"Check a draft against the rules a publish enforces","tags":["loginflow"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"}}}}}},"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"loginflow_id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["ok","issues"],"properties":{"ok":{"type":"boolean"},"issues":{"type":"array","items":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"node":{"type":"string"},"handle":{"type":"string"},"message":{"type":"string"}}}}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/loginflow/{loginflow_id}/publish":{"post":{"operationId":"loginflow/publish","summary":"Make the draft the graph logins run","tags":["loginflow"],"description":"Validates the draft and freezes it as `published` under its current revision; the previous published graph moves to `history`. Logins already in flight keep the revision they started on. Refused with the list of issues when the draft does not validate.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"}}}}}},"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"loginflow_id","required":true}],"responses":{"200":{"description":"LoginFlow","content":{"application/json":{"schema":{"type":"object","required":["id","name","trigger","graph","revision","history","account","createdAt"],"properties":{"id":{"type":"string","description":"LoginFlow ID"},"name":{"type":"string","maxLength":200},"trigger":{"type":"string","enum":["login"]},"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer","description":"Monotonic edit counter, bumped on every update. A publish freezes the graph under the revision it had; a login in flight pins the revision it started on."},"published":{"type":"object","required":["graph","revision","published_at"],"properties":{"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer"},"published_at":{"type":"string"}},"additionalProperties":false},"history":{"type":"array","items":{"type":"object","required":["graph","revision","published_at"],"properties":{"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer"},"published_at":{"type":"string"}},"additionalProperties":false},"description":"Previously published graphs, newest first, for rollback."},"layout":{"type":"object","description":"Node positions in the editor. Presentation only.","additionalProperties":{"type":"object","required":["x","y"],"properties":{"x":{"type":"number"},"y":{"type":"number"}},"additionalProperties":false}},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"LoginFlowMetadata","default":{}},"createdAt":{"type":"string","description":"LoginFlow creation date"},"updatedAt":{"type":"string","description":"LoginFlow updated date"}},"description":"LoginFlow"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["error","issues"],"properties":{"error":{"type":"string"},"issues":{"type":"array","items":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"node":{"type":"string"},"handle":{"type":"string"},"message":{"type":"string"}}}}}}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/loginflow/{loginflow_id}/rollback":{"post":{"operationId":"loginflow/rollback","summary":"Run a previously published revision again","tags":["loginflow"],"description":"Makes the named revision from `history` the published graph; the one being replaced moves to `history`. The draft is not touched.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["revision"],"properties":{"revision":{"type":"integer"}}}}}},"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"loginflow_id","required":true}],"responses":{"200":{"description":"LoginFlow","content":{"application/json":{"schema":{"type":"object","required":["id","name","trigger","graph","revision","history","account","createdAt"],"properties":{"id":{"type":"string","description":"LoginFlow ID"},"name":{"type":"string","maxLength":200},"trigger":{"type":"string","enum":["login"]},"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer","description":"Monotonic edit counter, bumped on every update. A publish freezes the graph under the revision it had; a login in flight pins the revision it started on."},"published":{"type":"object","required":["graph","revision","published_at"],"properties":{"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer"},"published_at":{"type":"string"}},"additionalProperties":false},"history":{"type":"array","items":{"type":"object","required":["graph","revision","published_at"],"properties":{"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer"},"published_at":{"type":"string"}},"additionalProperties":false},"description":"Previously published graphs, newest first, for rollback."},"layout":{"type":"object","description":"Node positions in the editor. Presentation only.","additionalProperties":{"type":"object","required":["x","y"],"properties":{"x":{"type":"number"},"y":{"type":"number"}},"additionalProperties":false}},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"LoginFlowMetadata","default":{}},"createdAt":{"type":"string","description":"LoginFlow creation date"},"updatedAt":{"type":"string","description":"LoginFlow updated date"}},"description":"LoginFlow"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`revision_not_found` — The revision is not in the flow history.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["revision_not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/loginflow/{loginflow_id}/preview-token":{"post":{"operationId":"loginflow/previewToken","summary":"Try the draft on a real login, in your browser only","tags":["loginflow"],"description":"Returns a short-lived token. Open `/authorize?…&flow_preview=<token>` (or the hosted login with the same parameter) and that login runs the DRAFT of this flow instead of what is published, for that browser only. Nothing is published.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"loginflow_id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["token","expires_in"],"properties":{"token":{"type":"string"},"expires_in":{"type":"integer"}}}}}},"400":{"description":"`flow_not_found` — The login flow does not exist in this account.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["flow_not_found","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/loginflow":{"get":{"operationId":"loginflow/list","summary":"List Loginflows","tags":["loginflow"],"description":"List Loginflows","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/LoginFlow"}}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"loginflow/create","summary":"Create Loginflow","tags":["loginflow"],"description":"Create Loginflow","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","graph"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"layout":{"type":"object","description":"Node positions in the editor. Presentation only.","additionalProperties":{"type":"object","required":["x","y"],"properties":{"x":{"type":"number"},"y":{"type":"number"}},"additionalProperties":false}}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"LoginFlow","content":{"application/json":{"schema":{"type":"object","required":["id","name","trigger","graph","revision","history","account","createdAt"],"properties":{"id":{"type":"string","description":"LoginFlow ID"},"name":{"type":"string","maxLength":200},"trigger":{"type":"string","enum":["login"]},"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer","description":"Monotonic edit counter, bumped on every update. A publish freezes the graph under the revision it had; a login in flight pins the revision it started on."},"published":{"type":"object","required":["graph","revision","published_at"],"properties":{"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer"},"published_at":{"type":"string"}},"additionalProperties":false},"history":{"type":"array","items":{"type":"object","required":["graph","revision","published_at"],"properties":{"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer"},"published_at":{"type":"string"}},"additionalProperties":false},"description":"Previously published graphs, newest first, for rollback."},"layout":{"type":"object","description":"Node positions in the editor. Presentation only.","additionalProperties":{"type":"object","required":["x","y"],"properties":{"x":{"type":"number"},"y":{"type":"number"}},"additionalProperties":false}},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"LoginFlowMetadata","default":{}},"createdAt":{"type":"string","description":"LoginFlow creation date"},"updatedAt":{"type":"string","description":"LoginFlow updated date"}},"description":"LoginFlow"}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/loginflow/{loginflow_id}":{"get":{"operationId":"loginflow/get","summary":"Get Loginflow","tags":["loginflow"],"description":"Get Loginflow","parameters":[{"schema":{"type":"string"},"in":"path","name":"loginflow_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"LoginFlow","content":{"application/json":{"schema":{"type":"object","required":["id","name","trigger","graph","revision","history","account","createdAt"],"properties":{"id":{"type":"string","description":"LoginFlow ID"},"name":{"type":"string","maxLength":200},"trigger":{"type":"string","enum":["login"]},"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer","description":"Monotonic edit counter, bumped on every update. A publish freezes the graph under the revision it had; a login in flight pins the revision it started on."},"published":{"type":"object","required":["graph","revision","published_at"],"properties":{"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer"},"published_at":{"type":"string"}},"additionalProperties":false},"history":{"type":"array","items":{"type":"object","required":["graph","revision","published_at"],"properties":{"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer"},"published_at":{"type":"string"}},"additionalProperties":false},"description":"Previously published graphs, newest first, for rollback."},"layout":{"type":"object","description":"Node positions in the editor. Presentation only.","additionalProperties":{"type":"object","required":["x","y"],"properties":{"x":{"type":"number"},"y":{"type":"number"}},"additionalProperties":false}},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"LoginFlowMetadata","default":{}},"createdAt":{"type":"string","description":"LoginFlow creation date"},"updatedAt":{"type":"string","description":"LoginFlow updated date"}},"description":"LoginFlow"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"loginflow/update","summary":"Update Loginflow","tags":["loginflow"],"description":"Update Loginflow","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"layout":{"type":"object","description":"Node positions in the editor. Presentation only.","additionalProperties":{"type":"object","required":["x","y"],"properties":{"x":{"type":"number"},"y":{"type":"number"}},"additionalProperties":false}}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"loginflow_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"LoginFlow","content":{"application/json":{"schema":{"type":"object","required":["id","name","trigger","graph","revision","history","account","createdAt"],"properties":{"id":{"type":"string","description":"LoginFlow ID"},"name":{"type":"string","maxLength":200},"trigger":{"type":"string","enum":["login"]},"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer","description":"Monotonic edit counter, bumped on every update. A publish freezes the graph under the revision it had; a login in flight pins the revision it started on."},"published":{"type":"object","required":["graph","revision","published_at"],"properties":{"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer"},"published_at":{"type":"string"}},"additionalProperties":false},"history":{"type":"array","items":{"type":"object","required":["graph","revision","published_at"],"properties":{"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer"},"published_at":{"type":"string"}},"additionalProperties":false},"description":"Previously published graphs, newest first, for rollback."},"layout":{"type":"object","description":"Node positions in the editor. Presentation only.","additionalProperties":{"type":"object","required":["x","y"],"properties":{"x":{"type":"number"},"y":{"type":"number"}},"additionalProperties":false}},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"LoginFlowMetadata","default":{}},"createdAt":{"type":"string","description":"LoginFlow creation date"},"updatedAt":{"type":"string","description":"LoginFlow updated date"}},"description":"LoginFlow"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"delete":{"operationId":"loginflow/delete","summary":"Delete Loginflow","tags":["loginflow"],"description":"Delete Loginflow","parameters":[{"schema":{"type":"string"},"in":"path","name":"loginflow_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"LoginFlow","content":{"application/json":{"schema":{"type":"object","required":["id","name","trigger","graph","revision","history","account","createdAt"],"properties":{"id":{"type":"string","description":"LoginFlow ID"},"name":{"type":"string","maxLength":200},"trigger":{"type":"string","enum":["login"]},"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer","description":"Monotonic edit counter, bumped on every update. A publish freezes the graph under the revision it had; a login in flight pins the revision it started on."},"published":{"type":"object","required":["graph","revision","published_at"],"properties":{"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer"},"published_at":{"type":"string"}},"additionalProperties":false},"history":{"type":"array","items":{"type":"object","required":["graph","revision","published_at"],"properties":{"graph":{"type":"object","required":["nodes","edges"],"properties":{"nodes":{"type":"array","items":{"type":"object","required":["id","type"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9_:.-]+$"},"type":{"anyOf":[{"type":"string","enum":["start"]},{"type":"string","enum":["identify"]},{"type":"string","enum":["methods"]},{"type":"string","enum":["credential"]},{"type":"string","enum":["action"]},{"type":"string","enum":["mfa_gate"]},{"type":"string","enum":["mfa_challenge"]},{"type":"string","enum":["mfa_enroll"]},{"type":"string","enum":["passkey_offer"]},{"type":"string","enum":["condition"]},{"type":"string","enum":["deny"]},{"type":"string","enum":["end"]},{"type":"string","enum":["consent"]},{"type":"string","enum":["set_data"]}]},"config":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"maxItems":100},"edges":{"type":"array","items":{"type":"object","required":["from","handle","to"],"properties":{"from":{"type":"string","minLength":1,"maxLength":64},"handle":{"type":"string","minLength":1,"maxLength":32},"to":{"type":"string","minLength":1,"maxLength":64}},"additionalProperties":false},"maxItems":300}},"additionalProperties":false,"description":"A login flow graph: typed nodes joined by named handles. See the validation rules on publish."},"revision":{"type":"integer"},"published_at":{"type":"string"}},"additionalProperties":false},"description":"Previously published graphs, newest first, for rollback."},"layout":{"type":"object","description":"Node positions in the editor. Presentation only.","additionalProperties":{"type":"object","required":["x","y"],"properties":{"x":{"type":"number"},"y":{"type":"number"}},"additionalProperties":false}},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"LoginFlowMetadata","default":{}},"createdAt":{"type":"string","description":"LoginFlow creation date"},"updatedAt":{"type":"string","description":"LoginFlow updated date"}},"description":"LoginFlow"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/notificationsubscription":{"get":{"operationId":"notificationsubscription/list","summary":"List Notificationsubscriptions","tags":["notificationsubscription"],"description":"List Notificationsubscriptions","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"string"},"in":"query","name":"active","required":false,"description":"Exact match on `active`. Equivalent to `?query=active:<value>`."},{"schema":{"type":"string"},"in":"query","name":"channel","required":false,"description":"Exact match on `channel`. Equivalent to `?query=channel:<value>`."},{"schema":{"type":"string"},"in":"query","name":"events","required":false,"description":"Exact match on `events`. Equivalent to `?query=events:<value>`."},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/NotificationSubscription"}}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"notificationsubscription/create","summary":"Create Notificationsubscription","tags":["notificationsubscription"],"description":"Create Notificationsubscription","requestBody":{"required":true,"content":{"application/json":{"schema":{"anyOf":[{"type":"object","required":["events","channel","config"],"properties":{"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["email"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["recipients"],"properties":{"recipients":{"type":"array","items":{"type":"string","description":"List of email addresses receiving notifications","format":"email"}}},"additionalProperties":false},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"NotificationSubscriptionEmailCreate","additionalProperties":false},{"type":"object","required":["events","channel","config"],"properties":{"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["webhook"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["url","secret"],"properties":{"url":{"type":"string","description":"Webhook URL","format":"uri"},"secret":{"type":"string","description":"Secret used to sign the webhook payload"}},"additionalProperties":false},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"NotificationSubscriptionWebhookCreate","additionalProperties":false}],"description":"NotificationSubscriptionCreate"}}},"description":"NotificationSubscriptionCreate"},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"NotificationSubscription","content":{"application/json":{"schema":{"anyOf":[{"type":"object","required":["id","events","channel","active","config","account","createdAt"],"properties":{"id":{"type":"string","description":"NotificationSubscriptionEmail ID"},"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["email"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["recipients"],"properties":{"recipients":{"type":"array","items":{"type":"string","description":"List of email addresses receiving notifications","format":"email"}}},"additionalProperties":false},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"NotificationSubscriptionEmailMetadata","default":{}},"createdAt":{"type":"string","description":"NotificationSubscriptionEmail creation date"},"updatedAt":{"type":"string","description":"NotificationSubscriptionEmail updated date"}},"description":"NotificationSubscriptionEmail"},{"type":"object","required":["id","events","channel","active","config","account","createdAt"],"properties":{"id":{"type":"string","description":"NotificationSubscriptionWebhook ID"},"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["webhook"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["url","secret"],"properties":{"url":{"type":"string","description":"Webhook URL","format":"uri"},"secret":{"type":"string","description":"Secret used to sign the webhook payload"}},"additionalProperties":false},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"NotificationSubscriptionWebhookMetadata","default":{}},"createdAt":{"type":"string","description":"NotificationSubscriptionWebhook creation date"},"updatedAt":{"type":"string","description":"NotificationSubscriptionWebhook updated date"}},"description":"NotificationSubscriptionWebhook"}],"description":"NotificationSubscription"}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/notificationsubscription/{notificationsubscription_id}":{"get":{"operationId":"notificationsubscription/get","summary":"Get Notificationsubscription","tags":["notificationsubscription"],"description":"Get Notificationsubscription","parameters":[{"schema":{"type":"string"},"in":"path","name":"notificationsubscription_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"NotificationSubscription","content":{"application/json":{"schema":{"anyOf":[{"type":"object","required":["id","events","channel","active","config","account","createdAt"],"properties":{"id":{"type":"string","description":"NotificationSubscriptionEmail ID"},"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["email"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["recipients"],"properties":{"recipients":{"type":"array","items":{"type":"string","description":"List of email addresses receiving notifications","format":"email"}}},"additionalProperties":false},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"NotificationSubscriptionEmailMetadata","default":{}},"createdAt":{"type":"string","description":"NotificationSubscriptionEmail creation date"},"updatedAt":{"type":"string","description":"NotificationSubscriptionEmail updated date"}},"description":"NotificationSubscriptionEmail"},{"type":"object","required":["id","events","channel","active","config","account","createdAt"],"properties":{"id":{"type":"string","description":"NotificationSubscriptionWebhook ID"},"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["webhook"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["url","secret"],"properties":{"url":{"type":"string","description":"Webhook URL","format":"uri"},"secret":{"type":"string","description":"Secret used to sign the webhook payload"}},"additionalProperties":false},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"NotificationSubscriptionWebhookMetadata","default":{}},"createdAt":{"type":"string","description":"NotificationSubscriptionWebhook creation date"},"updatedAt":{"type":"string","description":"NotificationSubscriptionWebhook updated date"}},"description":"NotificationSubscriptionWebhook"}],"description":"NotificationSubscription"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"notificationsubscription/update","summary":"Update Notificationsubscription","tags":["notificationsubscription"],"description":"Update Notificationsubscription","requestBody":{"required":true,"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["email"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","properties":{"recipients":{"type":"array","items":{"type":"string","description":"List of email addresses receiving notifications","format":"email"}}}},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Metadata update"}}},{"type":"object","properties":{"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["webhook"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","properties":{"url":{"type":"string","description":"Webhook URL","format":"uri"},"secret":{"type":"string","description":"Secret used to sign the webhook payload"}}},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Metadata update"}}}],"description":"NotificationSubscriptionUpdate"}}},"description":"NotificationSubscriptionUpdate"},"parameters":[{"schema":{"type":"string"},"in":"path","name":"notificationsubscription_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"NotificationSubscription","content":{"application/json":{"schema":{"anyOf":[{"type":"object","required":["id","events","channel","active","config","account","createdAt"],"properties":{"id":{"type":"string","description":"NotificationSubscriptionEmail ID"},"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["email"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["recipients"],"properties":{"recipients":{"type":"array","items":{"type":"string","description":"List of email addresses receiving notifications","format":"email"}}},"additionalProperties":false},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"NotificationSubscriptionEmailMetadata","default":{}},"createdAt":{"type":"string","description":"NotificationSubscriptionEmail creation date"},"updatedAt":{"type":"string","description":"NotificationSubscriptionEmail updated date"}},"description":"NotificationSubscriptionEmail"},{"type":"object","required":["id","events","channel","active","config","account","createdAt"],"properties":{"id":{"type":"string","description":"NotificationSubscriptionWebhook ID"},"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["webhook"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["url","secret"],"properties":{"url":{"type":"string","description":"Webhook URL","format":"uri"},"secret":{"type":"string","description":"Secret used to sign the webhook payload"}},"additionalProperties":false},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"NotificationSubscriptionWebhookMetadata","default":{}},"createdAt":{"type":"string","description":"NotificationSubscriptionWebhook creation date"},"updatedAt":{"type":"string","description":"NotificationSubscriptionWebhook updated date"}},"description":"NotificationSubscriptionWebhook"}],"description":"NotificationSubscription"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"delete":{"operationId":"notificationsubscription/delete","summary":"Delete Notificationsubscription","tags":["notificationsubscription"],"description":"Delete Notificationsubscription","parameters":[{"schema":{"type":"string"},"in":"path","name":"notificationsubscription_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"NotificationSubscription","content":{"application/json":{"schema":{"anyOf":[{"type":"object","required":["id","events","channel","active","config","account","createdAt"],"properties":{"id":{"type":"string","description":"NotificationSubscriptionEmail ID"},"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["email"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["recipients"],"properties":{"recipients":{"type":"array","items":{"type":"string","description":"List of email addresses receiving notifications","format":"email"}}},"additionalProperties":false},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"NotificationSubscriptionEmailMetadata","default":{}},"createdAt":{"type":"string","description":"NotificationSubscriptionEmail creation date"},"updatedAt":{"type":"string","description":"NotificationSubscriptionEmail updated date"}},"description":"NotificationSubscriptionEmail"},{"type":"object","required":["id","events","channel","active","config","account","createdAt"],"properties":{"id":{"type":"string","description":"NotificationSubscriptionWebhook ID"},"events":{"type":"array","items":{"type":"string","description":"Events to subscribe to (e.g., user.created)"}},"channel":{"type":"string","enum":["webhook"]},"active":{"type":"boolean","description":"Whether the subscription is active"},"config":{"type":"object","required":["url","secret"],"properties":{"url":{"type":"string","description":"Webhook URL","format":"uri"},"secret":{"type":"string","description":"Secret used to sign the webhook payload"}},"additionalProperties":false},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"NotificationSubscriptionWebhookMetadata","default":{}},"createdAt":{"type":"string","description":"NotificationSubscriptionWebhook creation date"},"updatedAt":{"type":"string","description":"NotificationSubscriptionWebhook updated date"}},"description":"NotificationSubscriptionWebhook"}],"description":"NotificationSubscription"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/log":{"get":{"operationId":"log/list","summary":"List Logs","tags":["log"],"description":"List Logs","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"string"},"in":"query","name":"type","required":false,"description":"Exact match on `type`. Equivalent to `?query=type:<value>`."},{"schema":{"type":"string"},"in":"query","name":"status","required":false,"description":"Exact match on `status`. Equivalent to `?query=status:<value>`."},{"schema":{"type":"string"},"in":"query","name":"origin","required":false,"description":"Exact match on `origin`. Equivalent to `?query=origin:<value>`."},{"schema":{"type":"string"},"in":"query","name":"action","required":false,"description":"Exact match on `action`. Equivalent to `?query=action:<value>`."},{"schema":{"type":"string"},"in":"query","name":"user","required":false,"description":"Exact match on `user`. Equivalent to `?query=user:<value>`."},{"schema":{"type":"string"},"in":"query","name":"client","required":false,"description":"Exact match on `client`. Equivalent to `?query=client:<value>`."},{"schema":{"type":"string"},"in":"query","name":"connection","required":false,"description":"Exact match on `connection`. Equivalent to `?query=connection:<value>`."},{"schema":{"type":"string"},"in":"query","name":"team","required":false,"description":"Exact match on `team`. Equivalent to `?query=team:<value>`."},{"schema":{"type":"string"},"in":"query","name":"identity","required":false,"description":"Exact match on `identity`. Equivalent to `?query=identity:<value>`."},{"schema":{"type":"string"},"in":"query","name":"ticket","required":false,"description":"Exact match on `ticket`. Equivalent to `?query=ticket:<value>`."},{"schema":{"type":"string"},"in":"query","name":"since","required":false,"description":"Exact match on `since`. Equivalent to `?query=since:<value>`."},{"schema":{"type":"string"},"in":"query","name":"until","required":false,"description":"Exact match on `until`. Equivalent to `?query=until:<value>`."},{"schema":{"type":"string"},"in":"query","name":"expires_before","required":false,"description":"Exact match on `expires_before`. Equivalent to `?query=expires_before:<value>`."},{"schema":{"type":"string"},"in":"query","name":"expires_after","required":false,"description":"Exact match on `expires_after`. Equivalent to `?query=expires_after:<value>`."},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"},{"schema":{"type":"array","items":{"type":"string","enum":["user","client","connection","team","identity"],"description":"Allowed expand paths on `log`: `user`, `client`, `connection`, `team`, `identity`."},"maxItems":5},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `?expand=user`)."},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false,"description":"Full-text search across: `message`."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/Log"}}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/log/{log_id}":{"get":{"operationId":"log/get","summary":"Get Log","tags":["log"],"description":"Get Log","parameters":[{"schema":{"type":"array","items":{"type":"string","enum":["user","client","connection","team","identity"],"description":"Allowed expand paths on `log`: `user`, `client`, `connection`, `team`, `identity`."},"maxItems":5},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `?expand=user`)."},{"schema":{"type":"string"},"in":"path","name":"log_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Log","content":{"application/json":{"schema":{"type":"object","required":["id","type","status","account","createdAt"],"properties":{"id":{"type":"string","description":"Log ID"},"type":{"type":"string","description":"Log type, e.g. email.user.created"},"status":{"anyOf":[{"type":"string","enum":["success"]},{"type":"string","enum":["failed"]},{"type":"string","enum":["skipped"]},{"type":"string","enum":["info"]}],"description":"Log status"},"message":{"type":"string","description":"Optional log message"},"data":{"description":"Type-specific structured payload"},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}]},"client":{"anyOf":[{"$ref":"#/components/schemas/Client"},{"type":"string"},{}]},"connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"team":{"anyOf":[{"$ref":"#/components/schemas/Team"},{"type":"string"},{}]},"identity":{"anyOf":[{"$ref":"#/components/schemas/Identity"},{"type":"string"},{}]},"ticket":{"type":"string"},"expires_at":{"type":"string","format":"date-time","description":"When this log row will be auto-deleted by the Mongo TTL monitor. Absent on rows recorded without `ttl_seconds`."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"LogMetadata","default":{}},"createdAt":{"type":"string","description":"Log creation date"},"updatedAt":{"type":"string","description":"Log updated date"}},"description":"Log"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/session":{"get":{"operationId":"session/list","summary":"List Sessions","tags":["session"],"description":"List Sessions","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"string"},"in":"query","name":"user","required":false,"description":"Exact match on `user`. Equivalent to `?query=user:<value>`."},{"schema":{"type":"string"},"in":"query","name":"client","required":false,"description":"Exact match on `client`. Equivalent to `?query=client:<value>`."},{"schema":{"type":"string"},"in":"query","name":"connection","required":false,"description":"Exact match on `connection`. Equivalent to `?query=connection:<value>`."},{"schema":{"type":"string"},"in":"query","name":"status","required":false,"description":"Exact match on `status`. Equivalent to `?query=status:<value>`."},{"schema":{"type":"string"},"in":"query","name":"sid","required":false,"description":"Exact match on `sid`. Equivalent to `?query=sid:<value>`."},{"schema":{"type":"string"},"in":"query","name":"since","required":false,"description":"Exact match on `since`. Equivalent to `?query=since:<value>`."},{"schema":{"type":"string"},"in":"query","name":"until","required":false,"description":"Exact match on `until`. Equivalent to `?query=until:<value>`."},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"},{"schema":{"type":"array","items":{"type":"string","enum":["user","client","connection"],"description":"Allowed expand paths on `session`: `user`, `client`, `connection`."},"maxItems":3},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `?expand=user`)."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/Session"}}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/session/{session_id}":{"get":{"operationId":"session/get","summary":"Get Session","tags":["session"],"description":"Get Session","parameters":[{"schema":{"type":"array","items":{"type":"string","enum":["user","client","connection"],"description":"Allowed expand paths on `session`: `user`, `client`, `connection`."},"maxItems":3},"in":"query","name":"expand","required":false,"description":"Expand id-only fields in the response. Pass one path per entry (e.g. `?expand=user`)."},{"schema":{"type":"string"},"in":"path","name":"session_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Session","content":{"application/json":{"schema":{"type":"object","required":["id","user","sid","status","last_seen_at","expires_at","account","createdAt"],"properties":{"id":{"type":"string","description":"Session ID"},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}]},"sid":{"type":"string","description":"OIDC session id. Every id_token minted for this session carries it as the `sid` claim; a back-channel or front-channel logout names the session with it."},"status":{"anyOf":[{"type":"string","enum":["active"]},{"type":"string","enum":["revoked"]}],"description":"`active` or `revoked`. An active session whose `expires_at` is in the past has expired: nothing can refresh it any more, but the row stays for the history."},"ip":{"type":"string","description":"Client IP at the last authentication."},"user_agent":{"type":"string","description":"Raw `User-Agent` header at the last authentication."},"device_name":{"type":"string","description":"The name a device gave itself when it asked for a code (device authorization grant), e.g. the hostname `faable login` runs on. Absent for browser sessions: a browser does not expose one."},"client":{"anyOf":[{"$ref":"#/components/schemas/Client"},{"type":"string"},{}]},"connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"issuer":{"type":"string","description":"Issuer URL the session was minted on."},"last_seen_at":{"type":"string","format":"date-time","description":"Last authentication or token refresh through this session. Each refresh slides `expires_at` 30 days from here."},"expires_at":{"type":"string","format":"date-time","description":"When the session stops being usable unless something refreshes it first."},"revoked_at":{"type":"string","format":"date-time"},"revoked_reason":{"anyOf":[{"type":"string","enum":["admin"]},{"type":"string","enum":["logout"]},{"type":"string","enum":["user"]}],"description":"`admin`: revoked through the Management API. `logout`: the user signed out. `user`: the user revoked it from another device."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"SessionMetadata","default":{}},"createdAt":{"type":"string","description":"Session creation date"},"updatedAt":{"type":"string","description":"Session updated date"}},"description":"Session"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/session/{session_id}/revoke":{"post":{"operationId":"session/revoke","summary":"Revoke a session","tags":["session"],"description":"Ends the session for good: the browser cookie behind it stops working, every refresh token issued through it is refused on its next use (`invalid_grant`), and the relying parties that took part in it are notified. Access and id tokens already issued keep working until they expire. The row stays, marked `revoked`, for the device history.","parameters":[{"schema":{"type":"string"},"in":"path","name":"session_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Session","content":{"application/json":{"schema":{"type":"object","required":["id","user","sid","status","last_seen_at","expires_at","account","createdAt"],"properties":{"id":{"type":"string","description":"Session ID"},"user":{"anyOf":[{"$ref":"#/components/schemas/User"},{"type":"string"},{}]},"sid":{"type":"string","description":"OIDC session id. Every id_token minted for this session carries it as the `sid` claim; a back-channel or front-channel logout names the session with it."},"status":{"anyOf":[{"type":"string","enum":["active"]},{"type":"string","enum":["revoked"]}],"description":"`active` or `revoked`. An active session whose `expires_at` is in the past has expired: nothing can refresh it any more, but the row stays for the history."},"ip":{"type":"string","description":"Client IP at the last authentication."},"user_agent":{"type":"string","description":"Raw `User-Agent` header at the last authentication."},"device_name":{"type":"string","description":"The name a device gave itself when it asked for a code (device authorization grant), e.g. the hostname `faable login` runs on. Absent for browser sessions: a browser does not expose one."},"client":{"anyOf":[{"$ref":"#/components/schemas/Client"},{"type":"string"},{}]},"connection":{"anyOf":[{"$ref":"#/components/schemas/Connection"},{"type":"string"},{}]},"issuer":{"type":"string","description":"Issuer URL the session was minted on."},"last_seen_at":{"type":"string","format":"date-time","description":"Last authentication or token refresh through this session. Each refresh slides `expires_at` 30 days from here."},"expires_at":{"type":"string","format":"date-time","description":"When the session stops being usable unless something refreshes it first."},"revoked_at":{"type":"string","format":"date-time"},"revoked_reason":{"anyOf":[{"type":"string","enum":["admin"]},{"type":"string","enum":["logout"]},{"type":"string","enum":["user"]}],"description":"`admin`: revoked through the Management API. `logout`: the user signed out. `user`: the user revoked it from another device."},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"SessionMetadata","default":{}},"createdAt":{"type":"string","description":"Session creation date"},"updatedAt":{"type":"string","description":"Session updated date"}},"description":"Session"}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"409":{"description":"`session_already_revoked` — The session was already revoked.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["session_already_revoked"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/customdomain/{customdomain_id}/retry":{"post":{"operationId":"customdomain/retry","summary":"Trigger a domain verification check now","tags":["customdomain"],"description":"Forces an immediate DNS re-check of a Custom Domain in any state. Rate-limited to one check per minute. A verified (ACTIVE) domain stays ACTIVE while it is re-checked; any other state moves to VERIFYING.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"customdomain_id","required":true}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/customdomain":{"get":{"operationId":"customdomain/list","summary":"List Customdomains","tags":["customdomain"],"description":"List Customdomains","parameters":[{"schema":{"type":"number","minimum":1,"maximum":200},"in":"query","name":"pageSize","required":false,"description":"Number of items per page (max 200)"},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false,"description":"Cursor for next page"},{"schema":{"type":"string"},"in":"query","name":"next","required":false,"description":"Cursor returned by the previous page"},{"schema":{"type":"string"},"in":"query","name":"query","required":false,"description":"Filter using a FaableQL query"},{"schema":{"type":"string","minLength":1,"maxLength":200},"in":"query","name":"q","required":false,"description":"Full-text search across: `domain`."}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["next","results"],"properties":{"next":{"anyOf":[{"type":"string"},{"type":"null"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/Customdomain"}}},"additionalProperties":false}}}},"400":{"description":"`invalid_query` — The `?query=` FaableQL expression could not be parsed.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`search_not_supported` — This resource does not support `?search=`.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_query","invalid_expand","search_not_supported","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"customdomain/create","summary":"Create Customdomain","tags":["customdomain"],"description":"Create Customdomain","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["domain"],"properties":{"domain":{"type":"string"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"Add Metadata"}},"description":"CustomdomainCreate","additionalProperties":false}}},"description":"CustomdomainCreate"},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Customdomain","content":{"application/json":{"schema":{"type":"object","required":["id","domain","token","status","attempts","account","createdAt"],"properties":{"id":{"type":"string","description":"Customdomain ID"},"domain":{"type":"string"},"token":{"type":"string"},"status":{"enum":["PENDING","VERIFYING","ACTIVE","FAILED","DEGRADED"]},"attempts":{"type":"number"},"nextCheck":{"type":"string","format":"date-time"},"lastCheck":{"type":"string","format":"date-time"},"errorLog":{"type":"string","description":"User feedback error from last check"},"verifiedAt":{"type":"string","format":"date-time"},"records":{"type":"array","items":{"type":"object","required":["type","host","expected","observed","ok"],"properties":{"type":{"type":"string"},"host":{"type":"string"},"expected":{"type":"string"},"observed":{"type":"array","items":{"type":"string"}},"ok":{"type":"boolean"}}},"description":"Per-record DNS status from the last check: what each record points to now vs. expected"},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"CustomdomainMetadata","default":{}},"createdAt":{"type":"string","description":"Customdomain creation date"},"updatedAt":{"type":"string","description":"Customdomain updated date"}},"description":"Customdomain"}}}},"400":{"description":"`already_exists` — A resource with the same unique field already exists (for a user, usually `email`). `details.fields` names the field.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["already_exists","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/customdomain/{customdomain_id}":{"get":{"operationId":"customdomain/get","summary":"Get Customdomain","tags":["customdomain"],"description":"Get Customdomain","parameters":[{"schema":{"type":"string"},"in":"path","name":"customdomain_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Customdomain","content":{"application/json":{"schema":{"type":"object","required":["id","domain","token","status","attempts","account","createdAt"],"properties":{"id":{"type":"string","description":"Customdomain ID"},"domain":{"type":"string"},"token":{"type":"string"},"status":{"enum":["PENDING","VERIFYING","ACTIVE","FAILED","DEGRADED"]},"attempts":{"type":"number"},"nextCheck":{"type":"string","format":"date-time"},"lastCheck":{"type":"string","format":"date-time"},"errorLog":{"type":"string","description":"User feedback error from last check"},"verifiedAt":{"type":"string","format":"date-time"},"records":{"type":"array","items":{"type":"object","required":["type","host","expected","observed","ok"],"properties":{"type":{"type":"string"},"host":{"type":"string"},"expected":{"type":"string"},"observed":{"type":"array","items":{"type":"string"}},"ok":{"type":"boolean"}}},"description":"Per-record DNS status from the last check: what each record points to now vs. expected"},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"CustomdomainMetadata","default":{}},"createdAt":{"type":"string","description":"Customdomain creation date"},"updatedAt":{"type":"string","description":"Customdomain updated date"}},"description":"Customdomain"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`invalid_expand` — An `?expand=` path is not allowed on this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","invalid_expand","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"delete":{"operationId":"customdomain/delete","summary":"Delete Customdomain","tags":["customdomain"],"description":"Delete Customdomain","parameters":[{"schema":{"type":"string"},"in":"path","name":"customdomain_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Customdomain","content":{"application/json":{"schema":{"type":"object","required":["id","domain","token","status","attempts","account","createdAt"],"properties":{"id":{"type":"string","description":"Customdomain ID"},"domain":{"type":"string"},"token":{"type":"string"},"status":{"enum":["PENDING","VERIFYING","ACTIVE","FAILED","DEGRADED"]},"attempts":{"type":"number"},"nextCheck":{"type":"string","format":"date-time"},"lastCheck":{"type":"string","format":"date-time"},"errorLog":{"type":"string","description":"User feedback error from last check"},"verifiedAt":{"type":"string","format":"date-time"},"records":{"type":"array","items":{"type":"object","required":["type","host","expected","observed","ok"],"properties":{"type":{"type":"string"},"host":{"type":"string"},"expected":{"type":"string"},"observed":{"type":"array","items":{"type":"string"}},"ok":{"type":"boolean"}}},"description":"Per-record DNS status from the last check: what each record points to now vs. expected"},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"CustomdomainMetadata","default":{}},"createdAt":{"type":"string","description":"Customdomain creation date"},"updatedAt":{"type":"string","description":"Customdomain updated date"}},"description":"Customdomain"}}}},"400":{"description":"`invalid_id` — The id in the path is not a valid id for this resource.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_id","validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`not_found` — The resource does not exist in this account.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/sdk-health/{account_id}":{"get":{"operationId":"account/sdk-health","summary":"Library health for this account","tags":["account"],"description":"Which Faable client libraries this account is being called with, at which versions, and how far behind the latest published release each one is. Versions are observed from the `x-faable-client` header, so an integration that talks raw OIDC shows up only in the traffic breakdown.","parameters":[{"schema":{"type":"string"},"in":"path","name":"account_id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["libraries","traffic","latest_checked_at","retention_days"],"properties":{"libraries":{"type":"array","items":{"type":"object","required":["name","package","latest","installs"],"properties":{"name":{"type":"string"},"package":{"type":"string"},"latest":{"anyOf":[{"type":"string"},{"type":"null"}]},"installs":{"type":"array","items":{"type":"object","required":["version","sha","app","lag","legacy_unversioned","first_seen","last_seen"],"properties":{"version":{"type":"string"},"sha":{"anyOf":[{"type":"string"},{"type":"null"}]},"app":{"anyOf":[{"type":"object","required":["id","client_id","name"],"properties":{"id":{"type":"string"},"client_id":{"type":"string"},"name":{"type":"string"}}},{"type":"null"}]},"lag":{"anyOf":[{"type":"string","enum":["current"]},{"type":"string","enum":["patch"]},{"type":"string","enum":["minor"]},{"type":"string","enum":["major"]},{"type":"string","enum":["unknown"]}]},"legacy_unversioned":{"type":"boolean"},"first_seen":{"type":"string"},"last_seen":{"type":"string"}}}}}}},"traffic":{"anyOf":[{"type":"object","required":["days","total","by_class"],"properties":{"days":{"type":"number"},"total":{"type":"number"},"by_class":{"type":"object","additionalProperties":{"type":"number"}}}},{"type":"null"}]},"latest_checked_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"retention_days":{"type":"number"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`unauthorized` — The request carries no valid credentials.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["unauthorized"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.\n\n`insufficient_scope` — The token lacks a scope this operation requires. `message` names it.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden","insufficient_scope","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/passwordless/verify_redirect":{"get":{"operationId":"passwordless/verify_redirect","summary":"Receive a passwordless token via email link and convert it to a valid session","tags":["passwordless"],"description":"Receive a passwordless token and convert it to a valid session","parameters":[{"schema":{"type":"string"},"in":"query","name":"token","required":true}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`expired_link` — The magic link token was not found or has expired.\n\n`invalid_link` — The magic link is malformed.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["expired_link","invalid_link","validation_error"]}}}]}}}},"401":{"description":"`login_denied` — A post-login Action denied the sign-in. `message` carries the reason it gave. On token grants it is a 403 with `error: invalid_grant`.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["login_denied"]}}}]}}}},"403":{"description":"`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/passwordless/verify_code":{"post":{"operationId":"passwordless/verifyCode","summary":"Verify a passwordless code and establish a session","tags":["passwordless"],"description":"Verifies an emailed passwordless code and establishes the browser `faable_sess` session, returning where to send the user next. Called by the first-party hosted login UI; SPAs on other origins use the `passwordless/otp` grant at `/oauth/token` instead, which returns tokens and no cookie.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["client_id","email","otp"],"properties":{"client_id":{"type":"string"},"email":{"type":"string"},"otp":{"type":"string"},"redirect_to":{"type":"string","description":"Same-origin path to return the user to after authentication, instead of the OAuth client redirect. Used by first-party interactive flows (e.g. device-code confirm). Non same-origin values are ignored."},"remember_me":{"type":"boolean","description":"The \"Remember me on this device\" checkbox. Honoured only when the tenant shows one (`login_methods.remember_me: \"optional\"`)."}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["redirect_url"],"properties":{"redirect_url":{"type":"string"}}}}}},"400":{"description":"`invalid_client` — The `client_id` in the request does not name a client of this account.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_client","validation_error"]}}}]}}}},"401":{"description":"`invalid_otp` — The one-time password is wrong or expired.\n\n`login_denied` — A post-login Action denied the sign-in. `message` carries the reason it gave. On token grants it is a 403 with `error: invalid_grant`.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_otp","login_denied"]}}}]}}}},"403":{"description":"`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/passwordless/start":{"post":{"operationId":"passwordless/start","summary":"Start Passwordless authentication flow","tags":["passwordless"],"description":"Start Passwordless authentication flow","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["client_id","email","send"],"properties":{"client_id":{"type":"string"},"email":{"type":"string"},"send":{"type":"string","default":"link"},"connection_id":{"type":"string"},"auth_params":{"type":"object","additionalProperties":{"type":"string"}}}}}}},"responses":{"200":{"description":"Default Response"},"400":{"description":"`ambiguous_connection` — Several connections match; pass `connection_id`.\n\n`invalid_client` — The `client_id` in the request does not name a client of this account.\n\n`invalid_connection` — The connection does not exist, is disabled, or is not of the type this flow needs.\n\n`invalid_email` — The email address is not valid.\n\n`passwordless_unavailable` — No passwordless connection is configured or enabled for this client.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["ambiguous_connection","invalid_client","invalid_connection","invalid_email","passwordless_unavailable","validation_error"]}}}]}}}},"403":{"description":"`origin_not_allowed` — The request `Origin` is not in the client's Allowed Web Origins.\n\n`signup_denied` — A pre-signup Action or an active block denied the sign-up. `message` carries the reason.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["origin_not_allowed","signup_denied"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/mfa/challenge/options":{"get":{"summary":"What the second-factor screen should offer","tags":["mfa"],"description":"For a login parked on the hosted challenge screen: which factors the user can answer with, in the order and with the default the login flow chose, and whether recovery codes are an option. Reads the state without consuming it.","parameters":[{"schema":{"type":"string"},"in":"query","name":"state","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["factors","recovery"],"properties":{"factors":{"type":"array","items":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}]},"description":"The ways this user can answer, in the order the screen should offer them. Only factors the user actually has and the policy accepts."},"preferred":{"anyOf":[{"type":"string","enum":["totp"]},{"type":"string","enum":["webauthn"]}],"description":"The one to mark as the default, when there are several."},"recovery":{"type":"boolean","description":"Whether to offer the recovery-code way in: the flow allows it and the user has codes."}},"additionalProperties":false}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`mfa_invalid_code` — The authenticator code did not verify.\n\n`mfa_invalid_recovery_code` — The recovery code did not verify.\n\n`totp_not_allowed` — This account does not accept authenticator apps.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["mfa_invalid_code","mfa_invalid_recovery_code","totp_not_allowed"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/mfa/verify":{"post":{"summary":"Answer an MFA challenge with a TOTP code","tags":["mfa"],"description":"Verifies a time-based code against the factors enrolled by the user the challenge belongs to, and resumes the parked login. The state is consumed only on success, so a mistyped code does not destroy the flow.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["state","code"],"properties":{"state":{"type":"string"},"code":{"type":"string"}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["redirect_url"],"properties":{"redirect_url":{"type":"string"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/mfa/recovery":{"post":{"summary":"Answer an MFA challenge with a recovery code","tags":["mfa"],"description":"Consumes one of the one-time recovery codes issued when the user enrolled their first factor.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["state","code"],"properties":{"state":{"type":"string"},"code":{"type":"string"}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["redirect_url"],"properties":{"redirect_url":{"type":"string"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/me/factors":{"get":{"summary":"List my second factors","tags":["mfa"],"description":"The security methods enrolled by the current session's user. Never returns secret material: TOTP seeds and recovery-code hashes are redacted by the store.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FactorSummary"}}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/me/factors/totp":{"post":{"summary":"Start enrolling an authenticator app","tags":["mfa"],"description":"Creates an UNCONFIRMED TOTP factor and returns its `otpauth://` URI and secret — the only time either is ever readable. The factor does not satisfy any policy until `POST /me/factors/:id/verify` proves the user can produce a code from it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":100},"state":{"type":"string"}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["id","secret","otpauth_uri"],"properties":{"id":{"type":"string"},"secret":{"type":"string"},"otpauth_uri":{"type":"string"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/me/factors/{factor_id}/verify":{"post":{"summary":"Confirm an enrolment","tags":["mfa"],"description":"Proves the user can produce a code from the seed, which is what makes the factor count. Confirming the first factor also mints the recovery codes — returned once and never again.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code"],"properties":{"code":{"type":"string"},"state":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"string"},"in":"path","name":"factor_id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["id","confirmed_at"],"properties":{"id":{"type":"string"},"confirmed_at":{"type":"string"},"recovery_codes":{"type":"array","items":{"type":"string"}},"redirect_url":{"type":"string"}}}}}},"400":{"description":"`factor_already_confirmed` — The factor was already confirmed.\n\n`factor_not_found` — No factor with that id belongs to this user.\n\n`invalid_code` — The verification code is wrong, expired or already used.\n\n`invalid_factor` — The factor cannot be verified (no secret material).\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["factor_already_confirmed","factor_not_found","invalid_code","invalid_factor","validation_error"]}}}]}}}},"401":{"description":"`state_mismatch` — The `state` belongs to another account, session, client or ceremony.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["state_mismatch"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/me/factors/{factor_id}":{"delete":{"summary":"Remove one of my second factors","tags":["mfa"],"parameters":[{"schema":{"type":"string"},"in":"path","name":"factor_id","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["deleted"],"properties":{"deleted":{"type":"boolean"}}}}}},"400":{"description":"`factor_not_found` — No factor with that id belongs to this user.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["factor_not_found","validation_error"]}}}]}}}},"401":{"description":"`mfa_pending` — A second-factor challenge is pending; answer it before continuing.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["mfa_pending"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/me/passkeys/options":{"post":{"summary":"Start registering a passkey","tags":["mfa"],"description":"Returns the WebAuthn creation options for `navigator.credentials.create()`. Existing credentials are excluded so the authenticator refuses to enrol the same key twice. Pass the `state` of a forced enrolment so the guard knows this is the way through, not a bypass.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"string"}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["ceremony_id","options"],"properties":{"ceremony_id":{"type":"string"},"options":{}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/me/passkeys":{"post":{"summary":"Finish registering a passkey","tags":["mfa"],"description":"Verifies the attestation and stores the credential as a confirmed second factor. Pass the `state` of a pending challenge to also resume the login it parked.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ceremony_id","credential"],"properties":{"ceremony_id":{"type":"string"},"credential":{},"name":{"type":"string","maxLength":100},"state":{"type":"string"}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"redirect_url":{"type":"string"}}}}}},"400":{"description":"`passkey_verification_failed` — The WebAuthn response could not be verified.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["passkey_verification_failed","validation_error"]}}}]}}}},"401":{"description":"`state_mismatch` — The `state` belongs to another account, session, client or ceremony.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["state_mismatch"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/mfa/webauthn/options":{"post":{"summary":"Start answering a challenge with a passkey","tags":["mfa"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["state"],"properties":{"state":{"type":"string"}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["ceremony_id","options"],"properties":{"ceremony_id":{"type":"string"},"options":{}}}}}},"400":{"description":"`no_passkey_enrolled` — The user has no passkey to sign in with.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["no_passkey_enrolled","validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/mfa/webauthn/verify":{"post":{"summary":"Answer a challenge with a passkey","tags":["mfa"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ceremony_id","credential"],"properties":{"ceremony_id":{"type":"string"},"credential":{},"name":{"type":"string","maxLength":100},"state":{"type":"string"}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["redirect_url"],"properties":{"redirect_url":{"type":"string"}}}}}},"400":{"description":"`invalid_state` — The `state` is missing, expired, or does not describe a resumable step.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_state","validation_error"]}}}]}}}},"401":{"description":"`state_mismatch` — The `state` belongs to another account, session, client or ceremony.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["state_mismatch"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/passkey/offer/skip":{"post":{"summary":"Decline the post-login passkey offer","tags":["mfa"],"description":"Finishes a login the passkey offer parked without registering anything. `dismissed` is the user saying \"not now\" (starts the snooze); `unsupported` is the screen finding the browser cannot do WebAuthn (the prompt is given back); `failed` is a registration that errored. Returns where the login was going.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["state","reason"],"properties":{"state":{"type":"string"},"reason":{"anyOf":[{"type":"string","enum":["dismissed"]},{"type":"string","enum":["unsupported"]},{"type":"string","enum":["failed"]}]}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["redirect_url"],"properties":{"redirect_url":{"type":"string"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`state_mismatch` — The `state` belongs to another account, session, client or ceremony.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["state_mismatch"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/passkey/login/options":{"post":{"summary":"Start a passwordless passkey login","tags":["mfa"],"description":"Returns authentication options with an EMPTY allowCredentials, so the browser offers whichever discoverable passkey it holds for this Relying Party — the usernameless flow behind conditional autofill.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["client_id"],"properties":{"client_id":{"type":"string"},"redirect_to":{"type":"string"}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["ceremony_id","options"],"properties":{"ceremony_id":{"type":"string"},"options":{}}}}}},"400":{"description":"`passkey_login_disabled` — Sign in with a passkey is not enabled for this client.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["passkey_login_disabled","validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/passkey/login/verify":{"post":{"summary":"Finish a passwordless passkey login","tags":["mfa"],"description":"Verifies the assertion, establishes the session and answers with where to send the browser. A passkey that verified the user (biometric or PIN) is already two factors, so no second-factor challenge follows.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ceremony_id","credential","client_id"],"properties":{"ceremony_id":{"type":"string"},"credential":{},"client_id":{"type":"string"},"redirect_to":{"type":"string"},"state":{"type":"string"},"remember_me":{"type":"boolean","description":"The \"Remember me on this device\" checkbox. Honoured only when the tenant shows one (`login_methods.remember_me: \"optional\"`)."}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["redirect_url"],"properties":{"redirect_url":{"type":"string"}}}}}},"400":{"description":"`invalid_state` — The `state` is missing, expired, or does not describe a resumable step.\n\n`malformed_credential` — The WebAuthn credential is malformed.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_state","malformed_credential","validation_error"]}}}]}}}},"401":{"description":"`invalid_passkey` — The passkey assertion did not verify.\n\n`state_mismatch` — The `state` belongs to another account, session, client or ceremony.\n\n`unknown_passkey` — The passkey is not enrolled for this user.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_passkey","state_mismatch","unknown_passkey"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/mfa/step-up":{"post":{"summary":"Start a step-up challenge for the current session","tags":["mfa"],"description":"Parks a challenge for the signed-in user and returns where to send the browser. Answering it seals the session at AAL2 and returns to `redirect_to`. Answers `already_satisfied` when the session is AAL2 already.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"redirect_to":{"type":"string","description":"Same-origin path to return to once the factor is proven. Off-origin values are dropped."}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["redirect_url","already_satisfied"],"properties":{"redirect_url":{"type":"string"},"already_satisfied":{"type":"boolean"}}}}}},"400":{"description":"`no_usable_factor` — The user has no confirmed second factor to challenge.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["no_usable_factor","validation_error"]}}}]}}}},"401":{"description":"`not_logged_in` — There is no signed-in session for this request.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_logged_in"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/mfa/recovery/options":{"get":{"summary":"What a recovery-by-factor screen should offer","tags":["mfa"],"parameters":[{"schema":{"type":"string"},"in":"query","name":"state","required":true}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["factors","recovery"],"properties":{"factors":{"type":"array","items":{"type":"string","enum":["totp"]},"description":"Factors the person can answer with here. Passkeys are not offered for recovery yet: their assertion flow is bound to the login session."},"preferred":{"type":"string","enum":["totp"]},"recovery":{"type":"boolean","description":"Whether they hold recovery codes."}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/mfa/recovery/verify":{"post":{"summary":"Answer a recovery-by-factor challenge with a TOTP code","tags":["mfa"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["state","code"],"properties":{"state":{"type":"string"},"code":{"type":"string"}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["redirect_url"],"properties":{"redirect_url":{"type":"string"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`mfa_invalid_code` — The authenticator code did not verify.\n\n`mfa_invalid_recovery_code` — The recovery code did not verify.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["mfa_invalid_code","mfa_invalid_recovery_code"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/mfa/recovery/code":{"post":{"summary":"Answer a recovery-by-factor challenge with a recovery code","tags":["mfa"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["state","code"],"properties":{"state":{"type":"string"},"code":{"type":"string"}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["redirect_url"],"properties":{"redirect_url":{"type":"string"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"401":{"description":"`mfa_invalid_code` — The authenticator code did not verify.\n\n`mfa_invalid_recovery_code` — The recovery code did not verify.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["mfa_invalid_code","mfa_invalid_recovery_code"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/me":{"get":{"operationId":"session/me","summary":"Get current User","tags":["session"],"description":"Returns the User behind the current session or bearer token. Requires a valid session cookie or `Authorization: Bearer <access_token>` header. With a Bearer, the custom claims of that access token (connection claims_mapping, actions setCustomClaim) are included as top-level properties.","responses":{"200":{"description":"The current User. With a Bearer access token the response also carries the token's custom claims (e.g. `ciapol.com/station_id`) as top-level properties; profile fields always win over a same-named claim.","content":{"application/json":{"schema":{"type":"object","required":["id","email_verified","phone_verified","logins_count","user_metadata","app_metadata","account","createdAt"],"properties":{"id":{"type":"string","description":"User ID"},"name":{"type":"string","description":"User name","nullable":true},"username":{"type":"string","description":"unique username for this user","nullable":true},"given_name":{"type":"string","description":"Given name","nullable":true},"family_name":{"type":"string","description":"Family name","nullable":true},"middle_name":{"type":"string","description":"Middle name (OIDC §5.1)","nullable":true},"nickname":{"type":"string","description":"Casual name. Distinct from given_name (e.g. \"Mike\" vs \"Michael\"). OIDC §5.1","nullable":true},"email":{"type":"string","description":"User email","nullable":true},"email_verified":{"type":"boolean","description":"true if email is verified","default":false},"email_verified_method":{"anyOf":[{"type":"string","enum":["manual"]},{"type":"string","enum":["verification_flow"]},{"type":"string","enum":["passwordless_otp"]},{"type":"string","enum":["team_invite"]},{"type":"string","enum":["email_change"]},{"type":"string","enum":["federated"]},{"type":"string","enum":["sms_otp"]},{"type":"string","enum":["password_reset"]},{"type":"string","enum":["import"]},{"type":"null"}],"description":"How `email_verified` was last set. `manual` — admin flipped the flag via `POST /user/:id`. `verification_flow` — user clicked the verification link. `passwordless_otp` — user completed passwordless OTP login. `team_invite` — user clicked a team invitation link. `email_change` — user confirmed a self-service email change. `federated` — verified by the external IdP on OAuth callback. `password_reset` — user completed the recovery link that was emailed to them (the `email` channel only). `import` — the user arrived verified from another provider through `POST /user/import` (asserted by whoever ran the migration). `null` — the field was cleared (admin un-verified the email)."},"email_verified_at":{"type":"string","description":"ISO 8601 timestamp of when `email_verified` was last flipped to true.","nullable":true},"email_change_locked_at":{"type":"string","description":"ISO 8601 timestamp of the user's last verified email change. When set, OAuth callbacks will not overwrite `email`/`email_verified` from the federated provider — the manually-chosen email wins.","nullable":true},"email_bounced_at":{"type":"string","description":"ISO 8601 timestamp of the last Postmark hard bounce / spam complaint for this address. When set, the email is treated as undeliverable and no further emails are sent to the user until the address changes.","nullable":true},"welcome_sent_at":{"type":"string","description":"ISO 8601 timestamp of the built-in welcome email send. Set once by the platform; guarantees at most one welcome per user.","nullable":true},"imported_at":{"type":"string","description":"ISO 8601 timestamp of when the user was created by `POST /user/import`. Imported users get none of the built-in new-user emails (verification, welcome, `user.created` notifications); `user.created` webhooks still fire and carry this field.","nullable":true},"passkey_prompted_at":{"type":"string","description":"ISO 8601 timestamp of the last time the hosted \"create a passkey\" offer was shown to this user after a login. Drives the snooze in `login_methods.passkey_promotion_snooze_days`.","nullable":true},"passkey_prompt_count":{"type":"integer","description":"How many times the passkey offer has been shown to this user. Capped by `login_methods.passkey_promotion_max_prompts`; reset by an administrator through `POST /user/:id/passkey-prompt/reset`."},"passkey_prompt_dismissed_at":{"type":"string","description":"ISO 8601 timestamp of the last time the user chose \"Not now\" on the passkey offer, as opposed to closing the tab.","nullable":true},"phone":{"type":"string","description":"contact phone number","nullable":true},"phone_e164":{"type":"boolean","description":"Whether `phone` is a valid E.164 number. Calculated on read, never stored: new writes are rejected unless they normalise, so a `false` here is a legacy row that no SMS can reach — fix the number, or set the account default country and write it again."},"phone_verified":{"type":"boolean","description":"phone is verified"},"phone_verified_method":{"anyOf":[{"type":"string","enum":["manual"]},{"type":"string","enum":["verification_flow"]},{"type":"string","enum":["passwordless_otp"]},{"type":"string","enum":["team_invite"]},{"type":"string","enum":["email_change"]},{"type":"string","enum":["federated"]},{"type":"string","enum":["sms_otp"]},{"type":"string","enum":["password_reset"]},{"type":"string","enum":["import"]},{"type":"null"}],"description":"How `phone_verified` was last set. Same enum as `email_verified_method`, plus `sms_otp` — the user typed a code sent by SMS (`POST /user/:id/verify-phone/start`)."},"phone_verified_at":{"type":"string","description":"ISO 8601 timestamp of when `phone_verified` was last flipped to true.","nullable":true},"country_iso":{"type":"string","description":"country iso code","nullable":true},"birth_date":{"type":"string","description":"user birth_date","nullable":true},"gender":{"type":"string","description":"User gender (free-form, OIDC §5.1)","nullable":true},"zoneinfo":{"type":"string","description":"IANA time-zone name (e.g. \"Europe/Madrid\"). OIDC §5.1 — used for the `zoneinfo` claim.","nullable":true},"locale":{"type":"string","description":"user main language","nullable":true},"region":{"type":"string","description":"customer region","nullable":true},"website":{"type":"string","description":"URL of the User's personal Web page or blog (OIDC §5.1)","nullable":true},"address":{"type":"object","properties":{"formatted":{"type":"string","description":"Full mailing address, formatted for display (may contain newlines)","nullable":true},"street_address":{"type":"string","nullable":true},"locality":{"type":"string","description":"City or locality","nullable":true},"region":{"type":"string","description":"State, province, or region","nullable":true},"postal_code":{"type":"string","nullable":true},"country":{"type":"string","description":"Country name","nullable":true}},"additionalProperties":false,"nullable":true},"picture":{"type":"string","description":"User picture url","nullable":true},"logins_count":{"type":"number","description":"Total number of logins this user has performed."},"last_ip":{"type":"string","description":"Last IP address from which this user logged in","nullable":true},"last_login":{"type":"string","description":"Last date and time this user logged in (ISO_8601 format)","nullable":true},"first_language":{"type":"string","description":"Primary Accept-Language subtag seen at this user's FIRST login (e.g. \"zh\"). Written once and never updated, so a later login whose language differs is visible as `event.stats.language_changed` in actions — a cross-session signal no single request can produce.","nullable":true},"suspended":{"type":"boolean","description":"true while the user is suspended. A suspended user is rejected at every login flow, token grant, session use and management-API call.","default":false},"suspended_at":{"type":"string","description":"ISO 8601 timestamp of when `suspended` was last flipped to true.","nullable":true},"suspended_reason":{"type":"string","description":"Free-form reason recorded when the user was suspended (e.g. \"abuse: RCE payload\").","nullable":true},"user_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"User Metadata","default":{}},"app_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"App Metadata","default":{}},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"UserMetadata","default":{}},"createdAt":{"type":"string","description":"User creation date"},"updatedAt":{"type":"string","description":"User updated date"}},"additionalProperties":true,"description":"The current User. With a Bearer access token the response also carries the token's custom claims (e.g. `ciapol.com/station_id`) as top-level properties; profile fields always win over a same-named claim."}}}},"401":{"description":"`mfa_pending` — A second-factor challenge is pending; answer it before continuing.\n\n`not_logged_in` — There is no signed-in session for this request.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["mfa_pending","not_logged_in"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"patch":{"operationId":"session/me_update","summary":"Update current User profile","tags":["session"],"description":"Updates the profile of the User behind the current session or bearer token. Only `name`, `company_name` and `country_iso` can be changed; `company_name` is merged into `user_metadata` without touching its other keys. A Bearer token must belong to this Account.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"company_name":{"type":"string","maxLength":100},"country_iso":{"type":"string","maxLength":3}},"additionalProperties":false,"description":"Profile fields the current User may edit on themselves. `company_name` is stored in `user_metadata`, merged with whatever else is there."}}},"description":"Profile fields the current User may edit on themselves. `company_name` is stored in `user_metadata`, merged with whatever else is there."},"responses":{"200":{"description":"The current User. With a Bearer access token the response also carries the token's custom claims (e.g. `ciapol.com/station_id`) as top-level properties; profile fields always win over a same-named claim.","content":{"application/json":{"schema":{"type":"object","required":["id","email_verified","phone_verified","logins_count","user_metadata","app_metadata","account","createdAt"],"properties":{"id":{"type":"string","description":"User ID"},"name":{"type":"string","description":"User name","nullable":true},"username":{"type":"string","description":"unique username for this user","nullable":true},"given_name":{"type":"string","description":"Given name","nullable":true},"family_name":{"type":"string","description":"Family name","nullable":true},"middle_name":{"type":"string","description":"Middle name (OIDC §5.1)","nullable":true},"nickname":{"type":"string","description":"Casual name. Distinct from given_name (e.g. \"Mike\" vs \"Michael\"). OIDC §5.1","nullable":true},"email":{"type":"string","description":"User email","nullable":true},"email_verified":{"type":"boolean","description":"true if email is verified","default":false},"email_verified_method":{"anyOf":[{"type":"string","enum":["manual"]},{"type":"string","enum":["verification_flow"]},{"type":"string","enum":["passwordless_otp"]},{"type":"string","enum":["team_invite"]},{"type":"string","enum":["email_change"]},{"type":"string","enum":["federated"]},{"type":"string","enum":["sms_otp"]},{"type":"string","enum":["password_reset"]},{"type":"string","enum":["import"]},{"type":"null"}],"description":"How `email_verified` was last set. `manual` — admin flipped the flag via `POST /user/:id`. `verification_flow` — user clicked the verification link. `passwordless_otp` — user completed passwordless OTP login. `team_invite` — user clicked a team invitation link. `email_change` — user confirmed a self-service email change. `federated` — verified by the external IdP on OAuth callback. `password_reset` — user completed the recovery link that was emailed to them (the `email` channel only). `import` — the user arrived verified from another provider through `POST /user/import` (asserted by whoever ran the migration). `null` — the field was cleared (admin un-verified the email)."},"email_verified_at":{"type":"string","description":"ISO 8601 timestamp of when `email_verified` was last flipped to true.","nullable":true},"email_change_locked_at":{"type":"string","description":"ISO 8601 timestamp of the user's last verified email change. When set, OAuth callbacks will not overwrite `email`/`email_verified` from the federated provider — the manually-chosen email wins.","nullable":true},"email_bounced_at":{"type":"string","description":"ISO 8601 timestamp of the last Postmark hard bounce / spam complaint for this address. When set, the email is treated as undeliverable and no further emails are sent to the user until the address changes.","nullable":true},"welcome_sent_at":{"type":"string","description":"ISO 8601 timestamp of the built-in welcome email send. Set once by the platform; guarantees at most one welcome per user.","nullable":true},"imported_at":{"type":"string","description":"ISO 8601 timestamp of when the user was created by `POST /user/import`. Imported users get none of the built-in new-user emails (verification, welcome, `user.created` notifications); `user.created` webhooks still fire and carry this field.","nullable":true},"passkey_prompted_at":{"type":"string","description":"ISO 8601 timestamp of the last time the hosted \"create a passkey\" offer was shown to this user after a login. Drives the snooze in `login_methods.passkey_promotion_snooze_days`.","nullable":true},"passkey_prompt_count":{"type":"integer","description":"How many times the passkey offer has been shown to this user. Capped by `login_methods.passkey_promotion_max_prompts`; reset by an administrator through `POST /user/:id/passkey-prompt/reset`."},"passkey_prompt_dismissed_at":{"type":"string","description":"ISO 8601 timestamp of the last time the user chose \"Not now\" on the passkey offer, as opposed to closing the tab.","nullable":true},"phone":{"type":"string","description":"contact phone number","nullable":true},"phone_e164":{"type":"boolean","description":"Whether `phone` is a valid E.164 number. Calculated on read, never stored: new writes are rejected unless they normalise, so a `false` here is a legacy row that no SMS can reach — fix the number, or set the account default country and write it again."},"phone_verified":{"type":"boolean","description":"phone is verified"},"phone_verified_method":{"anyOf":[{"type":"string","enum":["manual"]},{"type":"string","enum":["verification_flow"]},{"type":"string","enum":["passwordless_otp"]},{"type":"string","enum":["team_invite"]},{"type":"string","enum":["email_change"]},{"type":"string","enum":["federated"]},{"type":"string","enum":["sms_otp"]},{"type":"string","enum":["password_reset"]},{"type":"string","enum":["import"]},{"type":"null"}],"description":"How `phone_verified` was last set. Same enum as `email_verified_method`, plus `sms_otp` — the user typed a code sent by SMS (`POST /user/:id/verify-phone/start`)."},"phone_verified_at":{"type":"string","description":"ISO 8601 timestamp of when `phone_verified` was last flipped to true.","nullable":true},"country_iso":{"type":"string","description":"country iso code","nullable":true},"birth_date":{"type":"string","description":"user birth_date","nullable":true},"gender":{"type":"string","description":"User gender (free-form, OIDC §5.1)","nullable":true},"zoneinfo":{"type":"string","description":"IANA time-zone name (e.g. \"Europe/Madrid\"). OIDC §5.1 — used for the `zoneinfo` claim.","nullable":true},"locale":{"type":"string","description":"user main language","nullable":true},"region":{"type":"string","description":"customer region","nullable":true},"website":{"type":"string","description":"URL of the User's personal Web page or blog (OIDC §5.1)","nullable":true},"address":{"type":"object","properties":{"formatted":{"type":"string","description":"Full mailing address, formatted for display (may contain newlines)","nullable":true},"street_address":{"type":"string","nullable":true},"locality":{"type":"string","description":"City or locality","nullable":true},"region":{"type":"string","description":"State, province, or region","nullable":true},"postal_code":{"type":"string","nullable":true},"country":{"type":"string","description":"Country name","nullable":true}},"additionalProperties":false,"nullable":true},"picture":{"type":"string","description":"User picture url","nullable":true},"logins_count":{"type":"number","description":"Total number of logins this user has performed."},"last_ip":{"type":"string","description":"Last IP address from which this user logged in","nullable":true},"last_login":{"type":"string","description":"Last date and time this user logged in (ISO_8601 format)","nullable":true},"first_language":{"type":"string","description":"Primary Accept-Language subtag seen at this user's FIRST login (e.g. \"zh\"). Written once and never updated, so a later login whose language differs is visible as `event.stats.language_changed` in actions — a cross-session signal no single request can produce.","nullable":true},"suspended":{"type":"boolean","description":"true while the user is suspended. A suspended user is rejected at every login flow, token grant, session use and management-API call.","default":false},"suspended_at":{"type":"string","description":"ISO 8601 timestamp of when `suspended` was last flipped to true.","nullable":true},"suspended_reason":{"type":"string","description":"Free-form reason recorded when the user was suspended (e.g. \"abuse: RCE payload\").","nullable":true},"user_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"User Metadata","default":{}},"app_metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"App Metadata","default":{}},"account":{"type":"string","description":"Object is related with this account"},"metadata":{"type":"object","properties":{},"additionalProperties":true,"description":"UserMetadata","default":{}},"createdAt":{"type":"string","description":"User creation date"},"updatedAt":{"type":"string","description":"User updated date"}},"additionalProperties":true,"description":"The current User. With a Bearer access token the response also carries the token's custom claims (e.g. `ciapol.com/station_id`) as top-level properties; profile fields always win over a same-named claim."}}}},"400":{"description":"`bad_request` — The request is malformed. `message` says what is wrong.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["bad_request","validation_error"]}}}]}}}},"401":{"description":"`mfa_pending` — A second-factor challenge is pending; answer it before continuing.\n\n`not_logged_in` — There is no signed-in session for this request.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["mfa_pending","not_logged_in"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"delete":{"operationId":"session/me_delete","summary":"Delete current User","tags":["session"],"description":"Deletes the User behind the current session or bearer token, with the same effects as the management `DELETE /user/:id`: identities, credentials, second factors and team/role memberships go with it, pending tickets lose their reference, and `user.deleted` is emitted. A Bearer token must belong to this Account and be a user token (`sub` is a User); a client_credentials token is refused. When a session cookie was used it is cleared.","responses":{"204":{"description":"The User was deleted"},"401":{"description":"`mfa_pending` — A second-factor challenge is pending; answer it before continuing.\n\n`not_logged_in` — There is no signed-in session for this request.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["mfa_pending","not_logged_in"]}}}]}}}},"403":{"description":"`forbidden` — The credentials are valid but do not allow this operation.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["forbidden"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/me/sessions":{"get":{"operationId":"session/meSessions","summary":"List the current user's active sessions","tags":["session"],"description":"Every active session of the user behind the request (cookie or bearer), newest first; `current_session_id` names the one this request came through.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["results","current_session_id"],"properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/Session"}},"current_session_id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The id of the session this request came through: the cookie, or the session the bearer token was issued in. Null when the token predates session ids."}}}}}},"401":{"description":"`mfa_pending` — A second-factor challenge is pending; answer it before continuing.\n\n`not_logged_in` — There is no signed-in session for this request.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["mfa_pending","not_logged_in"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/me/sessions/revoke-others":{"post":{"operationId":"session/meRevokeOtherSessions","summary":"Sign the current user out everywhere else","tags":["session"],"description":"Revokes every active session of the user except the one this request came through. Refresh tokens issued in the revoked sessions are refused on their next use; access and id tokens already issued keep working until they expire.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["revoked"],"properties":{"revoked":{"type":"number","description":"How many sessions were ended."}}}}}},"401":{"description":"`mfa_pending` — A second-factor challenge is pending; answer it before continuing.\n\n`not_logged_in` — There is no signed-in session for this request.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["mfa_pending","not_logged_in"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/logout":{"get":{"operationId":"session/logout","summary":"OpenID Connect RP-Initiated Logout","tags":["session"],"description":"Destroys the current session. Implements OIDC RP-Initiated Logout 1.0 and Front-Channel Logout 1.0: when RPs with a registered `frontchannel_logout_uri` participated in the session, returns an HTML page with one iframe per RP and (when applicable) auto-redirects to `post_logout_redirect_uri`.","parameters":[{"schema":{"type":"string"},"in":"query","name":"id_token_hint","required":false,"description":"Previously issued id_token. RECOMMENDED per OIDC §3 — used to identify the client/session being logged out."},{"schema":{"type":"string"},"in":"query","name":"logout_hint","required":false,"description":"Hint about the End-User that is logging out (e.g. session id, email). Provider-specific."},{"schema":{"type":"string"},"in":"query","name":"client_id","required":false,"description":"OAuth Client identifier. Derived from `id_token_hint.aud` if omitted."},{"schema":{"type":"string"},"in":"query","name":"post_logout_redirect_uri","required":false,"description":"URL to redirect the User to after logout. MUST be pre-registered in the client `logout_urls` (exact match)."},{"schema":{"type":"string"},"in":"query","name":"state","required":false,"description":"Opaque value echoed back as `state` query param on the post-logout redirect, to mitigate CSRF."},{"schema":{"type":"string"},"in":"query","name":"ui_locales","required":false,"description":"Space-separated list of preferred UI languages."},{"schema":{"type":"string"},"in":"query","name":"returnTo","required":false,"description":"Deprecated alias of `post_logout_redirect_uri`. Kept for backwards compatibility."}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_redirect_uri` — The redirect URI is not registered for the client.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_redirect_uri","validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/logout/confirm":{"post":{"operationId":"session/logoutConfirm","summary":"Confirm a parked RP-Initiated Logout","tags":["session"],"description":"Finishes a `/logout` request that was parked pending confirmation (backlog §240) because it carried no verified `id_token_hint`. Single-use: the state cannot be replayed.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["state"],"properties":{"state":{"type":"string","description":"The `state` the confirmation screen was opened with."}}}}}},"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_state` — The `state` is missing, expired, or does not describe a resumable step.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_state","validation_error"]}}}]}}}},"401":{"description":"`state_mismatch` — The `state` belongs to another account, session, client or ceremony.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["state_mismatch"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/authorize":{"get":{"operationId":"authorize","summary":"Authorization request","tags":["oauth"],"description":"Authorization request","parameters":[{"schema":{"type":"string"},"in":"query","name":"response_type","required":false},{"schema":{"type":"string"},"in":"query","name":"code_challenge_method","required":false,"description":"Challenge method for PKCE. Only S256 is supported."},{"schema":{"type":"string"},"in":"query","name":"code_challenge","required":false,"description":"Challenge for PKCE authorization code flow"},{"schema":{"type":"string"},"in":"query","name":"redirect_to","required":false,"description":"Same-origin path to return the user to after authentication, instead of the OAuth client redirect. Used by first-party interactive flows (e.g. device-code confirm). Non same-origin values are ignored."},{"schema":{"type":"string"},"in":"query","name":"connection_scope","required":false,"description":"Space-separated extra scopes to request from the upstream identity provider (Auth0-compatible), added to the ones configured in the connection. The `scope` parameter describes the tokens Faable issues and is never forwarded to the provider."},{"schema":{"type":"boolean"},"in":"query","name":"link","required":false,"description":"Link mode: attach the resulting provider identity to the ACTIVE session user instead of signing in or up. Requires a session; incompatible with prompt=login/consent."},{"schema":{"type":"string"},"examples":{"connection_6475d947ea96eb57c2062c90":{"value":"connection_6475d947ea96eb57c2062c90"},"email,passkey":{"value":"email,passkey"}},"in":"query","name":"login_methods","required":false,"description":"Comma-separated methods the hosted login screen shows for this login: connection ids or connection names, and `passkey`. Each must be one the client already offers; the screen keeps the order configured for the client. Filters the screen only — it does not restrict which method the credential endpoints accept. Not combinable with `connection`, which skips the screen instead of narrowing it."},{"schema":{"type":"string"},"in":"query","name":"flow_preview","required":false,"description":"Preview token from `POST /loginflow/:id/preview-token`. This login runs the DRAFT of that flow, for this browser only; nothing is published. Invalid or expired tokens are ignored."},{"schema":{"type":"string"},"in":"query","name":"resume_state","required":false,"description":"Resume the login transaction the hosted screen has parked, instead of starting a new one. Used by the screen itself when the person picks a method: everything that belongs to the Relying Party — `code_challenge`, `state`, `redirect_uri`, `nonce`, `scope`, `audience` — is taken from the parked transaction and CANNOT be overridden by the query. Only `connection` is read from the caller."},{"schema":{"type":"string"},"in":"query","name":"link_ticket","required":false,"description":"Single-use ticket from `POST /oauth/link_ticket`. With `link=true`, establishes the cookie session for the ticket’s user before linking — for Relying Parties that hold tokens but no cookie session (direct grants such as passwordless OTP). Invalid or expired tickets are ignored."},{"schema":{"type":"string"},"in":"query","name":"client_id","required":true},{"schema":{"type":"string"},"in":"query","name":"connection","required":false,"description":"Deprecated. Use connection_id"},{"schema":{"type":"string"},"in":"query","name":"connection_id","required":false,"description":"Which oauth connection to use. Leave empty to use default connection"},{"schema":{"type":"string"},"in":"query","name":"scope","required":false},{"schema":{"type":"string"},"in":"query","name":"redirect_uri","required":false,"description":"The URL to which Faable will redirect the browser after authorization has been granted by the user."},{"schema":{"type":"string"},"in":"query","name":"state","required":false,"description":"An opaque value the applications adds to the initial request that the authorization server includes when redirecting the back to the application. This value must be used by the application to prevent CSRF attacks."},{"schema":{"type":"string"},"in":"query","name":"nonce","required":false,"description":"OIDC §3.1.2.1 — string value used to associate a Client session with an ID Token, to mitigate replay attacks. If passed at /authorize, the same value MUST appear in the issued id_token."},{"schema":{"type":"number","minimum":0},"in":"query","name":"max_age","required":false,"description":"OIDC §3.1.2.1 — maximum allowable elapsed time in seconds since the last End-User authentication. If exceeded, the OP re-authenticates and `auth_time` is REQUIRED in the resulting id_token."},{"schema":{"type":"string"},"in":"query","name":"prompt","required":false},{"schema":{"type":"string"},"in":"query","name":"login_hint","required":false,"description":"OIDC §3.1.2.1 — hint about the identifier (email, username) the End-User will sign in with. Pre-fills the hosted login screen and is forwarded to upstream identity providers."},{"schema":{"type":"string"},"in":"query","name":"id_token_hint","required":false,"description":"OIDC §3.1.2.1 — an id_token this provider issued earlier for the user the client believes is signed in. Verified (signature, not expiry). With `prompt=none`, a session for a different user answers `login_required`; otherwise it forces a fresh sign-in."},{"schema":{"type":"string"},"in":"query","name":"acr_values","required":false,"description":"OIDC §3.1.2.1 — space-separated Authentication Context Class References, in order of preference. `urn:faable:loa:2` asks for a second factor: a session that only has one is challenged (or, with `prompt=none`, answers `interaction_required`)."},{"schema":{"type":"string"},"in":"query","name":"ui_locales","required":false,"description":"OIDC §3.1.2.1 — preferred languages for the hosted screens, space-separated BCP47 tags. Forwarded to upstream identity providers."},{"schema":{"type":"string"},"in":"query","name":"audience","required":false,"description":"Auth0-compatible alias for `resource` (RFC 8707). When both are sent they must match. Identifier of the Api the client wants to access; persisted in the auth state so the token issued at /oauth/token targets the matching Api resource."},{"schema":{"type":"string"},"in":"query","name":"resource","required":false,"description":"RFC 8707 Resource Indicator. MUST be an absolute URI without fragment. When matched against a registered Api in the tenant the issued access_token gets `aud = <api.identifier>`."}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_client` — The `client_id` in the request does not name a client of this account.\n\n`invalid_connection` — The connection does not exist, is disabled, or is not of the type this flow needs.\n\n`invalid_redirect_uri` — The redirect URI is not registered for the client.\n\n`invalid_state` — The `state` is missing, expired, or does not describe a resumable step.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_client","invalid_connection","invalid_redirect_uri","invalid_state","validation_error"]}}}]}}}},"401":{"description":"`state_mismatch` — The `state` belongs to another account, session, client or ceremony.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["state_mismatch"]}}}]}}}},"403":{"description":"`origin_not_allowed` — The request `Origin` is not in the client's Allowed Web Origins.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["origin_not_allowed"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`login_flow_misconfigured` — The login flow of this account cannot run. `message` carries the node error.\n\n`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["login_flow_misconfigured","internal_error"]}}}]}}}}}}},"/select-account":{"post":{"operationId":"oauth/selectAccount","summary":"Answer the account chooser of a prompt=select_account login","tags":["oauth"],"description":"Resumes an authorization request that `/authorize` parked on the hosted account chooser. Requires the session cookie of the user the chooser was shown to. Returns where the browser goes next: the client callback with a code, or a login screen.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["state","choice"],"properties":{"state":{"type":"string","description":"The `state` the select-account screen was opened with."},"choice":{"anyOf":[{"type":"string","enum":["continue"]},{"type":"string","enum":["other"]}],"description":"`continue`: go on as the signed-in user. `other`: sign this browser out and start a fresh login for the same request."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["redirect_url"],"properties":{"redirect_url":{"type":"string"}}}}}},"400":{"description":"`invalid_state` — The `state` is missing, expired, or does not describe a resumable step.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_state","validation_error"]}}}]}}}},"401":{"description":"`not_logged_in` — There is no signed-in session for this request.\n\n`state_mismatch` — The `state` belongs to another account, session, client or ceremony.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_logged_in","state_mismatch"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/oauth/link_ticket":{"post":{"operationId":"oauth/link_ticket","summary":"Mint a link ticket","tags":["oauth"],"description":"Trades the Bearer access token of a signed-in user for a single-use, short-lived ticket that lets `/authorize?link=true` establish the cookie session it links the new identity to. For Relying Parties that authenticated the user with a direct grant (e.g. passwordless OTP) and therefore hold no cookie session. Subject to the client’s `web_origins` allowlist like `/oauth/token`.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["link_ticket","expires_in"],"properties":{"link_ticket":{"type":"string","description":"Opaque single-use ticket. Pass it as `link_ticket` on `/authorize?link=true` within `expires_in` seconds."},"expires_in":{"type":"number","description":"Ticket lifetime in seconds."}}}}}},"401":{"description":"`invalid_token` — The bearer token is missing, malformed, expired or has no subject.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_token"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/oauth/session-from-ticket":{"get":{"operationId":"oauth/sessionFromTicket","summary":"Open the hosted session from a ticket","tags":["oauth"],"description":"Consumes a ticket from `POST /oauth/link_ticket`, establishes the cookie session for its user and redirects to `redirect_to`, a hosted screen of this host. For Relying Parties that signed the user in with a direct grant (e.g. passwordless OTP) and so hold no cookie session, when they send the user to an account screen such as `/flow/account/security`.","parameters":[{"schema":{"type":"string"},"in":"query","name":"ticket","required":true,"description":"A ticket from `POST /oauth/link_ticket`, single use."},{"schema":{"type":"string"},"in":"query","name":"redirect_to","required":true,"description":"Where to go once the session is open: a path of this host under `/flow/` (e.g. `/flow/account/security?client_id=…`)."}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_request` — A required OAuth parameter is missing or two of them are incompatible. `message` says which.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_request","validation_error"]}}}]}}}},"403":{"description":"`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/oauth/token":{"get":{"operationId":"oauth/tokenGet","summary":"OAuth2 Token endpoint (GET)","tags":["oauth"],"description":"OAuth2 Token endpoint (RFC 6749 §3.2). Exchanges credentials for tokens depending on `grant_type` (authorization_code, client_credentials, refresh_token, password, otp, token_exchange, device_code). The GET variant accepts the same parameters as querystring.","parameters":[{"schema":{"anyOf":[{"type":"string","enum":["authorization_code"]},{"type":"string","enum":["client_credentials"]},{"type":"string","enum":["refresh_token"]},{"type":"string","enum":["password"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-otp"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-recovery-code"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/passwordless/otp"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:token-exchange"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:device_code"]}]},"in":"query","name":"grant_type","required":false,"description":"Grant Type. https://oauth.net/2/grant-types/"},{"schema":{"type":"string"},"in":"query","name":"client_id","required":false},{"schema":{"type":"string"},"in":"query","name":"client_secret","required":false},{"schema":{"type":"string"},"in":"query","name":"scope","required":false},{"schema":{"type":"string"},"in":"query","name":"code","required":false},{"schema":{"type":"string"},"in":"query","name":"code_verifier","required":false},{"schema":{"type":"string"},"in":"query","name":"username","required":false},{"schema":{"type":"string"},"in":"query","name":"password","required":false},{"schema":{"type":"string"},"in":"query","name":"realm","required":false,"description":"For `grant_type=password`: the name of the database connection to sign in against. Defaults to the first database connection offered to the client."},{"schema":{"type":"string"},"in":"query","name":"otp","required":false},{"schema":{"type":"string"},"in":"query","name":"refresh_token","required":false},{"schema":{"type":"string"},"in":"query","name":"mfa_token","required":false,"description":"Opaque token returned with a `403 mfa_required` response. Present it here with `grant_type=http://auth0.com/oauth/grant-type/mfa-otp` (or `…/mfa-recovery-code`) and the code, to finish a grant a second-factor policy interrupted."},{"schema":{"type":"string"},"in":"query","name":"recovery_code","required":false,"description":"One of the user's one-time recovery codes, for `grant_type=http://auth0.com/oauth/grant-type/mfa-recovery-code`."},{"schema":{"type":"string"},"in":"query","name":"subject_token","required":false},{"schema":{"type":"string"},"in":"query","name":"subject_token_type","required":false},{"schema":{"type":"string"},"in":"query","name":"device_code","required":false,"description":"The device code returned by the authorization server."},{"schema":{"type":"string"},"in":"query","name":"audience","required":false,"description":"Auth0-compatible alias for `resource` (RFC 8707). When both are provided they must match."},{"schema":{"type":"string"},"in":"query","name":"resource","required":false,"description":"RFC 8707 Resource Indicator. Absolute URI of the target Api, no fragment. Matched against `Api.identifier` in the tenant."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["token_type","expires_in","access_token"],"properties":{"token_type":{"type":"string"},"expires_in":{"type":"number"},"access_token":{"type":"string"},"refresh_token":{"type":"string"},"id_token":{"type":"string"},"scope":{"type":"string"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"oauth/token","summary":"OAuth2 Token endpoint","tags":["oauth"],"description":"OAuth2 Token endpoint (RFC 6749 §3.2). Exchanges credentials for tokens depending on `grant_type` (authorization_code, client_credentials, refresh_token, password, otp, token_exchange, device_code).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"grant_type":{"anyOf":[{"type":"string","enum":["authorization_code"]},{"type":"string","enum":["client_credentials"]},{"type":"string","enum":["refresh_token"]},{"type":"string","enum":["password"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-otp"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-recovery-code"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/passwordless/otp"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:token-exchange"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:device_code"]}],"description":"Grant Type. https://oauth.net/2/grant-types/"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"scope":{"type":"string"},"code":{"type":"string"},"code_verifier":{"type":"string"},"username":{"type":"string"},"password":{"type":"string"},"realm":{"type":"string","description":"For `grant_type=password`: the name of the database connection to sign in against. Defaults to the first database connection offered to the client."},"otp":{"type":"string"},"refresh_token":{"type":"string"},"mfa_token":{"type":"string","description":"Opaque token returned with a `403 mfa_required` response. Present it here with `grant_type=http://auth0.com/oauth/grant-type/mfa-otp` (or `…/mfa-recovery-code`) and the code, to finish a grant a second-factor policy interrupted."},"recovery_code":{"type":"string","description":"One of the user's one-time recovery codes, for `grant_type=http://auth0.com/oauth/grant-type/mfa-recovery-code`."},"subject_token":{"type":"string"},"subject_token_type":{"type":"string"},"device_code":{"type":"string","description":"The device code returned by the authorization server."},"audience":{"type":"string","description":"Auth0-compatible alias for `resource` (RFC 8707). When both are provided they must match."},"resource":{"type":"string","description":"RFC 8707 Resource Indicator. Absolute URI of the target Api, no fragment. Matched against `Api.identifier` in the tenant."}},"additionalProperties":true}}}},"parameters":[{"schema":{"anyOf":[{"type":"string","enum":["authorization_code"]},{"type":"string","enum":["client_credentials"]},{"type":"string","enum":["refresh_token"]},{"type":"string","enum":["password"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-otp"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/mfa-recovery-code"]},{"type":"string","enum":["http://auth0.com/oauth/grant-type/passwordless/otp"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:token-exchange"]},{"type":"string","enum":["urn:ietf:params:oauth:grant-type:device_code"]}]},"in":"query","name":"grant_type","required":false,"description":"Grant Type. https://oauth.net/2/grant-types/"},{"schema":{"type":"string"},"in":"query","name":"client_id","required":false},{"schema":{"type":"string"},"in":"query","name":"client_secret","required":false},{"schema":{"type":"string"},"in":"query","name":"scope","required":false},{"schema":{"type":"string"},"in":"query","name":"code","required":false},{"schema":{"type":"string"},"in":"query","name":"code_verifier","required":false},{"schema":{"type":"string"},"in":"query","name":"username","required":false},{"schema":{"type":"string"},"in":"query","name":"password","required":false},{"schema":{"type":"string"},"in":"query","name":"realm","required":false,"description":"For `grant_type=password`: the name of the database connection to sign in against. Defaults to the first database connection offered to the client."},{"schema":{"type":"string"},"in":"query","name":"otp","required":false},{"schema":{"type":"string"},"in":"query","name":"refresh_token","required":false},{"schema":{"type":"string"},"in":"query","name":"mfa_token","required":false,"description":"Opaque token returned with a `403 mfa_required` response. Present it here with `grant_type=http://auth0.com/oauth/grant-type/mfa-otp` (or `…/mfa-recovery-code`) and the code, to finish a grant a second-factor policy interrupted."},{"schema":{"type":"string"},"in":"query","name":"recovery_code","required":false,"description":"One of the user's one-time recovery codes, for `grant_type=http://auth0.com/oauth/grant-type/mfa-recovery-code`."},{"schema":{"type":"string"},"in":"query","name":"subject_token","required":false},{"schema":{"type":"string"},"in":"query","name":"subject_token_type","required":false},{"schema":{"type":"string"},"in":"query","name":"device_code","required":false,"description":"The device code returned by the authorization server."},{"schema":{"type":"string"},"in":"query","name":"audience","required":false,"description":"Auth0-compatible alias for `resource` (RFC 8707). When both are provided they must match."},{"schema":{"type":"string"},"in":"query","name":"resource","required":false,"description":"RFC 8707 Resource Indicator. Absolute URI of the target Api, no fragment. Matched against `Api.identifier` in the tenant."}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["token_type","expires_in","access_token"],"properties":{"token_type":{"type":"string"},"expires_in":{"type":"number"},"access_token":{"type":"string"},"refresh_token":{"type":"string"},"id_token":{"type":"string"},"scope":{"type":"string"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/oauth/device/code":{"post":{"operationId":"oauth/deviceCode","summary":"Device authorization request","tags":["oauth"],"description":"OAuth2 Device Authorization Grant (RFC 8628) — start step. A device that cannot run a browser calls this endpoint to obtain a `device_code`, a `user_code` to show to the user, and the verification URLs to display.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["client_id"],"properties":{"client_id":{"type":"string","description":"The client ID of the application."},"scope":{"type":"string","description":"The scope of the access request."},"audience":{"type":"string","description":"Identifier of the API the device wants to access. Resolved against the tenant Api resources (`Api.identifier`) at token issuance."},"device_name":{"type":"string","description":"Human-friendly name of the requesting device (e.g. \"marcs-mbp (macOS)\"). Shown on the confirm page so the user can recognise the device being authorised.","maxLength":200}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["device_code","user_code","verification_uri","verification_uri_complete","expires_in","interval"],"properties":{"device_code":{"type":"string"},"user_code":{"type":"string"},"verification_uri":{"type":"string"},"verification_uri_complete":{"type":"string"},"expires_in":{"type":"number"},"interval":{"type":"number"}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"404":{"description":"`client_not_found` — No client with that `client_id`.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["client_not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/activate":{"get":{"operationId":"oauth/deviceActivate","summary":"Device activation redirect","tags":["oauth"],"description":"Entry URL displayed on the device. Redirects the browser to the external `/flow/device-code/activate` UI where the user enters their `user_code`.","parameters":[{"schema":{"type":"string"},"in":"query","name":"user_code","required":false}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_device_code` — The device or user code is invalid or expired.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_device_code","validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/confirm":{"get":{"operationId":"oauth/deviceConfirm","summary":"Device confirmation redirect","tags":["oauth"],"description":"Redirects to the external `/flow/device-code/confirm` UI after the user enters their `user_code`. The UI then calls `/oauth/device/authorize` to grant the device.","parameters":[{"schema":{"type":"string"},"in":"query","name":"user_code","required":false}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/oauth/device/verify":{"post":{"operationId":"oauth/deviceVerify","summary":"Verify a device user_code","tags":["oauth"],"description":"Called by the external UI after the user enters their `user_code`. Returns the matching `device_code` and minimal client info so the UI can render \"Authorize <App>?\".","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["user_code"],"properties":{"user_code":{"type":"string"}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["device_code","client"],"properties":{"device_code":{"type":"string"},"device_name":{"type":"string"},"client":{"type":"object","properties":{"name":{"type":"string"},"client_id":{"type":"string"}}}}}}}},"400":{"description":"`invalid_device_code` — The device or user code is invalid or expired.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_device_code","validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/oauth/device/authorize":{"post":{"operationId":"oauth/deviceAuthorize","summary":"Authorize a pending device request","tags":["oauth"],"description":"Authorizes a `device_code` on behalf of the currently authenticated user, allowing the device to subsequently exchange that code for tokens at `/oauth/token`. Called by the first-party device-code confirm page, so it authenticates with the browser `faable_sess` session — NOT a bearer token (the page has no access token to send).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["device_code"],"properties":{"device_code":{"type":"string"}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean"}}}}}},"400":{"description":"`invalid_device_code` — The device or user code is invalid or expired.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_device_code","validation_error"]}}}]}}}},"401":{"description":"`not_logged_in` — There is no signed-in session for this request.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["not_logged_in"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/callback":{"get":{"operationId":"callback","summary":"OAuth2 Callback URL","tags":["oauth"],"description":"OAuth2 Callback URL","parameters":[{"schema":{"type":"string"},"in":"query","name":"code","required":false},{"schema":{"type":"string"},"in":"query","name":"state","required":false},{"schema":{"type":"string"},"in":"query","name":"error","required":false},{"schema":{"type":"string"},"in":"query","name":"error_uri","required":false},{"schema":{"type":"string"},"in":"query","name":"error_description","required":false}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`connection_required` — No connection was given and the client has no `default_connection`.\n\n`identity_orphaned` — The identity pointed at a user that no longer exists; it was removed. Sign in again.\n\n`invalid_request` — A required OAuth parameter is missing or two of them are incompatible. `message` says which.\n\n`invalid_state` — The `state` is missing, expired, or does not describe a resumable step.\n\n`provider_error` — The upstream identity provider returned an error. The suffix is the provider’s own code, when it sent one: `provider_error:bad_refresh_token` (the user must re-authorize), `provider_error:incorrect_client_credentials` (our configuration, the user can do nothing). `details.provider_error_description` carries the provider’s sentence.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["connection_required","identity_orphaned","invalid_request","invalid_state","provider_error","validation_error"]}}}]}}}},"401":{"description":"`login_denied` — A post-login Action denied the sign-in. `message` carries the reason it gave. On token grants it is a 403 with `error: invalid_grant`.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["login_denied"]}}}]}}}},"403":{"description":"`mfa_required` — The login needs a second factor that this flow cannot collect, or one is still pending on the session.\n\n`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["mfa_required","user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"409":{"description":"`identity_already_linked` — That external identity is already linked to another user.\n\n`identity_conflict` — The user already has a linked identity for this connection.\n\n`identity_link_denied` — That external identity cannot be linked to this user.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["identity_already_linked","identity_conflict","identity_link_denied"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/continue":{"get":{"operationId":"oauth_continue","summary":"OAuth2 Resume URL","tags":["oauth"],"description":"Resume OAuth flow after a progressive profiling action","parameters":[{"schema":{"type":"string","minLength":1},"in":"query","name":"state","required":true}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_state` — The `state` is missing, expired, or does not describe a resumable step.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_state","validation_error"]}}}]}}}},"401":{"description":"`action_unavailable` — The Action that paused this login is disabled or belongs elsewhere.\n\n`login_denied` — A post-login Action denied the sign-in. `message` carries the reason it gave. On token grants it is a 403 with `error: invalid_grant`.\n\n`state_mismatch` — The `state` belongs to another account, session, client or ceremony.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["action_unavailable","login_denied","state_mismatch"]}}}]}}}},"403":{"description":"`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/.well-known/openid-configuration":{"get":{"operationId":"oauth/openidConfiguration","summary":"OpenID Connect Discovery document","tags":["oauth"],"description":"Returns the OpenID Provider Configuration document for this Account (issuer, supported endpoints, response types, signing algorithms, claims, etc.) as defined by OIDC Discovery 1.0.","responses":{"200":{"description":"Default Response"},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/.well-known/jwks.json":{"get":{"operationId":"oauth/jwks","summary":"JSON Web Key Set","tags":["oauth"],"description":"Returns the public JWKs (RFC 7517) used to verify tokens issued by this Account. Consumers MUST use the `kid` from a token header to select the right key.","responses":{"200":{"description":"Default Response"},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/userinfo":{"get":{"operationId":"oauth/userinfo","summary":"OpenID Connect UserInfo endpoint","tags":["oauth"],"description":"Returns claims about the authenticated User. Authentication is via a Bearer access token (RFC 6750). Returned claims are filtered by the `scope` claim of the access token (OIDC §5.3.2). The response always contains `sub`. See https://openid.net/specs/openid-connect-core-1_0.html#UserInfo.","responses":{"200":{"description":"OpenID Connect UserInfo response (https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse).","content":{"application/json":{"schema":{"type":"object","required":["sub"],"properties":{"sub":{"type":"string","description":"Subject identifier (user id)"},"name":{"type":"string"},"family_name":{"type":"string"},"given_name":{"type":"string"},"nickname":{"type":"string"},"preferred_username":{"type":"string"},"picture":{"type":"string"},"birthdate":{"type":"string"},"locale":{"type":"string"},"updated_at":{"type":"number"},"email":{"type":"string"},"email_verified":{"type":"boolean"},"phone_number":{"type":"string"},"phone_number_verified":{"type":"boolean"}},"additionalProperties":true,"description":"OpenID Connect UserInfo response (https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse)."}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}},"post":{"operationId":"oauth/userinfoPost","summary":"OpenID Connect UserInfo endpoint (POST)","tags":["oauth"],"description":"Same as `GET /userinfo`. Provided for clients that prefer to send the Bearer token in a POST body or are required to by spec compliance audits.","responses":{"200":{"description":"OpenID Connect UserInfo response (https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse).","content":{"application/json":{"schema":{"type":"object","required":["sub"],"properties":{"sub":{"type":"string","description":"Subject identifier (user id)"},"name":{"type":"string"},"family_name":{"type":"string"},"given_name":{"type":"string"},"nickname":{"type":"string"},"preferred_username":{"type":"string"},"picture":{"type":"string"},"birthdate":{"type":"string"},"locale":{"type":"string"},"updated_at":{"type":"number"},"email":{"type":"string"},"email_verified":{"type":"boolean"},"phone_number":{"type":"string"},"phone_number_verified":{"type":"boolean"}},"additionalProperties":true,"description":"OpenID Connect UserInfo response (https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse)."}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/oidc/register":{"post":{"operationId":"oauth/register","summary":"OpenID Connect Dynamic Client Registration","tags":["oauth"],"description":"Register a new OAuth/OIDC client dynamically (OIDC Dynamic Client Registration 1.0 / RFC 7591). The new client is bound to the Account resolved from the request context.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["redirect_uris"],"properties":{"redirect_uris":{"type":"array","items":{"type":"string","format":"uri"},"description":"Array of redirection URI values used by the client (RFC 7591 §2). REQUIRED for any client that uses an authorization grant.","minItems":1},"client_name":{"type":"string","description":"Human-readable name of the client (RFC 7591 §2)"},"client_uri":{"type":"string","description":"URL of the home page of the client"},"logo_uri":{"type":"string"},"tos_uri":{"type":"string"},"policy_uri":{"type":"string"},"contacts":{"type":"array","items":{"type":"string"},"description":"Array of email addresses responsible for the client"},"grant_types":{"type":"array","items":{"type":"string"},"description":"Grant types the client will use. Defaults to [\"authorization_code\"]. Must be a subset of what the server supports."},"response_types":{"type":"array","items":{"type":"string"},"description":"Response types the client will use. Defaults to [\"code\"]."},"token_endpoint_auth_method":{"type":"string","description":"How the client authenticates at the token endpoint. Defaults to \"client_secret_basic\"."},"application_type":{"anyOf":[{"type":"string","enum":["web"]},{"type":"string","enum":["native"]}],"description":"OIDC §2 — application type. Defaults to \"web\"."},"post_logout_redirect_uris":{"type":"array","items":{"type":"string"},"description":"OIDC RP-Initiated Logout — URIs the OP MAY redirect to after end-session."},"scope":{"type":"string","description":"Space-separated list of scope values the client may use."},"software_id":{"type":"string"},"software_version":{"type":"string"},"frontchannel_logout_uri":{"type":"string","description":"OIDC Front-Channel Logout 1.0 — URL the OP loads inside an iframe at end-session to let the RP clear its session."},"frontchannel_logout_session_required":{"type":"boolean","description":"OIDC Front-Channel Logout 1.0 — when true, the OP MUST include `iss` and `sid` parameters when calling the frontchannel_logout_uri."},"backchannel_logout_uri":{"type":"string","description":"OIDC Back-Channel Logout 1.0 — URL the OP POSTs a signed `logout_token` to when a session this client took part in ends."},"backchannel_logout_session_required":{"type":"boolean","description":"OIDC Back-Channel Logout 1.0 — when true, the `logout_token` MUST include a `sid` claim."},"web_origins":{"type":"array","items":{"type":"string"},"description":"Non-standard (Auth0-compatible) — origins (scheme://host[:port], no path) allowed to make cross-origin (CORS) requests to the token and passwordless endpoints. Empty allows any origin."}},"additionalProperties":true}}}},"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["client_id","client_secret","client_id_issued_at","client_secret_expires_at","redirect_uris","grant_types","response_types","token_endpoint_auth_method","application_type"],"properties":{"client_id":{"type":"string"},"client_secret":{"type":"string"},"client_id_issued_at":{"type":"number","description":"Unix timestamp (seconds) at which the client_id was issued"},"client_secret_expires_at":{"type":"number","description":"0 if the client_secret does not expire"},"redirect_uris":{"type":"array","items":{"type":"string"}},"client_name":{"type":"string"},"client_uri":{"type":"string"},"logo_uri":{"type":"string"},"tos_uri":{"type":"string"},"policy_uri":{"type":"string"},"contacts":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"type":"string"}},"response_types":{"type":"array","items":{"type":"string"}},"token_endpoint_auth_method":{"type":"string"},"application_type":{"type":"string"},"post_logout_redirect_uris":{"type":"array","items":{"type":"string"}},"scope":{"type":"string"},"software_id":{"type":"string"},"software_version":{"type":"string"},"frontchannel_logout_uri":{"type":"string"},"frontchannel_logout_session_required":{"type":"boolean"},"backchannel_logout_uri":{"type":"string"},"backchannel_logout_session_required":{"type":"boolean"},"web_origins":{"type":"array","items":{"type":"string"}}},"additionalProperties":true}}}},"400":{"description":"`invalid_client_metadata` — Dynamic client registration rejected a field (RFC 7591). `message` names it.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_client_metadata","validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/usernamepassword/login":{"post":{"operationId":"usernamepassword/login","summary":"Username + password login","tags":["usernamepassword"],"description":"Authenticates a user with username/password credentials against the database connection and renders an auto-submitting form that posts the resulting token to `/login/callback`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["username","password","client_id"],"properties":{"username":{"type":"string"},"password":{"type":"string"},"state":{"type":"string","description":"An opaque value the applications adds to the initial request that the authorization server includes when redirecting the back to the application. This value must be used by the application to prevent CSRF attacks."},"response_type":{"type":"string"},"redirect_to":{"type":"string","description":"Same-origin path to return the user to after authentication, instead of the OAuth client redirect. Used by first-party interactive flows (e.g. device-code confirm). Non same-origin values are ignored."},"remember_me":{"type":"boolean","description":"The \"Remember me on this device\" checkbox. Honoured only when the tenant shows one (`login_methods.remember_me: \"optional\"`): `true` keeps the session for `remember_me_days`, `false` ends it when the browser closes."},"client_id":{"type":"string"},"connection":{"type":"string","description":"Deprecated. Use connection_id"},"connection_id":{"type":"string","description":"Which oauth connection to use. Leave empty to use default connection"},"scope":{"type":"string"},"redirect_uri":{"type":"string","description":"The URL to which Faable will redirect the browser after authorization has been granted by the user."},"nonce":{"type":"string","description":"OIDC §3.1.2.1 — string value used to associate a Client session with an ID Token, to mitigate replay attacks. If passed at /authorize, the same value MUST appear in the issued id_token."},"max_age":{"type":"number","description":"OIDC §3.1.2.1 — maximum allowable elapsed time in seconds since the last End-User authentication. If exceeded, the OP re-authenticates and `auth_time` is REQUIRED in the resulting id_token.","minimum":0},"prompt":{"type":"string"},"login_hint":{"type":"string","description":"OIDC §3.1.2.1 — hint about the identifier (email, username) the End-User will sign in with. Pre-fills the hosted login screen and is forwarded to upstream identity providers."},"id_token_hint":{"type":"string","description":"OIDC §3.1.2.1 — an id_token this provider issued earlier for the user the client believes is signed in. Verified (signature, not expiry). With `prompt=none`, a session for a different user answers `login_required`; otherwise it forces a fresh sign-in."},"acr_values":{"type":"string","description":"OIDC §3.1.2.1 — space-separated Authentication Context Class References, in order of preference. `urn:faable:loa:2` asks for a second factor: a session that only has one is challenged (or, with `prompt=none`, answers `interaction_required`)."},"ui_locales":{"type":"string","description":"OIDC §3.1.2.1 — preferred languages for the hosted screens, space-separated BCP47 tags. Forwarded to upstream identity providers."},"audience":{"type":"string","description":"Auth0-compatible alias for `resource` (RFC 8707). When both are sent they must match. Identifier of the Api the client wants to access; persisted in the auth state so the token issued at /oauth/token targets the matching Api resource."},"resource":{"type":"string","description":"RFC 8707 Resource Indicator. MUST be an absolute URI without fragment. When matched against a registered Api in the tenant the issued access_token gets `aud = <api.identifier>`."}},"description":"UsernamepasswordLoginBody","additionalProperties":false}}},"description":"UsernamepasswordLoginBody"},"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_code` — The verification code is wrong, expired or already used.\n\n`invalid_connection` — The connection does not exist, is disabled, or is not of the type this flow needs.\n\n`invalid_credentials` — The email/username or password is incorrect.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_code","invalid_connection","invalid_credentials","validation_error"]}}}]}}}},"401":{"description":"`login_denied` — A post-login Action denied the sign-in. `message` carries the reason it gave. On token grants it is a 403 with `error: invalid_grant`.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["login_denied"]}}}]}}}},"403":{"description":"`user_suspended` — The user is suspended and cannot sign in or be modified.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["user_suspended"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/login/callback":{"post":{"operationId":"usernamepassword/loginCallback","summary":"Username + password login callback","tags":["usernamepassword"],"description":"Completes the username/password login flow. The token issued by `/usernamepassword/login` is posted back here together with the serialized state (`wctx`) so the server can redirect the user agent to the original client `redirect_uri`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["wa","wresult","wctx"],"properties":{"wa":{"type":"string","description":"is always wsignin1.0"},"wresult":{"type":"string","description":"JWT"},"wctx":{"type":"string","description":"Serialized JSON with context"}}}}}},"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_request` — A required OAuth parameter is missing or two of them are incompatible. `message` says which.\n\n`invalid_state` — The `state` is missing, expired, or does not describe a resumable step.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_request","invalid_state","validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/dbconnections/change_password":{"post":{"operationId":"change_password","summary":"Change password","tags":["dbconnection"],"description":"Change password","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string"},"channel":{"anyOf":[{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]}],"description":"How to deliver the reset: `email` (link), `sms` / `whatsapp` (a 6-digit code to the verified phone on file). Silently falls back to the tenant default when the channel is not available for this user — the response never says which channels an account has."}}}}}},"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_client` — The `client_id` in the request does not name a client of this account.\n\n`invalid_connection` — The connection does not exist, is disabled, or is not of the type this flow needs.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_client","invalid_connection","validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/reset-verify":{"get":{"operationId":"change_password_verify","summary":"Change password verify","tags":["dbconnection"],"description":"Change password verify","parameters":[{"schema":{"type":"string"},"in":"query","name":"ticket","required":true}],"responses":{"200":{"description":"Default Response"},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/reset-change":{"post":{"operationId":"change_password_change","summary":"Reset password change","tags":["dbconnection"],"description":"Reset password change","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["state","new_password"],"properties":{"state":{"type":"string"},"new_password":{"type":"string","minLength":1}}}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["ok"]}}}}}},"400":{"description":"`invalid_code` — The verification code is wrong, expired or already used.\n\n`invalid_connection` — The connection does not exist, is disabled, or is not of the type this flow needs.\n\n`invalid_ticket` — The ticket does not exist or is not the kind this endpoint accepts.\n\n`password_too_weak` — The password does not meet the connection policy. `message` lists each unmet rule.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_code","invalid_connection","invalid_ticket","password_too_weak","validation_error"]}}}]}}}},"404":{"description":"`credential_not_found` — The user has no password credential on this connection.\n\n`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["credential_not_found","account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/reset-code/verify":{"post":{"operationId":"reset_code_verify","summary":"Redeem a password-reset code sent by SMS/WhatsApp","tags":["dbconnection"],"description":"Second entry point to the new-password screen, for a reset delivered as a 6-digit code instead of a link. Answers the same `redirect_url` that the emailed link would land on. Five wrong codes consume the ticket; the error is always `invalid_code` and reveals nothing about the account.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","code"],"properties":{"email":{"type":"string"},"code":{"type":"string","minLength":4,"maxLength":12}},"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["redirect_url"],"properties":{"redirect_url":{"type":"string"}}}}}},"400":{"description":"`invalid_code` — The verification code is wrong, expired or already used.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_code","validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/dbconnections/recovery_options":{"post":{"operationId":"recovery_options","summary":"Recovery channels to offer for an identifier","tags":["dbconnection"],"description":"For the forgot-password screen. With the tenant's `visible` list empty (the default) it answers from configuration alone and never looks the account up. Otherwise it returns the channels the user can pick, with masked destinations.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","required":["picker","default","channels"],"properties":{"picker":{"type":"boolean","description":"Whether the screen should offer a choice. False when the tenant keeps `visible` empty — then nothing here depends on the account existing."},"default":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"channels":{"type":"array","items":{"type":"object","required":["channel"],"properties":{"channel":{"anyOf":[{"type":"string","enum":["email"]},{"type":"string","enum":["sms"]},{"type":"string","enum":["whatsapp"]},{"type":"string","enum":["factor"]}]},"destination_masked":{"type":"string"}}}}}}}}},"400":{"description":"`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["validation_error"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/dbconnections/signup":{"post":{"operationId":"signup","summary":"Self-service signup","tags":["dbconnection"],"description":"Creates a new user and database credential (email + password) in a single call against the tenant database connection. Public and account-scoped (no management token). Gated per-connection by `disable_signup`. The user is created with `email_verified: false`; the account notification settings (`verify_email_auto_send`, `welcome_email_enabled`) drive any follow-up emails. Does not establish a session — the client logs in afterwards.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","description":"Email login identifier."},"password":{"type":"string","description":"Plaintext password (validated against the connection policy, hashed on write)."},"name":{"type":"string"},"given_name":{"type":"string"},"family_name":{"type":"string"},"user_metadata":{"type":"object","additionalProperties":{}},"connection":{"type":"string","description":"Optional connection_name to disambiguate when the tenant has more than one database connection. Defaults to the tenant database connection."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Default Response"},"400":{"description":"`invalid_connection` — The connection does not exist, is disabled, or is not of the type this flow needs.\n\n`password_too_weak` — The password does not meet the connection policy. `message` lists each unmet rule.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["invalid_connection","password_too_weak","validation_error"]}}}]}}}},"403":{"description":"`signup_denied` — A pre-signup Action or an active block denied the sign-up. `message` carries the reason.\n\n`signup_disabled` — Self-service sign-up is disabled on this connection.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["signup_denied","signup_disabled"]}}}]}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"409":{"description":"`email_taken` — Another user in this account already uses that email.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["email_taken"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}},"/version":{"get":{"operationId":"getVersion","summary":"Get the running service version and git commit","tags":["system"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"},"commit":{"type":"string"}}}}}},"404":{"description":"`account_not_found` — No Auth Account matches the request (domain, header or token).","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["account_not_found"]}}}]}}}},"429":{"description":"`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["too_many_requests"]}}}]}}}},"500":{"description":"`internal_error` — Unexpected server error. Retry later; the request id is logged.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/ErrorResponse"},{"type":"object","properties":{"error_code":{"type":"string","enum":["internal_error"]}}}]}}}}}}}},"servers":[{"url":"https://faable.auth.faable.link","description":"Faable (account_6475c88abdb00153b3494b8e)"}],"tags":[{"name":"user","description":"👨🏻‍💻 Manage Users"},{"name":"passwordless","description":"🪄 Passwordless authorization","externalDocs":{"url":"https://faable.com/docs/auth/passwordless"}},{"name":"session","description":"🪄 Current session endpoints"},{"name":"mfa","description":"Second factor: enrol authenticator apps and passkeys, answer challenges, step up a session, recover with a factor. Session-authenticated; used by the hosted screens.","externalDocs":{"url":"https://faable.com/docs/auth/mfa"}},{"name":"client","description":"🌏 Manage OAuth Clients","externalDocs":{"url":"https://faable.com/docs/auth/clients"}},{"name":"oauth","description":"OAuth Endpoints","externalDocs":{"url":"https://faable.com/docs/auth/oauth-flows/authorization-code"}},{"name":"connection","description":"Social connections and Authorization Servers","externalDocs":{"url":"https://faable.com/docs/auth/connections"}},{"name":"identity","description":"Identities fetched from Connections like Google, Slack, Facebook, etc."},{"name":"role","description":"Account-wide roles. A Role is a named label (e.g. `admin`, `editor`) that can be granted to users via RoleMember. Roles are the primary RBAC primitive used by downstream services to gate access."},{"name":"rolemember","description":"Grants of a Role to a User at the account level. Each RoleMember binds one (role, user) pair. Use `GET /role/:role_id/users` to list everyone holding a given role, and `POST /role/:role_id/users` to grant a role to one or more users in bulk."},{"name":"team","description":"Groups of Users inside an Account. Teams scope membership and per-team role assignments — a User can belong to multiple Teams and hold different Roles in each. Members are managed via the `/team/:team_id/member` sub-resource."},{"name":"api","description":"Resource Servers protected by Faable Auth. Each Api declares an `identifier` (audience URL clients pass as `audience=` when requesting a token), the `permissions` it exposes, and its token issuance policy (`token_dialect`, `token_lifetime`, `enforce_policies`). Tokens issued with `audience=<identifier>` carry that identifier in the `aud` claim and, when `token_dialect=access_token_authz`, a filtered `permissions` claim.","externalDocs":{"url":"https://faable.com/docs/auth/apis"}}]}