Organization API: Customer Authentication: Auth
The 32 Organization API operations for auth.
Part of Customer Authentication.
Operations
Section titled Operations| Method | Path | Summary |
|---|---|---|
| POST | /api/v1/orgs/{org_slug}/auth/checkout-session |
Give a visitor at checkout a customer session from their email address |
| POST | /api/v1/orgs/{org_slug}/auth/code/request |
Email the customer a 6-digit code to sign in with |
| POST | /api/v1/orgs/{org_slug}/auth/code/verify |
Sign in with the code from the email |
| POST | /api/v1/orgs/{org_slug}/auth/forgot-password |
Email a link that sets a new password, to the address of an active account |
| POST | /api/v1/orgs/{org_slug}/auth/login |
Password sign-in with an email address or a username |
| POST | /api/v1/orgs/{org_slug}/auth/logout |
Revoke the current access token (and its refresh twin if present) |
| GET | /api/v1/orgs/{org_slug}/auth/me |
The signed-in customer |
| PATCH | /api/v1/orgs/{org_slug}/auth/me |
Update customer profile |
| POST | /api/v1/orgs/{org_slug}/auth/me/new-account-notice/dismiss |
Hide the "this is a new account" notice for good |
| GET | /api/v1/orgs/{org_slug}/auth/mfa |
Customer MFA status |
| POST | /api/v1/orgs/{org_slug}/auth/mfa/disable |
Turn the factor off with a current code, and with the password when the account has one |
| POST | /api/v1/orgs/{org_slug}/auth/mfa/enable |
Customer MFA enable |
| POST | /api/v1/orgs/{org_slug}/auth/mfa/recovery-codes |
Customer MFA regenerate recovery codes |
| POST | /api/v1/orgs/{org_slug}/auth/mfa/setup |
Customer MFA setup |
| POST | /api/v1/orgs/{org_slug}/auth/mfa/verify |
Customer MFA verify |
| GET | /api/v1/orgs/{org_slug}/auth/oauth/connections |
List OAuth connections |
| DELETE | /api/v1/orgs/{org_slug}/auth/oauth/connections/{provider} |
Disconnect OAuth provider |
| GET | /api/v1/orgs/{org_slug}/auth/oauth/providers |
Public: which social providers the storefront offers |
| POST | /api/v1/orgs/{org_slug}/auth/oauth/set-password |
OAuth set password |
| GET | /api/v1/orgs/{org_slug}/auth/oauth/{provider}/authorize |
OAuth authorize |
| GET | /api/v1/orgs/{org_slug}/auth/oauth/{provider}/callback |
Finish a provider sign-in and send the customer back to the storefront |
| POST | /api/v1/orgs/{org_slug}/auth/reauth/code |
Request reauth code |
| POST | /api/v1/orgs/{org_slug}/auth/reauth/code/verify |
Trade the code from POST /auth/reauth/code for a reauthtoken |
| POST | /api/v1/orgs/{org_slug}/auth/refresh |
Customer refresh token |
| POST | /api/v1/orgs/{org_slug}/auth/register |
Self-service customer signup for the organization's storefront |
| POST | /api/v1/orgs/{org_slug}/auth/resend-verification |
Send another confirmation link to the signed-in customer's own address |
| POST | /api/v1/orgs/{org_slug}/auth/reset-password |
Set a new password with the token from a password reset email |
| POST | /api/v1/orgs/{org_slug}/auth/reset-password/resend |
Email a new password link to the customer a link was made for |
| GET | /api/v1/orgs/{org_slug}/auth/sessions |
Where this account is currently signed in |
| POST | /api/v1/orgs/{org_slug}/auth/sessions/revoke-others |
Sign out everywhere except here |
| GET | /api/v1/orgs/{org_slug}/auth/username-available |
Whether a username can be taken at signup, and why not when it cannot |
| POST | /api/v1/orgs/{org_slug}/auth/verify-email |
Confirm an address from an emailed link |
Give a visitor at checkout a customer session from their email address
Section titled Give a visitor at checkout a customer session from their email addressPOST /api/v1/orgs/{org_slug}/auth/checkout-session
Give a visitor at checkout a customer session from their email address.
- An address with no account and
verify: false: we make the account with no password and the details sent, and answer 201{"status": "created", access_token, refresh_token, expires_in, customer}. We email a link to choose a password (account_set_password, good for 3 days). When your organization requires two-factor authentication, the answer holds the set-up step (mfa_tokenwithmfa_setup_required) instead of a session. - An address with no account and
verify: true: we email a code and answer 200{"status": "code_sent", challenge, email_hint, expires_in, resend_after, new_account: true}.POST /auth/code/verifythen makes the account and signs the customer in. - An address that has an account: we email a code to sign in and answer
409 with
detail{"error": "account_exists", "message", challenge, email_hint, has_password, expires_in, resend_after}, and never a session. The customer gives the code toPOST /auth/code/verify, or their password toPOST /auth/login.
The names, company, country and address are saved only on an account this request makes; otherwise save them after sign-in. The first proof of the address (a code, or the password link) ends the session this request returned.
Refusals as for POST /auth/code/request, and 429 when one network
made 20 accounts here in the last hour.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Request body
Section titled Request bodyapplication/json (required)
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | yes | The customer's email address. |
first_name |
string or null | no | Saved on an account this request makes. |
last_name |
string or null | no | Saved on an account this request makes. |
company |
string or null | no | Saved on an account this request makes. |
country_code |
string or null | no | The billing country as ISO 3166-1 alpha-2, such as DE. Saved on an account this request makes. |
address |
BillingAddressBody or null | no | The billing address. Saved on an account this request makes. |
address.line1 |
string or null | no | Street and number. |
address.line2 |
string or null | no | Flat, suite or building. |
address.city |
string or null | no | City or town. |
address.postal_code |
string or null | no | Postal or ZIP code. |
address.state |
string or null | no | State, province or region. |
turnstile_token |
string or null | no | The Cloudflare Turnstile answer, needed when GET /storefront/branding reports turnstile.enabled. |
verify |
boolean | no | true to prove the address with a code before an account is made, as a free server needs. false makes the account at once. |
Responses
Section titled Responses| Status | Meaning |
|---|---|
201 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Email the customer a 6-digit code to sign in with
Section titled Email the customer a 6-digit code to sign in withPOST /api/v1/orgs/{org_slug}/auth/code/request
Email the customer a 6-digit code to sign in with.
identifier is an email address or a Minecraft username. An address
with an account gets a code to sign in to it. An address with no account
gets one too, and new_account is true: giving the code makes the
account. Both answers show where the code went in email_hint.
A name finds the active account that first bought for that player or
named it in POST /portal/players (a later customer who uses the same
name does not take it over), and then the account whose own username it
is; the code goes to that account's address. The answer for a name is the
same whether or not an account uses it: email_hint is null,
new_account is false, and challenge is always present. For a
name no account uses, no email goes out and no code can open the
challenge. Names count against the same limits as addresses, by name and
by network.
Send challenge and the code to POST /auth/code/verify. A code
works for 10 minutes and 5 tries. An address can ask for another after 30
seconds and gets at most 5 codes in 15 minutes, and one network at most
20 an hour.
Refusals: 400 for an identifier with @ that is not an address; 403 as sign-up
refuses a new address (a blocked email domain, the customer limit) and
for an account that is banned, suspended or closed; 403
turnstile_failed; 429 with Retry-After while the address or the
network must wait; 503 when we cannot send your organization's email.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Request body
Section titled Request bodyapplication/json (required)
| Field | Type | Required | Description |
|---|---|---|---|
identifier |
string | yes | The customer's email address, or a Minecraft username as the game shows it, with the prefix the network gives its edition when it has one. |
turnstile_token |
string or null | no | The Cloudflare Turnstile answer, needed when GET /storefront/branding reports turnstile.enabled. |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Fields of a 200 response:
| Field | Type | Description |
|---|---|---|
sent |
boolean | Always true for an address. For a name it is true whether or not a code went out. |
challenge |
string | Send this back with the code to POST /auth/code/verify. |
email_hint |
string or null | Where the code went, with most of the address hidden, such as s•••@example.com. null when the identifier was a player name or a username: the answer must not say whether an account uses that name. |
new_account |
boolean | true when no account has this address yet: giving the code makes one. Always false for a name. |
expires_in |
integer | Seconds the code works for: 600. |
resend_after |
integer | Seconds before another code can be asked for: 30. |
Sign in with the code from the email
Section titled Sign in with the code from the emailPOST /api/v1/orgs/{org_slug}/auth/code/verify
Sign in with the code from the email.
The answer is the one POST /auth/login gives: a session
(access_token, refresh_token, expires_in, scopes), or the
second-factor step (mfa_token with mfa_required, or
mfa_setup_required when your organization requires two-factor
authentication and the customer has not set it up).
The code proves the address. When no account has it, we make one with no
password, send your welcome email, and your webhooks receive
customer.created. The first time an account's address is proven,
every session made before ends, such as one a checkout made for an
address its owner had not proven yet. On an account with no password,
the same step removes an authenticator, which only such a session can
have added, and applies invites to share a server with the address.
Refusals: 400 with detail.error wrong_code, expired or
used, a message to show and attempts_left. The fifth wrong
code spends the challenge. 403 as for POST /auth/code/request.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Request body
Section titled Request bodyapplication/json (required)
| Field | Type | Required | Description |
|---|---|---|---|
challenge |
string | yes | The challenge from the answer that sent the code. |
code |
string | yes | The 6 digits from the email. Spaces and dashes are ignored. |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Email a link that sets a new password, to the address of an active account
Section titled Email a link that sets a new password, to the address of an active accountPOST /api/v1/orgs/{org_slug}/auth/forgot-password
Email a link that sets a new password, to the address of an active account.
The answer is always 200 {"message": "If the account exists, we issued a reset link"}, whether or not the address has an account, and the link
works for 2 hours. Only the newest forgot-password link works. It never
replaces a link we made on our own, such as the set-password button of
an account move, so a stranger who types an address cannot void the link
its owner was sent.
429 when one network has asked 10 times in an hour. One address gets 3 links an hour: later requests for it answer the same 200 and send nothing. Both counts include addresses that have no account, so neither says whether an address has one.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Request body
Section titled Request bodyapplication/json (required)
| Field | Type | Required |
|---|---|---|
email |
string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Password sign-in with an email address or a username
Section titled Password sign-in with an email address or a usernamePOST /api/v1/orgs/{org_slug}/auth/login
Password sign-in with an email address or a username.
Send identifier (or email or username) and password. We answer 200
with a session (access_token and refresh_token), or with the
second-factor step (mfa_token) when the account or the organization asks
for one.
A name that is no customer's username may be a Minecraft player name:
it signs in the active customer who first bought for that player or
named it in POST /portal/players. A later customer who uses the same
name does not take it over.
The password is checked by check_password: a hash imported from a
WordPress store ($wp$, phpass $P$ or $H$, plain MD5) opens the
account like a bcrypt one, and is replaced by a bcrypt hash of the same
password in this sign-in's transaction.
Every refusal has detail, a sentence to show, and code, a word to act on:
- 401
invalid_credentials: the login or the password is wrong. - 401
no_password: the account has no password yet.POST /auth/forgot-passwordemails the link that sets one, and a storefront with a code form (POST /auth/code/request) can offer that as well. - 429
rate_limited, withRetry-After: 300: the old site asked the person to wait before another try. - 401
legacy_unavailable: the organization checks old accounts at sign-in, and the old site did not answer. The password may still be right; try again in a minute.
While the organization moves accounts from its previous website and asks
that site at sign-in, a login this organization does not know, or a
password that fails here, is checked there too. A password longer than 256
characters is never sent. A password that opens an old account answers 200
with legacy instead of 401: action is move with a ticket for the
move page, or old_site with a url on the old site's own domain that
signs the person in there, because that account still has servers there.
An old username of an account that already moved to this organization
signs in to that account here, with its password.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Request body
Section titled Request bodyapplication/json (required)
| Field | Type | Required |
|---|---|---|
identifier |
string or null | no |
email |
string or null | no |
username |
string or null | no |
password |
string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Revoke the current access token (and its refresh twin if present)
Section titled Revoke the current access token (and its refresh twin if present)POST /api/v1/orgs/{org_slug}/auth/logout
Revoke the current access token (and its refresh twin if present).
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
The signed-in customer
Section titled The signed-in customerGET /api/v1/orgs/{org_slug}/auth/me
The signed-in customer.
support_session is set when the token is a support session that your
team or Coritan support opened as the customer: its mode
(read_only, servers or full) and expires_at. A storefront
that receives a token at /auth/callback with impersonated=1 reads
this to confirm it is a support session before it signs the visitor in,
and shows whose account is on screen until the session ends.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Fields of a 200 response:
| Field | Type | Description |
|---|---|---|
id |
integer | |
org_id |
integer | |
email |
string | |
first_name |
string or null | |
last_name |
string or null | |
chat_handle |
string or null | |
chat_handle_locked_for |
integer | |
company |
string or null | |
phone |
string or null | |
status |
string | |
credit_balance |
string | |
currency |
string | |
country_code |
string or null | |
currency_source |
string | |
custom_fields |
object or null | |
last_login_at |
string (date-time) or null | |
created_at |
string (date-time) or null | |
updated_at |
string (date-time) or null | |
has_password |
boolean | |
staff_member |
boolean | |
support_session |
SupportSessionInfo or null | Set when the token is a support session the organization's team or Coritan support opened as this customer (read_only, servers or full), null for the customer's own sign-in. A storefront that receives a support session shows whose account it is and that the session ends. |
support_session.mode |
string | read_only, servers (an organization's customer only) or full. |
support_session.expires_at |
string (date-time) or null | When the session ends. It cannot be renewed. |
new_account_notice |
object or null | Set while an account that a Google or Discord sign-in created has no services and the customer has not dismissed it: {provider} is how this account was created. The storefront says this is a new account and that servers bought before are on the account they used then. POST /{org_slug}/auth/me/new-account-notice/dismiss clears it. |
email_verified |
boolean | |
avatar_url |
string or null | |
marketing_consent |
boolean | Whether the customer agreed to marketing email. Mail about their own orders and services needs no agreement. |
marketing_consent_at |
string (date-time) or null | When the customer gave that answer. |
marketing_consent_source |
string or null | Where the answer came from: signup, account, newsletter, staff or unsubscribe. |
credit_currency |
string | What credit_balance is held in: the wallet's currency (US dollars), whatever currency the customer is billed in. |
Update customer profile
Section titled Update customer profilePATCH /api/v1/orgs/{org_slug}/auth/me
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Request body
Section titled Request bodyapplication/json (required)
| Field | Type | Required | Description |
|---|---|---|---|
first_name |
string or null | no | |
last_name |
string or null | no | |
chat_handle |
string or null | no | |
company |
string or null | no | |
phone |
string or null | no | |
password |
string or null | no | |
email |
string or null | no | |
current_password |
string or null | no | |
marketing_consent |
boolean or null | no | The customer's own choice about marketing email, stored with the time and the source account. true also takes their address off the organization's suppression list. Left out, the choice stays as it is. |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Fields of a 200 response:
| Field | Type | Description |
|---|---|---|
id |
integer | |
org_id |
integer | |
email |
string | |
first_name |
string or null | |
last_name |
string or null | |
chat_handle |
string or null | |
chat_handle_locked_for |
integer | |
company |
string or null | |
phone |
string or null | |
status |
string | |
credit_balance |
string | |
currency |
string | |
country_code |
string or null | |
currency_source |
string | |
custom_fields |
object or null | |
last_login_at |
string (date-time) or null | |
created_at |
string (date-time) or null | |
updated_at |
string (date-time) or null | |
has_password |
boolean | |
staff_member |
boolean | |
support_session |
SupportSessionInfo or null | Set when the token is a support session the organization's team or Coritan support opened as this customer (read_only, servers or full), null for the customer's own sign-in. A storefront that receives a support session shows whose account it is and that the session ends. |
support_session.mode |
string | read_only, servers (an organization's customer only) or full. |
support_session.expires_at |
string (date-time) or null | When the session ends. It cannot be renewed. |
new_account_notice |
object or null | Set while an account that a Google or Discord sign-in created has no services and the customer has not dismissed it: {provider} is how this account was created. The storefront says this is a new account and that servers bought before are on the account they used then. POST /{org_slug}/auth/me/new-account-notice/dismiss clears it. |
email_verified |
boolean | |
avatar_url |
string or null | |
marketing_consent |
boolean | Whether the customer agreed to marketing email. Mail about their own orders and services needs no agreement. |
marketing_consent_at |
string (date-time) or null | When the customer gave that answer. |
marketing_consent_source |
string or null | Where the answer came from: signup, account, newsletter, staff or unsubscribe. |
credit_currency |
string | What credit_balance is held in: the wallet's currency (US dollars), whatever currency the customer is billed in. |
Hide the "this is a new account" notice for good
Section titled Hide the "this is a new account" notice for goodPOST /api/v1/orgs/{org_slug}/auth/me/new-account-notice/dismiss
Hide the "this is a new account" notice for good. Refused in a support session: it is the customer's to dismiss.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
204 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Customer MFA status
Section titled Customer MFA statusGET /api/v1/orgs/{org_slug}/auth/mfa
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Turn the factor off with a current code, and with the password when the account has one
Section titled Turn the factor off with a current code, and with the password when the account has onePOST /api/v1/orgs/{org_slug}/auth/mfa/disable
Turn the factor off with a current code, and with the password when the account has one. Refused while the brand requires a factor of everyone.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Request body
Section titled Request bodyapplication/json (required)
| Field | Type | Required |
|---|---|---|
password |
string or null | no |
code |
string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Customer MFA enable
Section titled Customer MFA enablePOST /api/v1/orgs/{org_slug}/auth/mfa/enable
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Request body
Section titled Request bodyapplication/json (required)
| Field | Type | Required |
|---|---|---|
code |
string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Customer MFA regenerate recovery codes
Section titled Customer MFA regenerate recovery codesPOST /api/v1/orgs/{org_slug}/auth/mfa/recovery-codes
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Request body
Section titled Request bodyapplication/json (required)
| Field | Type | Required |
|---|---|---|
code |
string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Customer MFA setup
Section titled Customer MFA setupPOST /api/v1/orgs/{org_slug}/auth/mfa/setup
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Customer MFA verify
Section titled Customer MFA verifyPOST /api/v1/orgs/{org_slug}/auth/mfa/verify
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Request body
Section titled Request bodyapplication/json (required)
| Field | Type | Required |
|---|---|---|
code |
string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
List OAuth connections
Section titled List OAuth connectionsGET /api/v1/orgs/{org_slug}/auth/oauth/connections
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
A 200 response is a list; each item has these fields:
| Field | Type |
|---|---|
[].provider |
string |
[].provider_username |
string or null |
[].provider_email |
string or null |
[].provider_avatar |
string or null |
[].created_at |
string (date-time) or null |
[].account_created_at |
string (date-time) or null |
Disconnect OAuth provider
Section titled Disconnect OAuth providerDELETE /api/v1/orgs/{org_slug}/auth/oauth/connections/{provider}
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
provider |
path | string | yes |
org_slug |
path | string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Public: which social providers the storefront offers
Section titled Public: which social providers the storefront offersGET /api/v1/orgs/{org_slug}/auth/oauth/providers
Public: which social providers the storefront offers.
A provider is listed when Coritan has it configured and the organization's settings do not leave it out.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Fields of a 200 response:
| Field | Type |
|---|---|
providers |
array of string |
OAuth set password
Section titled OAuth set passwordPOST /api/v1/orgs/{org_slug}/auth/oauth/set-password
Set a first password on an account that has none, such as one made with a social account, a code or at checkout.
Needs the signed-in customer's token. The address must be proven first
(email_verified in GET /auth/me): a session can exist for an address
its owner has not confirmed, such as the one POST /auth/checkout-session
returns, and a password chosen there would still open the account after the
owner confirmed the address. 403 with detail.error email_unproven
and a message to show says so. POST /auth/forgot-password emails a
link that proves the address and sets the password in one step. 400 when the
account already has a password, 422 for a password that is too weak.
The answer is {"message": "Password set successfully"}.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Request body
Section titled Request bodyapplication/json (required)
| Field | Type | Required | Description |
|---|---|---|---|
password |
string | yes | The new password: 8 to 128 characters with a lowercase letter, an uppercase letter, a digit and a symbol. |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
OAuth authorize
Section titled OAuth authorizeGET /api/v1/orgs/{org_slug}/auth/oauth/{provider}/authorize
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
provider |
path | string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Finish a provider sign-in and send the customer back to the storefront
Section titled Finish a provider sign-in and send the customer back to the storefrontGET /api/v1/orgs/{org_slug}/auth/oauth/{provider}/callback
Finish a provider sign-in and send the customer back to the storefront.
The provider's redirect lands here with a code. We answer 302 to the
storefront's /auth/callback page with the result in the URL fragment:
access_token, refresh_token and expires_in, or mfa_token and its
flags, or error. new_account=true marks an account this sign-in made, and
a link a signed-in customer started ends with linked=true and provider.
A provider only vouches for an address it says it checked. We make an
account from a sign-in only when it did, and we never link a provider to an
account the customer did not sign in to. These error values say what to
tell the customer:
account_exists_login_required: an account with this address has a password. The customer signs in with it, then connects the provider in their account settings.account_exists_no_password: an account with this address has no password. The customer asks for a sign-in code or a password link by email, then connects the provider in their account settings.email_unverified_by_provider: the provider did not say it had checked the address, so we made no account. The customer confirms the address with the provider and tries again, or signs up with email.no_email_from_provider: the provider shared no address.email_unproven: a customer tried to connect a provider to an account whose address nobody has proven. They confirm the address by email first, with a sign-in code, the confirmation link or a password link.provider_already_linked_to_another_account: another customer has linked this provider account.discord_link_limit, withretry_at: this Discord account moved between customers too often.retry_atis when it can be linked again.account_disabled,email_domain_blockedandcustomer_limit_reached: the account is not active, the address's domain is blocked, or the organization is at its customer limit.access_denied,invalid_state,missing_code_or_state,token_exchange_failed,no_access_token,profile_fetch_failed,provider_not_availableand the rest: the sign-in did not finish, and the customer starts it again.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
provider |
path | string | yes |
code |
query | string or null | no |
state |
query | string or null | no |
error |
query | string or null | no |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Request reauth code
Section titled Request reauth codePOST /api/v1/orgs/{org_slug}/auth/reauth/code
Email the signed-in customer a 6-digit code that confirms an action that cannot be undone, such as deleting a server or closing the account.
Needs the customer's own session. The code always goes to the address on the
account, so it works for an account with no password and no linked social
account, and the body is empty ({}). Send challenge and the code to POST /auth/reauth/code/verify, which answers with a reauth_token that the
request needing it accepts as reauth_token.
A code works for 10 minutes and 5 tries. It counts against the same limits as a sign-in code, because both go to the same inbox: the address can ask for another after 30 seconds and gets at most 5 codes in 15 minutes, and one network at most 20 an hour.
Refusals: 401 without a session; 403 for a support session opened as the
customer, and for an account that is banned, suspended or closed; 429 with
Retry-After while the address or the network must wait; 503 when we cannot
send your organization's email.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Fields of a 200 response:
| Field | Type | Description |
|---|---|---|
sent |
boolean | Always true: we emailed a code to the account's own address. |
challenge |
string | Send this back with the code to POST /auth/reauth/code/verify. |
email_hint |
string | Where the code went, with most of the address hidden, such as s•••@example.com. |
expires_in |
integer | Seconds the code works for: 600. |
resend_after |
integer | Seconds before another code can be asked for: 30. |
Trade the code from POST /auth/reauth/code for a reauthtoken
Section titled Trade the code from POST /auth/reauth/code for a reauthtokenPOST /api/v1/orgs/{org_slug}/auth/reauth/code/verify
Trade the code from POST /auth/reauth/code for a reauth_token.
Needs the same customer's session. The answer is {"reauth_token", "expires_in": 300}: send the token as reauth_token with the request that
needs a confirmation, within 5 minutes. A code works once.
Refusals: 400 with detail.error wrong_code, expired or used, a
message to show and attempts_left. The fifth wrong code spends the
challenge. A code that was sent for signing in, or to another customer, reads
as expired, and so does a challenge your organization did not issue. 401
without a session; 403 for a support session; 429 with Retry-After after
many wrong codes from one network.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Request body
Section titled Request bodyapplication/json (required)
| Field | Type | Required | Description |
|---|---|---|---|
challenge |
string | yes | The challenge from the answer that sent the code. |
code |
string | yes | The 6 digits from the email. Spaces and dashes are ignored. |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Fields of a 200 response:
| Field | Type | Description |
|---|---|---|
reauth_token |
string | Proof that the customer just confirmed it is them. Send it as reauth_token with the request that needs it, such as POST /portal/account/close. |
expires_in |
integer | Seconds the token works for: 300. |
Customer refresh token
Section titled Customer refresh tokenPOST /api/v1/orgs/{org_slug}/auth/refresh
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Fields of a 200 response:
| Field | Type |
|---|---|
access_token |
string |
refresh_token |
string |
token_type |
string |
expires_in |
integer |
scopes |
array of string or null |
customer_id |
integer or null |
Self-service customer signup for the organization's storefront
Section titled Self-service customer signup for the organization's storefrontPOST /api/v1/orgs/{org_slug}/auth/register
Self-service customer signup for the organization's storefront.
Send email and password; username, the names, company, phone,
turnstile_token and marketing_consent are optional. We answer 201 with
a session (access_token and refresh_token), or with the second-factor
step when the organization asks every customer for one. The address starts
unconfirmed: nothing waits for the confirmation link, and we email it.
Refusals carry a code beside the detail sentence: 409
email_taken when an account here has the address, 409 username_taken
for a name that is in use, 403 email_banned for an address that cannot
register again, and 403 customer_limit_reached when the organization is
full. A sentence in detail stays the same for each, so older pages keep
working.
While the organization moves accounts from its previous website and asks
that site at sign-in, we also ask it whether the address or the username
belongs to an account there. When it does, we answer 409
account_on_old_site: the person should sign in with the old username
and password, and we move the account for them. When the old site does not
answer, the sign-up goes ahead.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Request body
Section titled Request bodyapplication/json (required)
| Field | Type | Required | Description |
|---|---|---|---|
email |
string | yes | |
password |
string | yes | |
username |
string or null | no | |
first_name |
string or null | no | |
last_name |
string or null | no | |
company |
string or null | no | |
phone |
string or null | no | |
turnstile_token |
string or null | no | |
marketing_consent |
boolean or null | no | Whether the customer agreed to marketing email. It is false unless the sign-up form sends true. An address on the organization's suppression list is still not mailed. |
Responses
Section titled Responses| Status | Meaning |
|---|---|
201 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Send another confirmation link to the signed-in customer's own address
Section titled Send another confirmation link to the signed-in customer's own addressPOST /api/v1/orgs/{org_slug}/auth/resend-verification
Send another confirmation link to the signed-in customer's own address.
Authenticated rather than taking an email in the body: an anonymous version would let anyone post mail to any address on this org's behalf, and there is nothing to gain from it when the customer can already sign in.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Set a new password with the token from a password reset email
Section titled Set a new password with the token from a password reset emailPOST /api/v1/orgs/{org_slug}/auth/reset-password
Set a new password with the token from a password reset email.
The token comes from the forgot-password email (it works for 2 hours),
from the email that asks a customer to choose a password for an account
made at checkout (3 days), from the set-password button of an account
moved from a previous site (30 days) or from the new link that
POST /auth/reset-password/resend sends (7 days). It works once. The
reset signs the customer out everywhere, and the answer is {"message": "Password updated", "email"} with the account's address, so the page can
sign the customer in with the new password straight away.
A link that names the customer carries id beside token. Send both:
the token is then checked on that customer alone. Without id the token
is looked up among the organization's customers, which is how links sent
before id existed still work; that lookup is limited to 10 requests an
hour from one network (429).
400 Invalid reset token for a token that is wrong, was replaced by a
newer one or was used already, and for an id that is unknown, belongs
to another organization or is not an active customer: the answer is the
same for all of them. A genuine token past its time answers 400
Reset token expired; when the request carried id the detail is
{"code": "reset_link_expired", "message": "Reset token expired"}, and
the page can offer POST /auth/reset-password/resend for a new link.
The link went to the account's address, so using it proves the address. When nobody had proven it before, we mark it confirmed, remove an authenticator from an account that had no password (only a session nobody had vouched for can have added one), remove the sign-in links to other services that an account move carried to it, and apply invites to share a server with the address.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Request body
Section titled Request bodyapplication/json (required)
| Field | Type | Required |
|---|---|---|
id |
integer or null | no |
token |
string | yes |
password |
string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Email a new password link to the customer a link was made for
Section titled Email a new password link to the customer a link was made forPOST /api/v1/orgs/{org_slug}/auth/reset-password/resend
Email a new password link to the customer a link was made for.
Send the id and token from the link that does not work, expired or
not. When that token is one we hold for an active customer, we issue a
new link that works for 7 days, replace every older link of the customer
with it (only the newest link works) and email it to the address on the
account. The page offers this when POST /auth/reset-password answers
reset_link_expired.
The answer is always 200 {"message": "If that link was ours, we sent a new one to the address on file."}, whether or not a link went out, so it
says nothing about which customers exist. When the organization cannot
send email, nothing is sent and the old link stays as it was.
429 when the network has asked 20 times in an hour, or when the customer has had 3 new links in an hour. The customer's budget counts only requests with the right token, so someone who knows an id cannot use it up.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Request body
Section titled Request bodyapplication/json (required)
| Field | Type | Required |
|---|---|---|
id |
integer | yes |
token |
string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Where this account is currently signed in
Section titled Where this account is currently signed inGET /api/v1/orgs/{org_slug}/auth/sessions
Where this account is currently signed in.
The token row already records the address and user agent it was issued to,
so a customer can see a session they do not recognise without us keeping a
second log of it. Tokens are never returned, only the hash we match the
caller's own session on. country_code is where the address places
(see _session_countries), or null when nothing can place it, so the
page can say "Germany · 203.0.113.9" rather than an address alone.
Support sessions are labelled rather than hidden. An admin looking at an account shows up here as a sign-in from an address the customer will not recognise, and the honest answer to that is to say what it is.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Sign out everywhere except here
Section titled Sign out everywhere except herePOST /api/v1/orgs/{org_slug}/auth/sessions/revoke-others
Sign out everywhere except here.
The one action worth having after a shared or stolen laptop, and it must not log the customer out of the browser asking for it, so the presented token is the one row left alone.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Whether a username can be taken at signup, and why not when it cannot
Section titled Whether a username can be taken at signup, and why not when it cannotGET /api/v1/orgs/{org_slug}/auth/username-available
Whether a username can be taken at signup, and why not when it cannot.
Public, because the signup form asks while somebody is still typing and has no session yet. It gives away nothing the community pages do not: a handle is the name everyone already sees above every message its owner posts. Answers rather than raising, so a 422 per keystroke is not the shape of it.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
username |
query | string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |
Confirm an address from an emailed link
Section titled Confirm an address from an emailed linkPOST /api/v1/orgs/{org_slug}/auth/verify-email
Confirm an address from an emailed link.
Public, because the customer may follow the link in a browser that is not
signed in. The token is the credential. The first time an address is
confirmed, every session the account had before ends, such as one a
checkout handed to a stranger, and invites to share a server with the
address now apply (settle_first_proof). Following the link again changes
nothing. The answer is {"verified": true, "email"}; 400 for a link
that is not valid, has expired or was sent to another address.
Parameters
Section titled Parameters| Name | In | Type | Required |
|---|---|---|---|
org_slug |
path | string | yes |
Request body
Section titled Request bodyapplication/json (required)
| Field | Type | Required |
|---|---|---|
customer_id |
integer | yes |
token |
string | yes |
Responses
Section titled Responses| Status | Meaning |
|---|---|
200 |
Success. |
422 |
The request is not valid. detail lists each problem. |