@faable/auth-sdk 2.7.68 → 2.7.70

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.
@@ -441,22 +441,7 @@ export declare abstract class GeneratedFaableAuthApi extends FaableApi {
441
441
  mode?: "required" | "optional" | "off" | undefined;
442
442
  allowed_factors?: ("webauthn" | "totp")[] | undefined;
443
443
  remember_device_days?: number | undefined;
444
- } | undefined; /**
445
- * `GET /user/{user_id}/factors` — operationId: `user/factors`
446
- *
447
- * List a user's second factors
448
- *
449
- * 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.
450
- *
451
- * @throws {FaableApiError} `validation_error` (400) — The body, query or path failed schema validation. `details.issues` lists each failing field.
452
- * @throws {FaableApiError} `unauthorized` (401) — The request carries no valid credentials.
453
- * @throws {FaableApiError} `forbidden` (403) — The credentials are valid but do not allow this operation.
454
- * @throws {FaableApiError} `insufficient_scope` (403) — The token lacks a scope this operation requires. `message` names it.
455
- * @throws {FaableApiError} `user_suspended` (403) — The user is suspended and cannot sign in or be modified.
456
- * @throws {FaableApiError} `account_not_found` (404) — No Auth Account matches the request (domain, header or token).
457
- * @throws {FaableApiError} `too_many_requests` (429) — Rate limit exceeded. Honour the `Retry-After` header.
458
- * @throws {FaableApiError} `internal_error` (500) — Unexpected server error. Retry later; the request id is logged.
459
- */
444
+ } | undefined;
460
445
  recovery_channels?: {
461
446
  enabled?: ("email" | "sms" | "whatsapp" | "factor")[] | undefined;
462
447
  default?: "email" | "sms" | "whatsapp" | "factor" | undefined;
@@ -517,22 +502,7 @@ export declare abstract class GeneratedFaableAuthApi extends FaableApi {
517
502
  mode?: "required" | "optional" | "off" | undefined;
518
503
  allowed_factors?: ("webauthn" | "totp")[] | undefined;
519
504
  remember_device_days?: number | undefined;
520
- } | undefined; /**
521
- * `GET /user/{user_id}/factors` — operationId: `user/factors`
522
- *
523
- * List a user's second factors
524
- *
525
- * 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.
526
- *
527
- * @throws {FaableApiError} `validation_error` (400) — The body, query or path failed schema validation. `details.issues` lists each failing field.
528
- * @throws {FaableApiError} `unauthorized` (401) — The request carries no valid credentials.
529
- * @throws {FaableApiError} `forbidden` (403) — The credentials are valid but do not allow this operation.
530
- * @throws {FaableApiError} `insufficient_scope` (403) — The token lacks a scope this operation requires. `message` names it.
531
- * @throws {FaableApiError} `user_suspended` (403) — The user is suspended and cannot sign in or be modified.
532
- * @throws {FaableApiError} `account_not_found` (404) — No Auth Account matches the request (domain, header or token).
533
- * @throws {FaableApiError} `too_many_requests` (429) — Rate limit exceeded. Honour the `Retry-After` header.
534
- * @throws {FaableApiError} `internal_error` (500) — Unexpected server error. Retry later; the request id is logged.
535
- */
505
+ } | undefined;
536
506
  recovery_channels?: {
537
507
  enabled?: ("email" | "sms" | "whatsapp" | "factor")[] | undefined;
538
508
  default?: "email" | "sms" | "whatsapp" | "factor" | undefined;
@@ -596,22 +566,7 @@ export declare abstract class GeneratedFaableAuthApi extends FaableApi {
596
566
  mode?: "required" | "optional" | "off" | undefined;
597
567
  allowed_factors?: ("webauthn" | "totp")[] | undefined;
598
568
  remember_device_days?: number | undefined;
599
- } | undefined; /**
600
- * `GET /user/{user_id}/factors` — operationId: `user/factors`
601
- *
602
- * List a user's second factors
603
- *
604
- * 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.
605
- *
606
- * @throws {FaableApiError} `validation_error` (400) — The body, query or path failed schema validation. `details.issues` lists each failing field.
607
- * @throws {FaableApiError} `unauthorized` (401) — The request carries no valid credentials.
608
- * @throws {FaableApiError} `forbidden` (403) — The credentials are valid but do not allow this operation.
609
- * @throws {FaableApiError} `insufficient_scope` (403) — The token lacks a scope this operation requires. `message` names it.
610
- * @throws {FaableApiError} `user_suspended` (403) — The user is suspended and cannot sign in or be modified.
611
- * @throws {FaableApiError} `account_not_found` (404) — No Auth Account matches the request (domain, header or token).
612
- * @throws {FaableApiError} `too_many_requests` (429) — Rate limit exceeded. Honour the `Retry-After` header.
613
- * @throws {FaableApiError} `internal_error` (500) — Unexpected server error. Retry later; the request id is logged.
614
- */
569
+ } | undefined;
615
570
  recovery_channels?: {
616
571
  enabled?: ("email" | "sms" | "whatsapp" | "factor")[] | undefined;
617
572
  default?: "email" | "sms" | "whatsapp" | "factor" | undefined;
@@ -5009,6 +4964,34 @@ export declare abstract class GeneratedFaableAuthApi extends FaableApi {
5009
4964
  createdAt: string;
5010
4965
  updatedAt?: string | undefined;
5011
4966
  }>;
4967
+ /**
4968
+ * `POST /team/invite/accept` — operationId: `team/acceptInvite`
4969
+ *
4970
+ * Accept a team invite for a signed-in user
4971
+ *
4972
+ * Accepts the invite whose secret is `token` on behalf of `user_id`, an existing user of this tenant. The user's email must be verified and equal to the invited one (`invite_email_mismatch` otherwise, with the invited address masked in `details`). Never creates a user. Use it from the page you gave as the invite's `accept_url`.
4973
+ *
4974
+ * @throws {FaableApiError} `invalid_ticket` (400) — The ticket does not exist or is not the kind this endpoint accepts.
4975
+ * @throws {FaableApiError} `ticket_used` (400) — The ticket was already consumed.
4976
+ * @throws {FaableApiError} `ticket_expired` (400) — The ticket is past its expiry.
4977
+ * @throws {FaableApiError} `validation_error` (400) — The body, query or path failed schema validation. `details.issues` lists each failing field.
4978
+ * @throws {FaableApiError} `unauthorized` (401) — The request carries no valid credentials.
4979
+ * @throws {FaableApiError} `invite_email_mismatch` (403) — The invite is for another email address than the accepting user's. `details.invited_email` shows it masked.
4980
+ * @throws {FaableApiError} `invite_email_unverified` (403) — The accepting user's email matches the invite but is not verified yet.
4981
+ * @throws {FaableApiError} `forbidden` (403) — The credentials are valid but do not allow this operation.
4982
+ * @throws {FaableApiError} `insufficient_scope` (403) — The token lacks a scope this operation requires. `message` names it.
4983
+ * @throws {FaableApiError} `user_suspended` (403) — The user is suspended and cannot sign in or be modified.
4984
+ * @throws {FaableApiError} `not_found` (404) — The resource does not exist in this account.
4985
+ * @throws {FaableApiError} `account_not_found` (404) — No Auth Account matches the request (domain, header or token).
4986
+ * @throws {FaableApiError} `too_many_requests` (429) — Rate limit exceeded. Honour the `Retry-After` header.
4987
+ * @throws {FaableApiError} `internal_error` (500) — Unexpected server error. Retry later; the request id is logged.
4988
+ */
4989
+ teamAcceptInvite(data: OpBody<"team/acceptInvite">): Promise<{
4990
+ status: "already_member" | "accepted";
4991
+ team_id: string;
4992
+ user_id: string;
4993
+ roles: string[];
4994
+ }>;
5012
4995
  /**
5013
4996
  * `POST /team/{team_id}/member` — operationId: `team/addMembers`
5014
4997
  *
@@ -5165,6 +5148,7 @@ export declare abstract class GeneratedFaableAuthApi extends FaableApi {
5165
5148
  *
5166
5149
  * @throws {FaableApiError} `invite_used` (400) — The invite was already accepted.
5167
5150
  * @throws {FaableApiError} `invalid_role` (400) — The role belongs to another account.
5151
+ * @throws {FaableApiError} `invalid_invite_url` (400) — An invite's `accept_url` or `redirect_uri` is not an http(s) URL at an origin of this tenant's client callbacks or `callback_hostnames`, or it carries a fragment.
5168
5152
  * @throws {FaableApiError} `validation_error` (400) — The body, query or path failed schema validation. `details.issues` lists each failing field.
5169
5153
  * @throws {FaableApiError} `unauthorized` (401) — The request carries no valid credentials.
5170
5154
  * @throws {FaableApiError} `forbidden` (403) — The credentials are valid but do not allow this operation.
@@ -1797,6 +1797,31 @@ export class GeneratedFaableAuthApi extends FaableApi {
1797
1797
  requireId("session_id", session_id);
1798
1798
  return this.fetcher.post(`/session/${session_id}/revoke`, {});
1799
1799
  }
1800
+ /**
1801
+ * `POST /team/invite/accept` — operationId: `team/acceptInvite`
1802
+ *
1803
+ * Accept a team invite for a signed-in user
1804
+ *
1805
+ * Accepts the invite whose secret is `token` on behalf of `user_id`, an existing user of this tenant. The user's email must be verified and equal to the invited one (`invite_email_mismatch` otherwise, with the invited address masked in `details`). Never creates a user. Use it from the page you gave as the invite's `accept_url`.
1806
+ *
1807
+ * @throws {FaableApiError} `invalid_ticket` (400) — The ticket does not exist or is not the kind this endpoint accepts.
1808
+ * @throws {FaableApiError} `ticket_used` (400) — The ticket was already consumed.
1809
+ * @throws {FaableApiError} `ticket_expired` (400) — The ticket is past its expiry.
1810
+ * @throws {FaableApiError} `validation_error` (400) — The body, query or path failed schema validation. `details.issues` lists each failing field.
1811
+ * @throws {FaableApiError} `unauthorized` (401) — The request carries no valid credentials.
1812
+ * @throws {FaableApiError} `invite_email_mismatch` (403) — The invite is for another email address than the accepting user's. `details.invited_email` shows it masked.
1813
+ * @throws {FaableApiError} `invite_email_unverified` (403) — The accepting user's email matches the invite but is not verified yet.
1814
+ * @throws {FaableApiError} `forbidden` (403) — The credentials are valid but do not allow this operation.
1815
+ * @throws {FaableApiError} `insufficient_scope` (403) — The token lacks a scope this operation requires. `message` names it.
1816
+ * @throws {FaableApiError} `user_suspended` (403) — The user is suspended and cannot sign in or be modified.
1817
+ * @throws {FaableApiError} `not_found` (404) — The resource does not exist in this account.
1818
+ * @throws {FaableApiError} `account_not_found` (404) — No Auth Account matches the request (domain, header or token).
1819
+ * @throws {FaableApiError} `too_many_requests` (429) — Rate limit exceeded. Honour the `Retry-After` header.
1820
+ * @throws {FaableApiError} `internal_error` (500) — Unexpected server error. Retry later; the request id is logged.
1821
+ */
1822
+ teamAcceptInvite(data) {
1823
+ return this.fetcher.post(`/team/invite/accept`, data);
1824
+ }
1800
1825
  /**
1801
1826
  * `POST /team/{team_id}/member` — operationId: `team/addMembers`
1802
1827
  *
@@ -1910,6 +1935,7 @@ export class GeneratedFaableAuthApi extends FaableApi {
1910
1935
  *
1911
1936
  * @throws {FaableApiError} `invite_used` (400) — The invite was already accepted.
1912
1937
  * @throws {FaableApiError} `invalid_role` (400) — The role belongs to another account.
1938
+ * @throws {FaableApiError} `invalid_invite_url` (400) — An invite's `accept_url` or `redirect_uri` is not an http(s) URL at an origin of this tenant's client callbacks or `callback_hostnames`, or it carries a fragment.
1913
1939
  * @throws {FaableApiError} `validation_error` (400) — The body, query or path failed schema validation. `details.issues` lists each failing field.
1914
1940
  * @throws {FaableApiError} `unauthorized` (401) — The request carries no valid credentials.
1915
1941
  * @throws {FaableApiError} `forbidden` (403) — The credentials are valid but do not allow this operation.
@@ -1372,6 +1372,26 @@ export interface paths {
1372
1372
  patch?: never;
1373
1373
  trace?: never;
1374
1374
  };
1375
+ "/team/invite/accept": {
1376
+ parameters: {
1377
+ query?: never;
1378
+ header?: never;
1379
+ path?: never;
1380
+ cookie?: never;
1381
+ };
1382
+ get?: never;
1383
+ put?: never;
1384
+ /**
1385
+ * Accept a team invite for a signed-in user
1386
+ * @description Accepts the invite whose secret is `token` on behalf of `user_id`, an existing user of this tenant. The user's email must be verified and equal to the invited one (`invite_email_mismatch` otherwise, with the invited address masked in `details`). Never creates a user. Use it from the page you gave as the invite's `accept_url`.
1387
+ */
1388
+ post: operations["team/acceptInvite"];
1389
+ delete?: never;
1390
+ options?: never;
1391
+ head?: never;
1392
+ patch?: never;
1393
+ trace?: never;
1394
+ };
1375
1395
  "/api": {
1376
1396
  parameters: {
1377
1397
  query?: never;
@@ -6938,6 +6958,9 @@ export interface components {
6938
6958
  * - `malformed_credential` (400): The WebAuthn credential is malformed.
6939
6959
  * - `unknown_passkey` (401): The passkey is not enrolled for this user.
6940
6960
  * - `invalid_passkey` (401): The passkey assertion did not verify.
6961
+ * - `invalid_invite_url` (400): An invite's `accept_url` or `redirect_uri` is not an http(s) URL at an origin of this tenant's client callbacks or `callback_hostnames`, or it carries a fragment.
6962
+ * - `invite_email_mismatch` (403): The invite is for another email address than the accepting user's. `details.invited_email` shows it masked.
6963
+ * - `invite_email_unverified` (403): The accepting user's email matches the invite but is not verified yet.
6941
6964
  * - `invite_not_found` (404): No invite with that id in this account.
6942
6965
  * - `invite_used` (400): The invite was already accepted.
6943
6966
  * - `already_member` (400): The user is already a member of the team.
@@ -6955,7 +6978,7 @@ export interface components {
6955
6978
  * - `delivery_in_progress` (409): The delivery is being attempted right now; retry the redeliver in a minute.
6956
6979
  * @enum {string}
6957
6980
  */
6958
- ErrorCode: "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" | "team_id_unavailable" | "use_team_member_route" | "role_not_found" | "invalid_role" | "flow_not_found" | "revision_not_found" | "invalid_webhook_event" | "invalid_webhook_url" | "not_a_webhook" | "delivery_not_found" | "delivery_in_progress";
6981
+ ErrorCode: "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" | "invalid_invite_url" | "invite_email_mismatch" | "invite_email_unverified" | "invite_not_found" | "invite_used" | "already_member" | "not_a_member" | "team_id_unavailable" | "use_team_member_route" | "role_not_found" | "invalid_role" | "flow_not_found" | "revision_not_found" | "invalid_webhook_event" | "invalid_webhook_url" | "not_a_webhook" | "delivery_not_found" | "delivery_in_progress";
6959
6982
  /** @description Every error response has this shape. Branch on `error_code`, never on `message`: the message is written for a person and may change. */
6960
6983
  ErrorResponse: {
6961
6984
  /** @description The HTTP status, repeated. */
@@ -19534,8 +19557,12 @@ export interface operations {
19534
19557
  roles?: string[];
19535
19558
  /** @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. */
19536
19559
  mode?: "auto" | "invite";
19537
- /** @description Where to redirect the invitee after a successful accept. Defaults to `/`. */
19560
+ /** @description Where `/invite-verify` redirects the invitee after a successful accept. Must be at an origin of one of this tenant's client callbacks or `callback_hostnames`. */
19538
19561
  redirect_uri?: string;
19562
+ /** @description The user who is inviting, when you call on their behalf with a machine token. Shown in the invite email. Must be a user of this tenant; defaults to the token's own user. */
19563
+ inviter_user_id?: string;
19564
+ /** @description Your own page for accepting the invite. The emailed link becomes `<accept_url>#<token>`; the page reads the token from the fragment, signs the user in, and calls `POST /team/invite/accept` with the token and the signed-in user's id. Must be at an origin of one of this tenant's client callbacks or `callback_hostnames`. Without it the link is auth's own `/invite-verify`. */
19565
+ accept_url?: string;
19539
19566
  };
19540
19567
  };
19541
19568
  };
@@ -19583,6 +19610,8 @@ export interface operations {
19583
19610
  *
19584
19611
  * `invalid_role` — The role belongs to another account.
19585
19612
  *
19613
+ * `invalid_invite_url` — An invite's `accept_url` or `redirect_uri` is not an http(s) URL at an origin of this tenant's client callbacks or `callback_hostnames`, or it carries a fragment.
19614
+ *
19586
19615
  * `validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.
19587
19616
  */
19588
19617
  400: {
@@ -19592,7 +19621,7 @@ export interface operations {
19592
19621
  content: {
19593
19622
  "application/json": components["schemas"]["ErrorResponse"] & {
19594
19623
  /** @enum {string} */
19595
- error_code?: "invite_used" | "invalid_role" | "validation_error";
19624
+ error_code?: "invite_used" | "invalid_role" | "invalid_invite_url" | "validation_error";
19596
19625
  };
19597
19626
  };
19598
19627
  };
@@ -19853,6 +19882,135 @@ export interface operations {
19853
19882
  };
19854
19883
  };
19855
19884
  };
19885
+ "team/acceptInvite": {
19886
+ parameters: {
19887
+ query?: never;
19888
+ header?: never;
19889
+ path?: never;
19890
+ cookie?: never;
19891
+ };
19892
+ requestBody: {
19893
+ content: {
19894
+ "application/json": {
19895
+ /** @description The invite secret, as it arrived in the fragment of the `accept_url` link. */
19896
+ token: string;
19897
+ /** @description The user accepting — the one signed in on your page. Must exist in this tenant and have the invited email, verified. */
19898
+ user_id: string;
19899
+ };
19900
+ };
19901
+ };
19902
+ responses: {
19903
+ /** @description Default Response */
19904
+ 200: {
19905
+ headers: {
19906
+ [name: string]: unknown;
19907
+ };
19908
+ content: {
19909
+ "application/json": {
19910
+ /** @description `accepted` when the user joined now; `already_member` when they already were (the invite is spent either way). */
19911
+ status: "accepted" | "already_member";
19912
+ team_id: string;
19913
+ user_id: string;
19914
+ roles: string[];
19915
+ };
19916
+ };
19917
+ };
19918
+ /**
19919
+ * @description `invalid_ticket` — The ticket does not exist or is not the kind this endpoint accepts.
19920
+ *
19921
+ * `ticket_used` — The ticket was already consumed.
19922
+ *
19923
+ * `ticket_expired` — The ticket is past its expiry.
19924
+ *
19925
+ * `validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.
19926
+ */
19927
+ 400: {
19928
+ headers: {
19929
+ [name: string]: unknown;
19930
+ };
19931
+ content: {
19932
+ "application/json": components["schemas"]["ErrorResponse"] & {
19933
+ /** @enum {string} */
19934
+ error_code?: "invalid_ticket" | "ticket_used" | "ticket_expired" | "validation_error";
19935
+ };
19936
+ };
19937
+ };
19938
+ /** @description `unauthorized` — The request carries no valid credentials. */
19939
+ 401: {
19940
+ headers: {
19941
+ [name: string]: unknown;
19942
+ };
19943
+ content: {
19944
+ "application/json": components["schemas"]["ErrorResponse"] & {
19945
+ /** @enum {string} */
19946
+ error_code?: "unauthorized";
19947
+ };
19948
+ };
19949
+ };
19950
+ /**
19951
+ * @description `invite_email_mismatch` — The invite is for another email address than the accepting user's. `details.invited_email` shows it masked.
19952
+ *
19953
+ * `invite_email_unverified` — The accepting user's email matches the invite but is not verified yet.
19954
+ *
19955
+ * `forbidden` — The credentials are valid but do not allow this operation.
19956
+ *
19957
+ * `insufficient_scope` — The token lacks a scope this operation requires. `message` names it.
19958
+ *
19959
+ * `user_suspended` — The user is suspended and cannot sign in or be modified.
19960
+ */
19961
+ 403: {
19962
+ headers: {
19963
+ [name: string]: unknown;
19964
+ };
19965
+ content: {
19966
+ "application/json": components["schemas"]["ErrorResponse"] & {
19967
+ /** @enum {string} */
19968
+ error_code?: "invite_email_mismatch" | "invite_email_unverified" | "forbidden" | "insufficient_scope" | "user_suspended";
19969
+ };
19970
+ };
19971
+ };
19972
+ /**
19973
+ * @description `not_found` — The resource does not exist in this account.
19974
+ *
19975
+ * `account_not_found` — No Auth Account matches the request (domain, header or token).
19976
+ */
19977
+ 404: {
19978
+ headers: {
19979
+ [name: string]: unknown;
19980
+ };
19981
+ content: {
19982
+ "application/json": components["schemas"]["ErrorResponse"] & {
19983
+ /** @enum {string} */
19984
+ error_code?: "not_found" | "account_not_found";
19985
+ };
19986
+ };
19987
+ };
19988
+ /** @description `too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header. */
19989
+ 429: {
19990
+ headers: {
19991
+ [name: string]: unknown;
19992
+ };
19993
+ content: {
19994
+ "application/json": components["schemas"]["ErrorResponse"] & {
19995
+ /** @enum {string} */
19996
+ error_code?: "too_many_requests";
19997
+ };
19998
+ };
19999
+ };
20000
+ /** @description `internal_error` — Unexpected server error. Retry later; the request id is logged. */
20001
+ 500: {
20002
+ headers: {
20003
+ [name: string]: unknown;
20004
+ };
20005
+ content: {
20006
+ "application/json": components["schemas"]["ErrorResponse"] & {
20007
+ /** @enum {string} */
20008
+ error_code?: "internal_error";
20009
+ };
20010
+ };
20011
+ };
20012
+ };
20013
+ };
19856
20014
  "api/list": {
19857
20015
  parameters: {
19858
20016
  query?: {
package/dist/version.js CHANGED
@@ -9,13 +9,13 @@
9
9
  // login pages could not import the SDK at all. Same pattern as auth-js.
10
10
  //
11
11
  // The sentinels MUST stay byte-identical to the `from` values in `.releaserc`.
12
- export const version = "2.7.68";
12
+ export const version = "2.7.70";
13
13
  // Short git SHA of the released commit. The version dates a build; this names
14
14
  // the exact tree, so a canonical log line or an audit entry leads straight to
15
15
  // `git show <sha>`. Deliberately NOT hex: an unreleased build (dev, a local
16
16
  // link) cannot be mistaken for a real commit — auth only records values that
17
17
  // look like a SHA, and this one never will.
18
- export const commit = "46acf9c";
18
+ export const commit = "44a74c7";
19
19
  // What this SDK writes in `x-faable-client`. Exported for a consumer that
20
20
  // builds a strategy on its own (outside `FaableAuthApi`) and still wants the
21
21
  // token request attributed — a bare `authClientCredentials` stamps only what
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faable/auth-sdk",
3
- "version": "2.7.68",
3
+ "version": "2.7.70",
4
4
  "author": "Marc Pomar <marc@faable.com>",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
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.55.0",
6
+ "version": "2.57.0",
7
7
  "license": {
8
8
  "name": "private",
9
9
  "url": "https://faable.com/docs/platform/privacy-policy"
@@ -7519,7 +7519,7 @@
7519
7519
  },
7520
7520
  "ErrorCode": {
7521
7521
  "type": "string",
7522
- "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- `team_id_unavailable` (409): The team id cannot be used in this tenant. Pick another id or let the API generate one.\n- `use_team_member_route` (400): This generic route cannot create memberships. Use `POST /team/{team_id}/member` for team members or `POST /role/{role_id}/users` for role grants.\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.\n- `invalid_webhook_event` (400): An event is not in the webhook event catalog. `message` and `details.valid_events` list the valid ones.\n- `invalid_webhook_url` (400): The webhook URL must be an absolute `https` URL.\n- `not_a_webhook` (400): The operation only applies to `webhook` subscriptions.\n- `delivery_not_found` (404): No webhook delivery with that id for this subscription.\n- `delivery_in_progress` (409): The delivery is being attempted right now; retry the redeliver in a minute.",
7522
+ "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- `invalid_invite_url` (400): An invite's `accept_url` or `redirect_uri` is not an http(s) URL at an origin of this tenant's client callbacks or `callback_hostnames`, or it carries a fragment.\n- `invite_email_mismatch` (403): The invite is for another email address than the accepting user's. `details.invited_email` shows it masked.\n- `invite_email_unverified` (403): The accepting user's email matches the invite but is not verified yet.\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- `team_id_unavailable` (409): The team id cannot be used in this tenant. Pick another id or let the API generate one.\n- `use_team_member_route` (400): This generic route cannot create memberships. Use `POST /team/{team_id}/member` for team members or `POST /role/{role_id}/users` for role grants.\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.\n- `invalid_webhook_event` (400): An event is not in the webhook event catalog. `message` and `details.valid_events` list the valid ones.\n- `invalid_webhook_url` (400): The webhook URL must be an absolute `https` URL.\n- `not_a_webhook` (400): The operation only applies to `webhook` subscriptions.\n- `delivery_not_found` (404): No webhook delivery with that id for this subscription.\n- `delivery_in_progress` (409): The delivery is being attempted right now; retry the redeliver in a minute.",
7523
7523
  "enum": [
7524
7524
  "bad_request",
7525
7525
  "validation_error",
@@ -7630,6 +7630,9 @@
7630
7630
  "malformed_credential",
7631
7631
  "unknown_passkey",
7632
7632
  "invalid_passkey",
7633
+ "invalid_invite_url",
7634
+ "invite_email_mismatch",
7635
+ "invite_email_unverified",
7633
7636
  "invite_not_found",
7634
7637
  "invite_used",
7635
7638
  "already_member",
@@ -36812,7 +36815,15 @@
36812
36815
  },
36813
36816
  "redirect_uri": {
36814
36817
  "type": "string",
36815
- "description": "Where to redirect the invitee after a successful accept. Defaults to `/`."
36818
+ "description": "Where `/invite-verify` redirects the invitee after a successful accept. Must be at an origin of one of this tenant's client callbacks or `callback_hostnames`."
36819
+ },
36820
+ "inviter_user_id": {
36821
+ "type": "string",
36822
+ "description": "The user who is inviting, when you call on their behalf with a machine token. Shown in the invite email. Must be a user of this tenant; defaults to the token's own user."
36823
+ },
36824
+ "accept_url": {
36825
+ "type": "string",
36826
+ "description": "Your own page for accepting the invite. The emailed link becomes `<accept_url>#<token>`; the page reads the token from the fragment, signs the user in, and calls `POST /team/invite/accept` with the token and the signed-in user's id. Must be at an origin of one of this tenant's client callbacks or `callback_hostnames`. Without it the link is auth's own `/invite-verify`."
36816
36827
  }
36817
36828
  },
36818
36829
  "additionalProperties": false,
@@ -36952,7 +36963,7 @@
36952
36963
  }
36953
36964
  },
36954
36965
  "400": {
36955
- "description": "`invite_used` — The invite was already accepted.\n\n`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.",
36966
+ "description": "`invite_used` — The invite was already accepted.\n\n`invalid_role` — The role belongs to another account.\n\n`invalid_invite_url` — An invite's `accept_url` or `redirect_uri` is not an http(s) URL at an origin of this tenant's client callbacks or `callback_hostnames`, or it carries a fragment.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
36956
36967
  "content": {
36957
36968
  "application/json": {
36958
36969
  "schema": {
@@ -36968,6 +36979,7 @@
36968
36979
  "enum": [
36969
36980
  "invite_used",
36970
36981
  "invalid_role",
36982
+ "invalid_invite_url",
36971
36983
  "validation_error"
36972
36984
  ]
36973
36985
  }
@@ -37763,6 +37775,255 @@
37763
37775
  }
37764
37776
  }
37765
37777
  },
37778
+ "/team/invite/accept": {
37779
+ "post": {
37780
+ "operationId": "team/acceptInvite",
37781
+ "summary": "Accept a team invite for a signed-in user",
37782
+ "tags": [
37783
+ "team"
37784
+ ],
37785
+ "description": "Accepts the invite whose secret is `token` on behalf of `user_id`, an existing user of this tenant. The user's email must be verified and equal to the invited one (`invite_email_mismatch` otherwise, with the invited address masked in `details`). Never creates a user. Use it from the page you gave as the invite's `accept_url`.",
37786
+ "requestBody": {
37787
+ "required": true,
37788
+ "content": {
37789
+ "application/json": {
37790
+ "schema": {
37791
+ "type": "object",
37792
+ "required": [
37793
+ "token",
37794
+ "user_id"
37795
+ ],
37796
+ "properties": {
37797
+ "token": {
37798
+ "type": "string",
37799
+ "minLength": 1,
37800
+ "description": "The invite secret, as it arrived in the fragment of the `accept_url` link."
37801
+ },
37802
+ "user_id": {
37803
+ "type": "string",
37804
+ "description": "The user accepting — the one signed in on your page. Must exist in this tenant and have the invited email, verified."
37805
+ }
37806
+ },
37807
+ "additionalProperties": false
37808
+ }
37809
+ }
37810
+ }
37811
+ },
37812
+ "security": [
37813
+ {
37814
+ "bearerAuth": []
37815
+ }
37816
+ ],
37817
+ "responses": {
37818
+ "200": {
37819
+ "description": "Default Response",
37820
+ "content": {
37821
+ "application/json": {
37822
+ "schema": {
37823
+ "type": "object",
37824
+ "required": [
37825
+ "status",
37826
+ "team_id",
37827
+ "user_id",
37828
+ "roles"
37829
+ ],
37830
+ "properties": {
37831
+ "status": {
37832
+ "anyOf": [
37833
+ {
37834
+ "type": "string",
37835
+ "enum": [
37836
+ "accepted"
37837
+ ]
37838
+ },
37839
+ {
37840
+ "type": "string",
37841
+ "enum": [
37842
+ "already_member"
37843
+ ]
37844
+ }
37845
+ ],
37846
+ "description": "`accepted` when the user joined now; `already_member` when they already were (the invite is spent either way)."
37847
+ },
37848
+ "team_id": {
37849
+ "type": "string"
37850
+ },
37851
+ "user_id": {
37852
+ "type": "string"
37853
+ },
37854
+ "roles": {
37855
+ "type": "array",
37856
+ "items": {
37857
+ "type": "string"
37858
+ }
37859
+ }
37860
+ },
37861
+ "additionalProperties": false
37862
+ }
37863
+ }
37864
+ }
37865
+ },
37866
+ "400": {
37867
+ "description": "`invalid_ticket` — The ticket does not exist or is not the kind this endpoint accepts.\n\n`ticket_used` — The ticket was already consumed.\n\n`ticket_expired` — The ticket is past its expiry.\n\n`validation_error` — The body, query or path failed schema validation. `details.issues` lists each failing field.",
37868
+ "content": {
37869
+ "application/json": {
37870
+ "schema": {
37871
+ "allOf": [
37872
+ {
37873
+ "$ref": "#/components/schemas/ErrorResponse"
37874
+ },
37875
+ {
37876
+ "type": "object",
37877
+ "properties": {
37878
+ "error_code": {
37879
+ "type": "string",
37880
+ "enum": [
37881
+ "invalid_ticket",
37882
+ "ticket_used",
37883
+ "ticket_expired",
37884
+ "validation_error"
37885
+ ]
37886
+ }
37887
+ }
37888
+ }
37889
+ ]
37890
+ }
37891
+ }
37892
+ }
37893
+ },
37894
+ "401": {
37895
+ "description": "`unauthorized` — The request carries no valid credentials.",
37896
+ "content": {
37897
+ "application/json": {
37898
+ "schema": {
37899
+ "allOf": [
37900
+ {
37901
+ "$ref": "#/components/schemas/ErrorResponse"
37902
+ },
37903
+ {
37904
+ "type": "object",
37905
+ "properties": {
37906
+ "error_code": {
37907
+ "type": "string",
37908
+ "enum": [
37909
+ "unauthorized"
37910
+ ]
37911
+ }
37912
+ }
37913
+ }
37914
+ ]
37915
+ }
37916
+ }
37917
+ }
37918
+ },
37919
+ "403": {
37920
+ "description": "`invite_email_mismatch` — The invite is for another email address than the accepting user's. `details.invited_email` shows it masked.\n\n`invite_email_unverified` — The accepting user's email matches the invite but is not verified yet.\n\n`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.",
37921
+ "content": {
37922
+ "application/json": {
37923
+ "schema": {
37924
+ "allOf": [
37925
+ {
37926
+ "$ref": "#/components/schemas/ErrorResponse"
37927
+ },
37928
+ {
37929
+ "type": "object",
37930
+ "properties": {
37931
+ "error_code": {
37932
+ "type": "string",
37933
+ "enum": [
37934
+ "invite_email_mismatch",
37935
+ "invite_email_unverified",
37936
+ "forbidden",
37937
+ "insufficient_scope",
37938
+ "user_suspended"
37939
+ ]
37940
+ }
37941
+ }
37942
+ }
37943
+ ]
37944
+ }
37945
+ }
37946
+ }
37947
+ },
37948
+ "404": {
37949
+ "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).",
37950
+ "content": {
37951
+ "application/json": {
37952
+ "schema": {
37953
+ "allOf": [
37954
+ {
37955
+ "$ref": "#/components/schemas/ErrorResponse"
37956
+ },
37957
+ {
37958
+ "type": "object",
37959
+ "properties": {
37960
+ "error_code": {
37961
+ "type": "string",
37962
+ "enum": [
37963
+ "not_found",
37964
+ "account_not_found"
37965
+ ]
37966
+ }
37967
+ }
37968
+ }
37969
+ ]
37970
+ }
37971
+ }
37972
+ }
37973
+ },
37974
+ "429": {
37975
+ "description": "`too_many_requests` — Rate limit exceeded. Honour the `Retry-After` header.",
37976
+ "content": {
37977
+ "application/json": {
37978
+ "schema": {
37979
+ "allOf": [
37980
+ {
37981
+ "$ref": "#/components/schemas/ErrorResponse"
37982
+ },
37983
+ {
37984
+ "type": "object",
37985
+ "properties": {
37986
+ "error_code": {
37987
+ "type": "string",
37988
+ "enum": [
37989
+ "too_many_requests"
37990
+ ]
37991
+ }
37992
+ }
37993
+ }
37994
+ ]
37995
+ }
37996
+ }
37997
+ }
37998
+ },
37999
+ "500": {
38000
+ "description": "`internal_error` — Unexpected server error. Retry later; the request id is logged.",
38001
+ "content": {
38002
+ "application/json": {
38003
+ "schema": {
38004
+ "allOf": [
38005
+ {
38006
+ "$ref": "#/components/schemas/ErrorResponse"
38007
+ },
38008
+ {
38009
+ "type": "object",
38010
+ "properties": {
38011
+ "error_code": {
38012
+ "type": "string",
38013
+ "enum": [
38014
+ "internal_error"
38015
+ ]
38016
+ }
38017
+ }
38018
+ }
38019
+ ]
38020
+ }
38021
+ }
38022
+ }
38023
+ }
38024
+ }
38025
+ }
38026
+ },
37766
38027
  "/api": {
37767
38028
  "get": {
37768
38029
  "operationId": "api/list",