@faable/auth-sdk 2.7.51 → 2.7.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/spec/openapi.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "info": {
4
4
  "title": "@faablecloud/auth",
5
5
  "description": "Auth Platform made by Faable. Manage Users and Roles",
6
- "version": "2.47.2",
6
+ "version": "2.48.0",
7
7
  "license": {
8
8
  "name": "private",
9
9
  "url": "https://faable.com/docs/platform/privacy-policy"
@@ -148,6 +148,17 @@
148
148
  "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.",
149
149
  "nullable": true
150
150
  },
151
+ "email_from_name": {
152
+ "type": "string",
153
+ "maxLength": 100,
154
+ "description": "Display name on every email this tenant sends to its users. Absent = the account `name`.",
155
+ "nullable": true
156
+ },
157
+ "email_from_address": {
158
+ "type": "string",
159
+ "description": "Sender address of every email this tenant sends to its users. Absent = the platform default. Set by Faable only.",
160
+ "nullable": true
161
+ },
151
162
  "welcome_email": {
152
163
  "type": "object",
153
164
  "properties": {
@@ -6718,6 +6729,33 @@
6718
6729
  }
6719
6730
  }
6720
6731
  },
6732
+ "AccountEmailSenderParams": {
6733
+ "type": "object",
6734
+ "required": [
6735
+ "account_id"
6736
+ ],
6737
+ "properties": {
6738
+ "account_id": {
6739
+ "type": "string"
6740
+ }
6741
+ },
6742
+ "additionalProperties": false
6743
+ },
6744
+ "AccountEmailSenderBody": {
6745
+ "type": "object",
6746
+ "required": [
6747
+ "address"
6748
+ ],
6749
+ "properties": {
6750
+ "address": {
6751
+ "type": "string",
6752
+ "format": "email",
6753
+ "maxLength": 320,
6754
+ "nullable": true
6755
+ }
6756
+ },
6757
+ "additionalProperties": false
6758
+ },
6721
6759
  "AuthAccountUpdate": {
6722
6760
  "type": "object",
6723
6761
  "properties": {
@@ -6779,6 +6817,12 @@
6779
6817
  "maxLength": 320,
6780
6818
  "nullable": true
6781
6819
  },
6820
+ "email_from_name": {
6821
+ "type": "string",
6822
+ "minLength": 1,
6823
+ "maxLength": 100,
6824
+ "nullable": true
6825
+ },
6782
6826
  "welcome_email": {
6783
6827
  "anyOf": [
6784
6828
  {
@@ -7458,7 +7502,7 @@
7458
7502
  },
7459
7503
  "ErrorCode": {
7460
7504
  "type": "string",
7461
- "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- `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- `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.",
7505
+ "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.",
7462
7506
  "enum": [
7463
7507
  "bad_request",
7464
7508
  "validation_error",
@@ -7485,6 +7529,7 @@
7485
7529
  "account_limit_reached",
7486
7530
  "invalid_project_id",
7487
7531
  "account_transfer_refused",
7532
+ "email_sender_domain_not_allowed",
7488
7533
  "plan_required",
7489
7534
  "invalid_canonical_host",
7490
7535
  "canonical_host_breaks_passkeys",
@@ -7501,6 +7546,7 @@
7501
7546
  "signup_disabled",
7502
7547
  "email_taken",
7503
7548
  "invalid_email",
7549
+ "invalid_name",
7504
7550
  "same_email",
7505
7551
  "user_has_no_email",
7506
7552
  "password_too_weak",
@@ -7843,6 +7889,17 @@
7843
7889
  "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.",
7844
7890
  "nullable": true
7845
7891
  },
7892
+ "email_from_name": {
7893
+ "type": "string",
7894
+ "maxLength": 100,
7895
+ "description": "Display name on every email this tenant sends to its users. Absent = the account `name`.",
7896
+ "nullable": true
7897
+ },
7898
+ "email_from_address": {
7899
+ "type": "string",
7900
+ "description": "Sender address of every email this tenant sends to its users. Absent = the platform default. Set by Faable only.",
7901
+ "nullable": true
7902
+ },
7846
7903
  "welcome_email": {
7847
7904
  "type": "object",
7848
7905
  "properties": {
@@ -8553,6 +8610,17 @@
8553
8610
  "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.",
8554
8611
  "nullable": true
8555
8612
  },
8613
+ "email_from_name": {
8614
+ "type": "string",
8615
+ "maxLength": 100,
8616
+ "description": "Display name on every email this tenant sends to its users. Absent = the account `name`.",
8617
+ "nullable": true
8618
+ },
8619
+ "email_from_address": {
8620
+ "type": "string",
8621
+ "description": "Sender address of every email this tenant sends to its users. Absent = the platform default. Set by Faable only.",
8622
+ "nullable": true
8623
+ },
8556
8624
  "welcome_email": {
8557
8625
  "type": "object",
8558
8626
  "properties": {
@@ -8923,7 +8991,7 @@
8923
8991
  }
8924
8992
  },
8925
8993
  "400": {
8926
- "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.",
8994
+ "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.",
8927
8995
  "content": {
8928
8996
  "application/json": {
8929
8997
  "schema": {
@@ -8939,7 +9007,8 @@
8939
9007
  "enum": [
8940
9008
  "team_required",
8941
9009
  "already_exists",
8942
- "validation_error"
9010
+ "validation_error",
9011
+ "invalid_name"
8943
9012
  ]
8944
9013
  }
8945
9014
  }
@@ -9499,6 +9568,17 @@
9499
9568
  "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.",
9500
9569
  "nullable": true
9501
9570
  },
9571
+ "email_from_name": {
9572
+ "type": "string",
9573
+ "maxLength": 100,
9574
+ "description": "Display name on every email this tenant sends to its users. Absent = the account `name`.",
9575
+ "nullable": true
9576
+ },
9577
+ "email_from_address": {
9578
+ "type": "string",
9579
+ "description": "Sender address of every email this tenant sends to its users. Absent = the platform default. Set by Faable only.",
9580
+ "nullable": true
9581
+ },
9502
9582
  "welcome_email": {
9503
9583
  "type": "object",
9504
9584
  "properties": {
@@ -10097,6 +10177,12 @@
10097
10177
  "maxLength": 320,
10098
10178
  "nullable": true
10099
10179
  },
10180
+ "email_from_name": {
10181
+ "type": "string",
10182
+ "minLength": 1,
10183
+ "maxLength": 100,
10184
+ "nullable": true
10185
+ },
10100
10186
  "welcome_email": {
10101
10187
  "anyOf": [
10102
10188
  {
@@ -10593,6 +10679,17 @@
10593
10679
  "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.",
10594
10680
  "nullable": true
10595
10681
  },
10682
+ "email_from_name": {
10683
+ "type": "string",
10684
+ "maxLength": 100,
10685
+ "description": "Display name on every email this tenant sends to its users. Absent = the account `name`.",
10686
+ "nullable": true
10687
+ },
10688
+ "email_from_address": {
10689
+ "type": "string",
10690
+ "description": "Sender address of every email this tenant sends to its users. Absent = the platform default. Set by Faable only.",
10691
+ "nullable": true
10692
+ },
10596
10693
  "welcome_email": {
10597
10694
  "type": "object",
10598
10695
  "properties": {
@@ -10963,7 +11060,7 @@
10963
11060
  }
10964
11061
  },
10965
11062
  "400": {
10966
- "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.",
11063
+ "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.",
10967
11064
  "content": {
10968
11065
  "application/json": {
10969
11066
  "schema": {
@@ -10981,7 +11078,8 @@
10981
11078
  "canonical_host_breaks_passkeys",
10982
11079
  "invalid_id",
10983
11080
  "already_exists",
10984
- "validation_error"
11081
+ "validation_error",
11082
+ "invalid_name"
10985
11083
  ]
10986
11084
  }
10987
11085
  }
@@ -13338,90 +13436,44 @@
13338
13436
  }
13339
13437
  }
13340
13438
  },
13341
- "/connection": {
13342
- "get": {
13343
- "operationId": "connection/list",
13344
- "summary": "List Connections",
13439
+ "/account/{account_id}/email_sender": {
13440
+ "post": {
13441
+ "operationId": "account/email_sender",
13442
+ "summary": "Set an Account's sender address",
13345
13443
  "tags": [
13346
- "connection"
13444
+ "account"
13347
13445
  ],
13348
- "description": "List Connections",
13446
+ "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.",
13447
+ "requestBody": {
13448
+ "required": true,
13449
+ "content": {
13450
+ "application/json": {
13451
+ "schema": {
13452
+ "type": "object",
13453
+ "required": [
13454
+ "address"
13455
+ ],
13456
+ "properties": {
13457
+ "address": {
13458
+ "type": "string",
13459
+ "format": "email",
13460
+ "maxLength": 320,
13461
+ "nullable": true
13462
+ }
13463
+ },
13464
+ "additionalProperties": false
13465
+ }
13466
+ }
13467
+ }
13468
+ },
13349
13469
  "parameters": [
13350
- {
13351
- "schema": {
13352
- "type": "number",
13353
- "minimum": 1,
13354
- "maximum": 200
13355
- },
13356
- "in": "query",
13357
- "name": "pageSize",
13358
- "required": false,
13359
- "description": "Number of items per page (max 200)"
13360
- },
13361
- {
13362
- "schema": {
13363
- "type": "string"
13364
- },
13365
- "in": "query",
13366
- "name": "cursor",
13367
- "required": false,
13368
- "description": "Cursor for next page"
13369
- },
13370
- {
13371
- "schema": {
13372
- "type": "string"
13373
- },
13374
- "in": "query",
13375
- "name": "next",
13376
- "required": false,
13377
- "description": "Cursor returned by the previous page"
13378
- },
13379
- {
13380
- "schema": {
13381
- "type": "string"
13382
- },
13383
- "in": "query",
13384
- "name": "connection_type",
13385
- "required": false,
13386
- "description": "Exact match on `connection_type`. Equivalent to `?query=connection_type:<value>`."
13387
- },
13388
- {
13389
- "schema": {
13390
- "type": "string"
13391
- },
13392
- "in": "query",
13393
- "name": "enabled",
13394
- "required": false,
13395
- "description": "Exact match on `enabled`. Equivalent to `?query=enabled:<value>`."
13396
- },
13397
- {
13398
- "schema": {
13399
- "type": "string"
13400
- },
13401
- "in": "query",
13402
- "name": "category",
13403
- "required": false,
13404
- "description": "Exact match on `category`. Equivalent to `?query=category:<value>`."
13405
- },
13406
13470
  {
13407
13471
  "schema": {
13408
13472
  "type": "string"
13409
13473
  },
13410
- "in": "query",
13411
- "name": "query",
13412
- "required": false,
13413
- "description": "Filter using a FaableQL query"
13414
- },
13415
- {
13416
- "schema": {
13417
- "type": "string",
13418
- "minLength": 1,
13419
- "maxLength": 200
13420
- },
13421
- "in": "query",
13422
- "name": "q",
13423
- "required": false,
13424
- "description": "Full-text search across: `connection_name`, `connection_type`."
13474
+ "in": "path",
13475
+ "name": "account_id",
13476
+ "required": true
13425
13477
  }
13426
13478
  ],
13427
13479
  "security": [
@@ -13431,52 +13483,796 @@
13431
13483
  ],
13432
13484
  "responses": {
13433
13485
  "200": {
13434
- "description": "Default Response",
13486
+ "description": "AuthAccount",
13435
13487
  "content": {
13436
13488
  "application/json": {
13437
13489
  "schema": {
13438
13490
  "type": "object",
13439
13491
  "required": [
13440
- "next",
13441
- "results"
13492
+ "id",
13493
+ "name",
13494
+ "domain",
13495
+ "slug",
13496
+ "callback_hostnames",
13497
+ "token_signature",
13498
+ "token_signing_alg",
13499
+ "enabled_locales",
13500
+ "notification_settings",
13501
+ "createdAt"
13442
13502
  ],
13443
13503
  "properties": {
13444
- "next": {
13504
+ "id": {
13505
+ "type": "string",
13506
+ "description": "AuthAccount ID"
13507
+ },
13508
+ "name": {
13509
+ "type": "string"
13510
+ },
13511
+ "domain": {
13512
+ "type": "string"
13513
+ },
13514
+ "slug": {
13515
+ "type": "string"
13516
+ },
13517
+ "logo_src": {
13518
+ "type": "string",
13519
+ "nullable": true
13520
+ },
13521
+ "icon_src": {
13522
+ "type": "string",
13523
+ "nullable": true
13524
+ },
13525
+ "callback_hostnames": {
13526
+ "type": "array",
13527
+ "items": {
13528
+ "type": "string"
13529
+ }
13530
+ },
13531
+ "token_signature": {
13532
+ "type": "string",
13533
+ "description": "Shared HMAC secret. Used when token_signing_alg is an HS* algorithm; ignored for RS*/ES*/PS*."
13534
+ },
13535
+ "token_signing_alg": {
13445
13536
  "anyOf": [
13446
13537
  {
13447
- "type": "string"
13538
+ "type": "string",
13539
+ "enum": [
13540
+ "RS256"
13541
+ ]
13542
+ }
13543
+ ],
13544
+ "description": "JWA algorithm used to sign tokens issued by this Account. Today only RS256 is implemented."
13545
+ },
13546
+ "default_connection": {
13547
+ "anyOf": [
13548
+ {
13549
+ "$ref": "#/components/schemas/Connection"
13448
13550
  },
13449
13551
  {
13450
- "type": "null"
13451
- }
13552
+ "type": "string"
13553
+ },
13554
+ {}
13452
13555
  ]
13453
13556
  },
13454
- "results": {
13557
+ "enabled_locales": {
13455
13558
  "type": "array",
13456
13559
  "items": {
13457
- "$ref": "#/components/schemas/Connection"
13560
+ "type": "string"
13458
13561
  }
13459
- }
13460
- },
13461
- "additionalProperties": false
13462
- }
13463
- }
13464
- }
13465
- },
13466
- "400": {
13467
- "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.",
13468
- "content": {
13469
- "application/json": {
13470
- "schema": {
13471
- "allOf": [
13472
- {
13473
- "$ref": "#/components/schemas/ErrorResponse"
13474
13562
  },
13475
- {
13476
- "type": "object",
13477
- "properties": {
13478
- "error_code": {
13479
- "type": "string",
13563
+ "tags": {
13564
+ "type": "array",
13565
+ "items": {
13566
+ "type": "string"
13567
+ },
13568
+ "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`)."
13569
+ },
13570
+ "team": {
13571
+ "type": "string",
13572
+ "nullable": true
13573
+ },
13574
+ "notification_settings": {
13575
+ "type": "object",
13576
+ "required": [
13577
+ "welcome_email_enabled",
13578
+ "verify_email_auto_send"
13579
+ ],
13580
+ "properties": {
13581
+ "welcome_email_enabled": {
13582
+ "type": "boolean",
13583
+ "description": "Send the built-in welcome email on user.created",
13584
+ "default": false
13585
+ },
13586
+ "verify_email_auto_send": {
13587
+ "type": "boolean",
13588
+ "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`.",
13589
+ "default": false
13590
+ }
13591
+ },
13592
+ "additionalProperties": false
13593
+ },
13594
+ "email_reply_to": {
13595
+ "type": "string",
13596
+ "format": "email",
13597
+ "maxLength": 320,
13598
+ "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.",
13599
+ "nullable": true
13600
+ },
13601
+ "email_from_name": {
13602
+ "type": "string",
13603
+ "maxLength": 100,
13604
+ "description": "Display name on every email this tenant sends to its users. Absent = the account `name`.",
13605
+ "nullable": true
13606
+ },
13607
+ "email_from_address": {
13608
+ "type": "string",
13609
+ "description": "Sender address of every email this tenant sends to its users. Absent = the platform default. Set by Faable only.",
13610
+ "nullable": true
13611
+ },
13612
+ "welcome_email": {
13613
+ "type": "object",
13614
+ "properties": {
13615
+ "cta_url": {
13616
+ "type": "string",
13617
+ "format": "uri",
13618
+ "maxLength": 500,
13619
+ "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.",
13620
+ "nullable": true
13621
+ },
13622
+ "body": {
13623
+ "type": "string",
13624
+ "maxLength": 600,
13625
+ "description": "Replaces the default one-line body (\"Your account on … is ready to go.\"). Plain text, sent verbatim in every enabled locale.",
13626
+ "nullable": true
13627
+ },
13628
+ "social_links": {
13629
+ "type": "array",
13630
+ "items": {
13631
+ "type": "object",
13632
+ "required": [
13633
+ "label",
13634
+ "url"
13635
+ ],
13636
+ "properties": {
13637
+ "label": {
13638
+ "type": "string",
13639
+ "minLength": 1,
13640
+ "maxLength": 40
13641
+ },
13642
+ "url": {
13643
+ "type": "string",
13644
+ "format": "uri",
13645
+ "maxLength": 500
13646
+ }
13647
+ },
13648
+ "additionalProperties": false
13649
+ },
13650
+ "maxItems": 6,
13651
+ "description": "Rendered as a \"Follow <account>\" block after the button — GitHub, LinkedIn, X, YouTube… Empty or absent hides the block.",
13652
+ "nullable": true
13653
+ }
13654
+ },
13655
+ "additionalProperties": false,
13656
+ "nullable": true
13657
+ },
13658
+ "email_change_verification_mode": {
13659
+ "anyOf": [
13660
+ {
13661
+ "type": "string",
13662
+ "enum": [
13663
+ "new_only"
13664
+ ]
13665
+ },
13666
+ {
13667
+ "type": "string",
13668
+ "enum": [
13669
+ "old_and_new"
13670
+ ]
13671
+ }
13672
+ ],
13673
+ "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."
13674
+ },
13675
+ "email_oauth_sync_policy": {
13676
+ "anyOf": [
13677
+ {
13678
+ "type": "string",
13679
+ "enum": [
13680
+ "preserve_manual"
13681
+ ]
13682
+ },
13683
+ {
13684
+ "type": "string",
13685
+ "enum": [
13686
+ "always_sync"
13687
+ ]
13688
+ }
13689
+ ],
13690
+ "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.)."
13691
+ },
13692
+ "login_methods": {
13693
+ "type": "object",
13694
+ "properties": {
13695
+ "order": {
13696
+ "type": "array",
13697
+ "items": {
13698
+ "type": "string"
13699
+ },
13700
+ "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()."
13701
+ },
13702
+ "disabled_connections": {
13703
+ "type": "array",
13704
+ "items": {
13705
+ "type": "string"
13706
+ },
13707
+ "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."
13708
+ },
13709
+ "passkey_login_enabled": {
13710
+ "type": "boolean",
13711
+ "description": "Offer passkey (WebAuthn) as a primary login method on the hosted login screen. Defaults to false."
13712
+ },
13713
+ "identifier_first": {
13714
+ "type": "boolean",
13715
+ "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."
13716
+ },
13717
+ "remember_last_method": {
13718
+ "type": "boolean",
13719
+ "description": "Surface the method the returning user chose last time. Defaults to false."
13720
+ },
13721
+ "passkey_promotion": {
13722
+ "anyOf": [
13723
+ {
13724
+ "type": "string",
13725
+ "enum": [
13726
+ "off"
13727
+ ]
13728
+ },
13729
+ {
13730
+ "type": "string",
13731
+ "enum": [
13732
+ "offer"
13733
+ ]
13734
+ }
13735
+ ],
13736
+ "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`."
13737
+ },
13738
+ "passkey_promotion_snooze_days": {
13739
+ "type": "integer",
13740
+ "minimum": 0,
13741
+ "maximum": 365,
13742
+ "description": "Days to wait before offering a passkey again to a user who dismissed it. Defaults to 30."
13743
+ },
13744
+ "passkey_promotion_max_prompts": {
13745
+ "type": "integer",
13746
+ "minimum": 0,
13747
+ "maximum": 10,
13748
+ "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."
13749
+ },
13750
+ "remember_me": {
13751
+ "anyOf": [
13752
+ {
13753
+ "type": "string",
13754
+ "enum": [
13755
+ "off"
13756
+ ]
13757
+ },
13758
+ {
13759
+ "type": "string",
13760
+ "enum": [
13761
+ "optional"
13762
+ ]
13763
+ }
13764
+ ],
13765
+ "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."
13766
+ },
13767
+ "remember_me_days": {
13768
+ "type": "integer",
13769
+ "minimum": 1,
13770
+ "maximum": 365,
13771
+ "description": "How long a session lasts when the user ticked \"Remember me\". Defaults to 30."
13772
+ }
13773
+ },
13774
+ "additionalProperties": false,
13775
+ "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."
13776
+ },
13777
+ "login_flow": {
13778
+ "type": "string",
13779
+ "description": "The login flow bound to this account (`loginflow_xxx`). Absent = the flow compiled from the settings. A Client may bind its own.",
13780
+ "nullable": true
13781
+ },
13782
+ "mfa_policy": {
13783
+ "type": "object",
13784
+ "properties": {
13785
+ "mode": {
13786
+ "anyOf": [
13787
+ {
13788
+ "anyOf": [
13789
+ {
13790
+ "type": "string",
13791
+ "enum": [
13792
+ "off"
13793
+ ]
13794
+ },
13795
+ {
13796
+ "type": "string",
13797
+ "enum": [
13798
+ "optional"
13799
+ ]
13800
+ },
13801
+ {
13802
+ "type": "string",
13803
+ "enum": [
13804
+ "required"
13805
+ ]
13806
+ }
13807
+ ]
13808
+ }
13809
+ ],
13810
+ "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."
13811
+ },
13812
+ "allowed_factors": {
13813
+ "type": "array",
13814
+ "items": {
13815
+ "anyOf": [
13816
+ {
13817
+ "type": "string",
13818
+ "enum": [
13819
+ "totp"
13820
+ ]
13821
+ },
13822
+ {
13823
+ "type": "string",
13824
+ "enum": [
13825
+ "webauthn"
13826
+ ]
13827
+ }
13828
+ ]
13829
+ },
13830
+ "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."
13831
+ },
13832
+ "remember_device_days": {
13833
+ "type": "integer",
13834
+ "minimum": 0,
13835
+ "maximum": 365,
13836
+ "description": "How long a browser that already passed a challenge may skip the next one. 0 (default) challenges every time."
13837
+ }
13838
+ },
13839
+ "additionalProperties": false,
13840
+ "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."
13841
+ },
13842
+ "recovery_channels": {
13843
+ "type": "object",
13844
+ "properties": {
13845
+ "enabled": {
13846
+ "type": "array",
13847
+ "items": {
13848
+ "anyOf": [
13849
+ {
13850
+ "type": "string",
13851
+ "enum": [
13852
+ "email"
13853
+ ]
13854
+ },
13855
+ {
13856
+ "type": "string",
13857
+ "enum": [
13858
+ "sms"
13859
+ ]
13860
+ },
13861
+ {
13862
+ "type": "string",
13863
+ "enum": [
13864
+ "whatsapp"
13865
+ ]
13866
+ },
13867
+ {
13868
+ "type": "string",
13869
+ "enum": [
13870
+ "factor"
13871
+ ]
13872
+ }
13873
+ ]
13874
+ },
13875
+ "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."
13876
+ },
13877
+ "default": {
13878
+ "anyOf": [
13879
+ {
13880
+ "anyOf": [
13881
+ {
13882
+ "type": "string",
13883
+ "enum": [
13884
+ "email"
13885
+ ]
13886
+ },
13887
+ {
13888
+ "type": "string",
13889
+ "enum": [
13890
+ "sms"
13891
+ ]
13892
+ },
13893
+ {
13894
+ "type": "string",
13895
+ "enum": [
13896
+ "whatsapp"
13897
+ ]
13898
+ },
13899
+ {
13900
+ "type": "string",
13901
+ "enum": [
13902
+ "factor"
13903
+ ]
13904
+ }
13905
+ ]
13906
+ }
13907
+ ],
13908
+ "description": "The channel used when the user is not asked to choose. Falls back to `email` when it is not available for that user."
13909
+ },
13910
+ "visible": {
13911
+ "type": "array",
13912
+ "items": {
13913
+ "anyOf": [
13914
+ {
13915
+ "type": "string",
13916
+ "enum": [
13917
+ "email"
13918
+ ]
13919
+ },
13920
+ {
13921
+ "type": "string",
13922
+ "enum": [
13923
+ "sms"
13924
+ ]
13925
+ },
13926
+ {
13927
+ "type": "string",
13928
+ "enum": [
13929
+ "whatsapp"
13930
+ ]
13931
+ },
13932
+ {
13933
+ "type": "string",
13934
+ "enum": [
13935
+ "factor"
13936
+ ]
13937
+ }
13938
+ ]
13939
+ },
13940
+ "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."
13941
+ }
13942
+ },
13943
+ "additionalProperties": false,
13944
+ "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`."
13945
+ },
13946
+ "default_country_iso": {
13947
+ "type": "string",
13948
+ "minLength": 2,
13949
+ "maxLength": 2,
13950
+ "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.",
13951
+ "nullable": true
13952
+ },
13953
+ "webauthn_rp_id": {
13954
+ "type": "string",
13955
+ "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.",
13956
+ "nullable": true
13957
+ },
13958
+ "logout_confirm_required": {
13959
+ "type": "boolean",
13960
+ "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)."
13961
+ },
13962
+ "canonical_host": {
13963
+ "type": "string",
13964
+ "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.",
13965
+ "nullable": true
13966
+ },
13967
+ "createdAt": {
13968
+ "type": "string",
13969
+ "description": "AuthAccount creation date"
13970
+ },
13971
+ "updatedAt": {
13972
+ "type": "string",
13973
+ "description": "AuthAccount updated date"
13974
+ }
13975
+ },
13976
+ "description": "AuthAccount"
13977
+ }
13978
+ }
13979
+ }
13980
+ },
13981
+ "400": {
13982
+ "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.",
13983
+ "content": {
13984
+ "application/json": {
13985
+ "schema": {
13986
+ "allOf": [
13987
+ {
13988
+ "$ref": "#/components/schemas/ErrorResponse"
13989
+ },
13990
+ {
13991
+ "type": "object",
13992
+ "properties": {
13993
+ "error_code": {
13994
+ "type": "string",
13995
+ "enum": [
13996
+ "email_sender_domain_not_allowed",
13997
+ "validation_error"
13998
+ ]
13999
+ }
14000
+ }
14001
+ }
14002
+ ]
14003
+ }
14004
+ }
14005
+ }
14006
+ },
14007
+ "401": {
14008
+ "description": "`unauthorized` — The request carries no valid credentials.",
14009
+ "content": {
14010
+ "application/json": {
14011
+ "schema": {
14012
+ "allOf": [
14013
+ {
14014
+ "$ref": "#/components/schemas/ErrorResponse"
14015
+ },
14016
+ {
14017
+ "type": "object",
14018
+ "properties": {
14019
+ "error_code": {
14020
+ "type": "string",
14021
+ "enum": [
14022
+ "unauthorized"
14023
+ ]
14024
+ }
14025
+ }
14026
+ }
14027
+ ]
14028
+ }
14029
+ }
14030
+ }
14031
+ },
14032
+ "403": {
14033
+ "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.",
14034
+ "content": {
14035
+ "application/json": {
14036
+ "schema": {
14037
+ "allOf": [
14038
+ {
14039
+ "$ref": "#/components/schemas/ErrorResponse"
14040
+ },
14041
+ {
14042
+ "type": "object",
14043
+ "properties": {
14044
+ "error_code": {
14045
+ "type": "string",
14046
+ "enum": [
14047
+ "forbidden",
14048
+ "insufficient_scope",
14049
+ "user_suspended"
14050
+ ]
14051
+ }
14052
+ }
14053
+ }
14054
+ ]
14055
+ }
14056
+ }
14057
+ }
14058
+ },
14059
+ "404": {
14060
+ "description": "`account_not_found` — No Auth Account matches the request (domain, header or token).",
14061
+ "content": {
14062
+ "application/json": {
14063
+ "schema": {
14064
+ "allOf": [
14065
+ {
14066
+ "$ref": "#/components/schemas/ErrorResponse"
14067
+ },
14068
+ {
14069
+ "type": "object",
14070
+ "properties": {
14071
+ "error_code": {
14072
+ "type": "string",
14073
+ "enum": [
14074
+ "account_not_found"
14075
+ ]
14076
+ }
14077
+ }
14078
+ }
14079
+ ]
14080
+ }
14081
+ }
14082
+ }
14083
+ },
14084
+ "429": {
14085
+ "description": "`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.",
14086
+ "content": {
14087
+ "application/json": {
14088
+ "schema": {
14089
+ "allOf": [
14090
+ {
14091
+ "$ref": "#/components/schemas/ErrorResponse"
14092
+ },
14093
+ {
14094
+ "type": "object",
14095
+ "properties": {
14096
+ "error_code": {
14097
+ "type": "string",
14098
+ "enum": [
14099
+ "too_many_requests"
14100
+ ]
14101
+ }
14102
+ }
14103
+ }
14104
+ ]
14105
+ }
14106
+ }
14107
+ }
14108
+ },
14109
+ "500": {
14110
+ "description": "`internal_error` — Unexpected server error. Retry later; the request id is logged.",
14111
+ "content": {
14112
+ "application/json": {
14113
+ "schema": {
14114
+ "allOf": [
14115
+ {
14116
+ "$ref": "#/components/schemas/ErrorResponse"
14117
+ },
14118
+ {
14119
+ "type": "object",
14120
+ "properties": {
14121
+ "error_code": {
14122
+ "type": "string",
14123
+ "enum": [
14124
+ "internal_error"
14125
+ ]
14126
+ }
14127
+ }
14128
+ }
14129
+ ]
14130
+ }
14131
+ }
14132
+ }
14133
+ }
14134
+ }
14135
+ }
14136
+ },
14137
+ "/connection": {
14138
+ "get": {
14139
+ "operationId": "connection/list",
14140
+ "summary": "List Connections",
14141
+ "tags": [
14142
+ "connection"
14143
+ ],
14144
+ "description": "List Connections",
14145
+ "parameters": [
14146
+ {
14147
+ "schema": {
14148
+ "type": "number",
14149
+ "minimum": 1,
14150
+ "maximum": 200
14151
+ },
14152
+ "in": "query",
14153
+ "name": "pageSize",
14154
+ "required": false,
14155
+ "description": "Number of items per page (max 200)"
14156
+ },
14157
+ {
14158
+ "schema": {
14159
+ "type": "string"
14160
+ },
14161
+ "in": "query",
14162
+ "name": "cursor",
14163
+ "required": false,
14164
+ "description": "Cursor for next page"
14165
+ },
14166
+ {
14167
+ "schema": {
14168
+ "type": "string"
14169
+ },
14170
+ "in": "query",
14171
+ "name": "next",
14172
+ "required": false,
14173
+ "description": "Cursor returned by the previous page"
14174
+ },
14175
+ {
14176
+ "schema": {
14177
+ "type": "string"
14178
+ },
14179
+ "in": "query",
14180
+ "name": "connection_type",
14181
+ "required": false,
14182
+ "description": "Exact match on `connection_type`. Equivalent to `?query=connection_type:<value>`."
14183
+ },
14184
+ {
14185
+ "schema": {
14186
+ "type": "string"
14187
+ },
14188
+ "in": "query",
14189
+ "name": "enabled",
14190
+ "required": false,
14191
+ "description": "Exact match on `enabled`. Equivalent to `?query=enabled:<value>`."
14192
+ },
14193
+ {
14194
+ "schema": {
14195
+ "type": "string"
14196
+ },
14197
+ "in": "query",
14198
+ "name": "category",
14199
+ "required": false,
14200
+ "description": "Exact match on `category`. Equivalent to `?query=category:<value>`."
14201
+ },
14202
+ {
14203
+ "schema": {
14204
+ "type": "string"
14205
+ },
14206
+ "in": "query",
14207
+ "name": "query",
14208
+ "required": false,
14209
+ "description": "Filter using a FaableQL query"
14210
+ },
14211
+ {
14212
+ "schema": {
14213
+ "type": "string",
14214
+ "minLength": 1,
14215
+ "maxLength": 200
14216
+ },
14217
+ "in": "query",
14218
+ "name": "q",
14219
+ "required": false,
14220
+ "description": "Full-text search across: `connection_name`, `connection_type`."
14221
+ }
14222
+ ],
14223
+ "security": [
14224
+ {
14225
+ "bearerAuth": []
14226
+ }
14227
+ ],
14228
+ "responses": {
14229
+ "200": {
14230
+ "description": "Default Response",
14231
+ "content": {
14232
+ "application/json": {
14233
+ "schema": {
14234
+ "type": "object",
14235
+ "required": [
14236
+ "next",
14237
+ "results"
14238
+ ],
14239
+ "properties": {
14240
+ "next": {
14241
+ "anyOf": [
14242
+ {
14243
+ "type": "string"
14244
+ },
14245
+ {
14246
+ "type": "null"
14247
+ }
14248
+ ]
14249
+ },
14250
+ "results": {
14251
+ "type": "array",
14252
+ "items": {
14253
+ "$ref": "#/components/schemas/Connection"
14254
+ }
14255
+ }
14256
+ },
14257
+ "additionalProperties": false
14258
+ }
14259
+ }
14260
+ }
14261
+ },
14262
+ "400": {
14263
+ "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.",
14264
+ "content": {
14265
+ "application/json": {
14266
+ "schema": {
14267
+ "allOf": [
14268
+ {
14269
+ "$ref": "#/components/schemas/ErrorResponse"
14270
+ },
14271
+ {
14272
+ "type": "object",
14273
+ "properties": {
14274
+ "error_code": {
14275
+ "type": "string",
13480
14276
  "enum": [
13481
14277
  "invalid_query",
13482
14278
  "invalid_expand",
@@ -20944,7 +21740,7 @@
20944
21740
  }
20945
21741
  },
20946
21742
  "400": {
20947
- "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.",
21743
+ "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.",
20948
21744
  "content": {
20949
21745
  "application/json": {
20950
21746
  "schema": {
@@ -20960,7 +21756,8 @@
20960
21756
  "enum": [
20961
21757
  "already_exists",
20962
21758
  "validation_error",
20963
- "invalid_phone"
21759
+ "invalid_phone",
21760
+ "invalid_name"
20964
21761
  ]
20965
21762
  }
20966
21763
  }
@@ -22166,7 +22963,7 @@
22166
22963
  }
22167
22964
  },
22168
22965
  "400": {
22169
- "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.",
22966
+ "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.",
22170
22967
  "content": {
22171
22968
  "application/json": {
22172
22969
  "schema": {
@@ -22183,7 +22980,8 @@
22183
22980
  "invalid_id",
22184
22981
  "already_exists",
22185
22982
  "validation_error",
22186
- "invalid_phone"
22983
+ "invalid_phone",
22984
+ "invalid_name"
22187
22985
  ]
22188
22986
  }
22189
22987
  }