Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

HTTP endpoints

Every HTTP route the server mounts, apart from the admin API (/admin/...), which has its own reference. Paths are relative to PUBLIC_URL; examples use https://id.example.com and the tenant acme, whose issuer is https://id.example.com/t/acme.

Tenant endpoints live under /t/{slug}. A tenant with a custom domain also answers every one of them on its own host without the prefix (https://login.acme.example/token as well as https://id.example.com/t/acme/token); both forms report the same issuer. A custom host serves that tenant only: besides the tenant’s routes it answers /healthz, /readyz, /.well-known/webfinger, /.well-known/security.txt and the tenant’s own /t/{slug}/… and /scim/v2/{slug}/… paths, and 404 for everything else (the admin API, /metrics, /docs, /openapi.json, other tenants). The router lives in api/src/lib.rs.

The Limit column names the rate-limit family a route counts against (see Rate limits, IP rules and CAPTCHA). Tenant IP rules are checked by the same guard, so routes marked — are subject to neither.

FamilyTenant settingRefusal format
tokenrate_limits.token_per_ip, token_per_clientOAuth JSON ({"error": "slow_down"})
authorizerate_limits.authorize_per_ipHTML page for /authorize, OAuth JSON for /par and /register
flowsrate_limits.flows_per_ipRFC 9457 problem document; HTML page for brokering

Global

MethodPathAuthLimitPurpose
GET/healthznone—Liveness: {"status": "ok", "version": "..."}. Never touches a dependency.
GET/readyznone—Readiness: pings Postgres and Valkey and checks the in-process event queues; 200 with {"status": "ok", "checks": {"database": "ok", "cache": "ok", "events": "ok"}}, or 503 with "status": "degraded" and the failing check as "fail" ("events": "saturated" when an event queue is 80% full).
GET/metricsBearer <METRICS_TOKEN> when that variable is set, otherwise none—Prometheus exposition (text/plain; version=0.0.4). See Observability.
GET/openapi.jsonnone—The admin and account API’s OpenAPI 3 document. Always served.
GET/docsnone—Swagger UI over /openapi.json; mounted only when DOCS_ENABLED=true.
GET/.well-known/webfingernone—Issuer discovery (OIDC Discovery 1.0 §2, RFC 7033). resource is acct:user@domain (the domain must be in a tenant’s settings.discovery.email_domains) or an issuer URL or any URL beneath it.
GET/.well-known/security.txtnone—Where to report vulnerabilities in this deployment (RFC 9116), as SECURITY_CONTACT or SECURITY_TXT configures it; 404 when neither is set. See Server configuration.

The container image’s HEALTHCHECK runs ridm-api --healthcheck inside the container, which probes /healthz at BIND_ADDR (loopback for a wildcard address), over HTTPS when TLS_CERT is set.

Tenant OpenID Connect and OAuth 2.0

MethodPathAuthLimitPurposeSpecification
GET/t/{slug}/.well-known/openid-configurationnone—Provider metadata (below). ETag, Cache-Control: public, max-age=300, must-revalidate, 304 on If-None-Match, Access-Control-Allow-Origin: *.OIDC Discovery 1.0 §3, RFC 8414
GET/t/{slug}/.well-known/jwks.jsonnone—The tenant’s published signing keys (pending, active and retiring; revoked keys are removed at once). Same caching as discovery.RFC 7517
GET, POST/t/{slug}/authorizeclient identified by client_idauthorizeAuthorization endpoint. POST takes application/x-www-form-urlencoded. Client and redirect_uri problems render an HTML error page; everything else is returned to the client’s redirect URI with state and iss.RFC 6749 §4.1, OIDC Core §3.1.2, RFC 7636, RFC 9207, RFC 9101
GET/t/{slug}/authorize/denied/{id}none (a one-time id)authorizeDelivers access_denied (with state and iss) after a cancelled, declined or refused sign-in to a client whose response mode is not plain query: a fragment, a form_post page or a signed JARM response. A query client is sent to its redirect URI directly.RFC 6749 §4.1.2.1, RFC 9207
POST/t/{slug}/parclient authenticationauthorizePushed authorization request; answers 201 {"request_uri": "urn:ietf:params:oauth:request_uri:...", "expires_in": ...} for a single use at /authorize.RFC 9126
POST/t/{slug}/tokenclient authenticationtokenToken endpoint, form-encoded only. Grants below. Optional DPoP header.RFC 6749 §3.2, RFC 8693, RFC 8628, RFC 9449, RFC 8707
POST/t/{slug}/device_authorizationclient authenticationtokenStarts the device authorization grant; the client must be allowed the device-code grant.RFC 8628 §3.1
POST/t/{slug}/bc-authorizeclient authentication (confidential clients only)tokenBackchannel authentication: names the user (login_hint or id_token_hint) and answers {"auth_req_id", "expires_in", "interval"}; the user approves in the account console. See Backchannel sign-in and FAPI 2.0.OpenID CIBA Core 1.0 §7
GET, POST/t/{slug}/userinfoaccess token, JWT or opaque (Authorization: Bearer or DPoP, or an access_token form field on POST)tokenClaims about the token’s subject; the token must carry openid. See Token claims.OIDC Core §5.3, RFC 6750
GET/t/{slug}/featuresaccess token of the tenant (Bearer or DPoP)tokenThe feature flags that are on for the token’s organization (org_id), or tenant-wide: {"features": [...], "org_id": ...}.—
POST/t/{slug}/introspectclient authentication (confidential clients only)tokenToken introspection for refresh tokens (rt_...), personal access tokens (rpat_...) and access tokens, JWT or opaque (at_...); the only way a resource server learns what an opaque token stands for. Unknown, expired, foreign or inactive tokens, and ID tokens, answer {"active": false}.RFC 7662
POST/t/{slug}/revokeclient authenticationtokenRevokes a refresh token with its whole family, or an access token: its jti is denylisted until it expires, and an opaque token’s entry is dropped. Unknown tokens still answer 200.RFC 7009
GET, POST/t/{slug}/end_sessionnone (id_token_hint recommended)—RP-initiated logout. With an id_token_hint matching the browser session the logout is immediate; otherwise the UI asks the user first.OIDC RP-Initiated Logout 1.0
GET/t/{slug}/end_session/{flow}none—State of a pending logout for the UI’s logout page (who is being signed out of, and the CSRF token).
POST/t/{slug}/end_session/confirmCSRF token of the logout flow—The user’s confirmation from the logout page. Back-channel logout tokens are sent and front-channel logout URLs (with iss and sid) are returned for the page to load.OIDC Back-Channel Logout 1.0, Front-Channel Logout 1.0
POST/t/{slug}/registernone, or an initial access token (Bearer iat_..., issued by an administrator; see Registering clients), per settings.dcr.modeauthorizeDynamic client registration. 403 access_denied when dcr.mode is disabled; 401 invalid_token when an initial access token is required and missing, unknown, revoked, expired or used up. Answers 201 with the metadata, registration_access_token and registration_client_uri.RFC 7591
GET, PUT, DELETE/t/{slug}/register/{client_id}registration access token (Bearer)authorizeRead, replace or delete a dynamically registered client.RFC 7592
GET/t/{slug}/brandingnone—Public description of the tenant for the sign-in pages: name, theme, links, locales and which sign-in options exist. Cache-Control: public, max-age=60.

Client authentication

/token, /par, /device_authorization, /bc-authorize, /introspect and /revoke authenticate the client with the method registered for it; any other method is invalid_client. A client under the FAPI 2.0 profile must use private_key_jwt with the issuer as the assertion’s aud.

MethodHow
nonepublic client: client_id in the body only (PKCE protects the code)
client_secret_basicAuthorization: Basic base64(client_id:secret)
client_secret_postclient_id and client_secret in the form body
private_key_jwtclient_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer and a client_assertion JWT signed with a key from the client’s jwks or jwks_uri

| tls_client_auth | client_id in the body, over a mutual-TLS connection with a certificate from one of the tenant’s trusted authorities that carries the client’s registered subject (Mutual TLS) | | self_signed_tls_client_auth | client_id in the body, over a mutual-TLS connection with a certificate registered in the client’s jwks or jwks_uri (x5c) |

The two mutual-TLS methods are offered when the deployment can receive client certificates (MTLS_BIND or CLIENT_CERT_HEADER); discovery then lists them, tls_client_certificate_bound_access_tokens: true and, with MTLS_PUBLIC_URL, the mtls_endpoint_aliases where they are used. client_secret_jwt is not implemented.

Grant types at /token

grant_typeNotes
authorization_codePKCE S256 only; plain is refused. Replaying a code revokes everything the first exchange produced. A code whose browser session was signed out before the exchange is refused (invalid_grant). resource may only pick among the resources named at /authorize (invalid_target otherwise).
refresh_tokenRotation on every use with reuse detection (not for a FAPI 2.0 client, which keeps its refresh token). scope and resource may narrow the original grant, never widen it (invalid_scope, invalid_target, checked before the token is spent). Without offline_access the token ends with its browser session; each refresh extends the session’s idle window.
client_credentialsTokens for the client itself, or for its service-account user when one exists. openid and offline_access are refused. With no scope, the tenant’s default scopes the client may hold.
urn:ietf:params:oauth:grant-type:device_codeRFC 8628. /device_authorization answers a device_code, a user_code (XXXX-XXXX from letters that are hard to confuse), verification_uri (the /device/ page), verification_uri_complete, expires_in (600) and interval (5). The user’s code becomes a login flow for the device’s client, so sign-in, second step, profile completion, terms and consent all apply. Polling answers authorization_pending, slow_down (polled faster than the interval, which then grows by five seconds), access_denied or expired_token until the user decides, then the tokens exactly once. Wrong user codes are limited to ten per address per ten minutes; every code leaves a device_codes audit row.
urn:openid:params:grant-type:cibaauth_req_id from /bc-authorize. The same answers as the device grant while the user decides; slow_down applies only to a request still undecided, so a pinged client collects at once. The tokens are handed over once.
urn:ietf:params:oauth:grant-type:token-exchangesubject_token (type urn:ietf:params:oauth:token-type:access_token or ...:jwt; an opaque at_... token must be sent as access_token), optional actor_token (same rule), audience, resource, scope. No refresh token; the result reports issued_token_type. A client registered for opaque tokens may not request ...:jwt.

A client may only use the grants in its allowed_grants; a known grant it is not allowed is unauthorized_client, an unknown one unsupported_grant_type. Audiences come from resource parameters (RFC 8707; token exchange also takes audience), or from the client’s allowed_audiences when none is requested, plus the resource server of any requested scope bound to one; see Resource servers, scopes and permissions and Token claims.

The response is {"access_token", "token_type", "expires_in", "refresh_token"?, "id_token"?, "scope"?, "issued_token_type"?} with Cache-Control: no-store. token_type is DPoP when a DPoP proof bound the tokens, otherwise Bearer (a certificate-bound token is a Bearer token, checked against the connection’s certificate).

Discovery document

The discovery document advertises exactly what the build implements (api/src/oidc/discovery.rs). Endpoints are {issuer}/authorize, /token, /.well-known/jwks.json, /userinfo, /introspect, /revoke, /end_session, /par, /device_authorization and /bc-authorize (backchannel_authentication_endpoint); registration_endpoint ({issuer}/register) appears only when the tenant’s dcr.mode is not disabled.

MemberValue
scopes_supportedthe tenant’s scopes (standard plus custom)
response_types_supportedcode
response_modes_supportedquery, fragment, jwt, query.jwt, fragment.jwt, form_post.jwt
grant_types_supportedauthorization_code, refresh_token, client_credentials, urn:ietf:params:oauth:grant-type:device_code, urn:ietf:params:oauth:grant-type:token-exchange, urn:openid:params:grant-type:ciba
backchannel_token_delivery_modes_supportedpoll, ping
backchannel_user_code_parameter_supportedfalse
subject_types_supportedpublic, pairwise
id_token_signing_alg_values_supportedRS256, RS384, RS512, ES256, EdDSA
id_token_encryption_alg_values_supportedRSA-OAEP-256, RSA-OAEP
id_token_encryption_enc_values_supportedA256GCM, A128GCM
token_endpoint_auth_methods_supported (also introspection_... and revocation_...)none, client_secret_basic, client_secret_post, private_key_jwt, and tls_client_auth, self_signed_tls_client_auth when mutual TLS is configured
tls_client_certificate_bound_access_tokenstrue when mutual TLS is configured, absent otherwise (RFC 8705 §3.3)
mtls_endpoint_aliaseswith MTLS_PUBLIC_URL: token_endpoint, userinfo_endpoint, introspection_endpoint, revocation_endpoint, pushed_authorization_request_endpoint, device_authorization_endpoint, backchannel_authentication_endpoint under {MTLS_PUBLIC_URL}/t/{slug} (RFC 8705 §5)
token_endpoint_auth_signing_alg_values_supportedRS256, RS384, RS512, ES256, EdDSA
request_object_signing_alg_values_supportedthe same five
authorization_signing_alg_values_supported (JARM)the same five
dpop_signing_alg_values_supportedES256, ES384, RS256, RS384, RS512, PS256, PS384, PS512, EdDSA: the algorithms the DPoP verifier accepts
code_challenge_methods_supportedS256
acr_values_supportedurn:ridm:acr:single, urn:ridm:acr:mfa
claims_supportedsub, iss, aud, exp, iat, auth_time, nonce, acr, amr, azp, sid, name, given_name, family_name, middle_name, nickname, preferred_username, profile, picture, website, gender, birthdate, zoneinfo, updated_at, email, email_verified, phone_number, phone_number_verified, address, locale
claim_types_supportednormal
prompt_values_supportednone, login, consent, select_account, create
ui_locales_supportedthe tenant’s settings.locale.supported
claims_parameter_supportedfalse
request_parameter_supportedtrue
request_uri_parameter_supportedtrue (PAR request_uri values only)
require_request_uri_registrationfalse
require_pushed_authorization_requestsfalse (tenant-wide; a client can be registered to require PAR)
authorization_response_iss_parameter_supportedtrue
backchannel_logout_supported, backchannel_logout_session_supportedtrue
frontchannel_logout_supported, frontchannel_logout_session_supportedtrue
op_policy_uri, op_tos_urithe tenant’s registration.privacy_url and registration.terms_url, when set
service_documentationthe tenant’s branding.support_url, when set

form_post is not offered as a plain response mode, only as form_post.jwt. Userinfo responses are plain JSON, so userinfo_signing_alg_values_supported is absent. Server-provided DPoP nonces and dpop_jkt at /authorize are not implemented.

Browser flow API

The sign-in pages drive a login flow through these routes. A flow is created by /authorize (or by device verification) and identified by {id}. Every mutating step carries the flow’s csrf token in its JSON body; a wrong one is 403. Responses carry the public flow state; when its stage is done the page navigates to finish_url. All are in the flows limit family and answer errors as RFC 9457 problems (see Errors). The flow is described in Sign-in flows and sessions.

MethodPathPurpose
GET/t/{slug}/flows/{id}Current flow state.
POST/t/{slug}/flows/{id}/passwordIdentifier and password.
POST/t/{slug}/flows/{id}/password-changeNew password when one is required (expired or flagged).
POST/t/{slug}/flows/{id}/registerSelf-registration (when settings.registration.enabled).
POST/t/{slug}/flows/{id}/magic-linkSend a sign-in link by email.
POST/t/{slug}/flows/{id}/magic-link/verifyRedeem the link’s token.
POST/t/{slug}/flows/{id}/email-otpSend a one-time sign-in code by email.
POST/t/{slug}/flows/{id}/email-otp/verifyCheck the emailed code.
POST/t/{slug}/flows/{id}/sms-otpSend a one-time sign-in code by SMS.
POST/t/{slug}/flows/{id}/sms-otp/verifyCheck the texted code.
POST/t/{slug}/flows/{id}/passkey/startPasskey sign-in: discoverable-credential challenge.
POST/t/{slug}/flows/{id}/passkey/finishPasskey sign-in: verify the assertion.
POST/t/{slug}/flows/{id}/kerberosKerberos desktop sign-in (HTTP Negotiate): 401 challenge, 204 when an automatic attempt is not for this client, the flow’s state with a mutual-authentication token once a ticket is accepted. See Kerberos desktop sign-in.
POST/t/{slug}/flows/{id}/mfa/totp/enrollSecond step: start authenticator-app enrolment.
POST/t/{slug}/flows/{id}/mfa/totp/confirmSecond step: confirm the first code.
POST/t/{slug}/flows/{id}/mfa/verifySecond step: TOTP or recovery code.
POST/t/{slug}/flows/{id}/mfa/passkey/registerSecond step: passkey creation options.
POST/t/{slug}/flows/{id}/mfa/passkey/register/finishSecond step: store the new passkey.
POST/t/{slug}/flows/{id}/mfa/passkey/startSecond step: passkey challenge.
POST/t/{slug}/flows/{id}/mfa/passkey/finishSecond step: verify the passkey.
POST/t/{slug}/flows/{id}/mfa/email/enroll, .../confirmSecond step: enrol email codes.
POST/t/{slug}/flows/{id}/mfa/email/send, .../verifySecond step: send and check an email code.
POST/t/{slug}/flows/{id}/mfa/sms/enroll, .../confirmSecond step: enrol SMS codes (phone in E.164).
POST/t/{slug}/flows/{id}/mfa/sms/send, .../verifySecond step: send and check an SMS code.
POST/t/{slug}/flows/{id}/profileComplete required profile attributes.
POST/t/{slug}/flows/{id}/termsAccept the terms (when settings.registration.require_terms).
POST/t/{slug}/flows/{id}/consentGrant or deny the requested scopes.
POST/t/{slug}/flows/{id}/cancelAbandon the flow: answers {"stage": "cancelled", "redirect_to"}, the client’s redirect URI with error=access_denied.
GET/t/{slug}/flows/{id}/finishBrowser navigation at the end: issues the authorization code (or approves the device code) and redirects to the client.

Registration, invitations, recovery, verification and devices

All in the flows limit family; errors are problem documents.

MethodPathPurpose
GET/t/{slug}/invitations/{token}Look up an invitation by the token from its email.
POST/t/{slug}/invitations/{token}Accept it: set up the account and sign in.
POST/t/{slug}/recovery/passwordRequest a password-reset message for an identifier. The answer does not reveal whether the account exists.
POST/t/{slug}/recovery/password/confirmSet a new password with the reset token.
POST/t/{slug}/verification/email/resendSend the email-verification message again.
POST/t/{slug}/verification/email/confirmConfirm an address with the token from the link; when the link belongs to a flow waiting for verification the user is signed in and the flow state returned.
POST/t/{slug}/device/verifyThe user’s side of the device grant: {user_code} becomes a login flow for the device’s client.

SAML identity provider

Every tenant is a SAML 2.0 IdP whose entity ID is its issuer. See SAML identity provider.

MethodPathAuthLimitPurposeSpec
GET/t/{slug}/saml/metadatanoneauthorizeThe IdP’s metadata: SSO and SLO endpoints (both bindings), NameID formats, and every SAML signing certificate, the active one first.SAML Metadata §2
GET, POST/t/{slug}/saml/ssothe SP’s signature when it has certificates registeredauthorizeAuthnRequest by HTTP-Redirect (query) or HTTP-POST (form). A POST is checked, kept for ten minutes and resumed at ?continue= by a same-site GET. Answers with a signed Response posted to the SP’s consumer URL.SAML Bindings §3.4, §3.5; Profiles §4.1
GET/t/{slug}/saml/initthe user’s sessionauthorizeIdP-initiated sign-in: ?sp= entity ID or client id, optional RelayState; only for SPs with allow_idp_initiated.Profiles §4.1.5
GET, POST/t/{slug}/saml/slothe SP’s signature when it has certificates registeredauthorizeLogoutRequest from an SP (ends the session, walks the other SPs, answers), or an SP’s LogoutResponse during such a walk (RelayState names it).Profiles §4.4
GET/t/{slug}/saml/slo/chain/{id}none (a one-time id)authorizeContinues a logout that started at rIDM through the session’s SAML SPs.
GET/t/{slug}/saml/respond/{ticket}none (a one-time id)authorizePosts a failure Response (RequestDenied, AuthnFailed) to the SP after a cancelled, refused or blocked sign-in.

Identity brokering

MethodPathAuthLimitPurpose
GET/t/{slug}/broker/{alias}/startnone (?flow= a login flow, or ?ticket= a link ticket from the account API)flowsSends the browser to the upstream provider named by alias.
GET, POST/t/{slug}/broker/{alias}/callbackstate of the upstream requestflowsReceives the upstream response (POST for providers that post it back), then continues the login flow or finishes linking.
GET/t/{slug}/broker/{alias}/saml/metadatanoneflowsrIDM’s SP metadata for a SAML provider (its URL is the SP entity ID).
POST/t/{slug}/broker/{alias}/saml/acsthe IdP’s signature, RelayStateflowsAssertion consumer service (HTTP-POST): checks the Response, then redirects to a same-site ?continue= GET, bound to the browser that started the sign-in, which continues the flow or the link.
GET, POST/t/{slug}/broker/{alias}/saml/slothe IdP’s signatureflowsThe IdP’s LogoutRequest (must be signed; ends the sessions it brokered) or its LogoutResponse to rIDM’s.
GET/t/{slug}/broker/{alias}/saml/slo/out/{id}, …/slo/done/{id}none (one-time ids)flowsSend rIDM’s LogoutRequest at the end of a sign-out that started here; answer the IdP’s once the downstream SAML SPs had their turn.

Register the callback URL with the upstream provider; the admin API reports it as callback_url on every identity provider (for SAML, the assertion consumer service, with the rest under saml_sp). See Identity brokering.

Account API

The self-service API for a signed-in user, under /t/{slug}/account. It takes a bearer (or DPoP-bound) access token, JWT or opaque, issued by that tenant with the urn:ridm:account audience (the built-in ridm-account-console client obtains one), or a personal access token with the account scope. It acts only on the token’s own subject. Errors are RFC 9457 problems; security changes may answer 403 with type urn:ridm:error:reauthentication-required (see Errors). These routes are in the OpenAPI document under the account tag and are not rate limited by the guard.

MethodPathPurpose
GET, DELETE/t/{slug}/account/meIdentity with the session’s auth_time, acr and amr; DELETE deletes the account (when settings.account.self_deletion).
GET, PATCH/t/{slug}/account/profileProfile attributes the schema lets the user see and edit.
GET, PUT/t/{slug}/account/passwordPassword status; change it.
POST, DELETE/t/{slug}/account/email/changeStart (or cancel) an email change.
POST/t/{slug}/account/email/confirmConfirm the new address with its code.
POST, DELETE/t/{slug}/account/phone/changeStart (or cancel) a phone change.
POST/t/{slug}/account/phone/confirmConfirm the new number.
DELETE/t/{slug}/account/phoneRemove the phone number.
GET/t/{slug}/account/mfaEnrolled second factors and remaining recovery codes.
POST/t/{slug}/account/mfa/totp/enroll, .../confirmEnrol an authenticator app.
POST/t/{slug}/account/mfa/email/enroll, .../confirmEnrol email codes.
POST/t/{slug}/account/mfa/sms/enroll, .../confirmEnrol SMS codes.
POST/t/{slug}/account/mfa/passkey/register, .../register/finishRegister a passkey.
POST/t/{slug}/account/mfa/recovery-codesReplace the recovery codes (shown once).
DELETE/t/{slug}/account/mfa/credentials/{credential_id}Remove a factor; removing the last second factor removes the recovery codes too.
GET, DELETE/t/{slug}/account/devicesTrusted devices; forget all.
DELETE/t/{slug}/account/devices/{device_id}Forget one trusted device.
GET, DELETE/t/{slug}/account/sessionsLive sessions; end all of them (?keep_current=true spares the calling session), with back-channel logout.
DELETE/t/{slug}/account/sessions/{session_id}End one session, with back-channel logout.
GET/t/{slug}/account/appsApplications the user consented to.
DELETE/t/{slug}/account/apps/{client_id}Withdraw consent and the application’s refresh tokens.
GET/t/{slug}/account/backchannel-requestsBackchannel (CIBA) sign-in requests waiting on the user.
POST/t/{slug}/account/backchannel-requests/{id}/approve, .../denyAnswer one; an approval is remembered as consent. Refused (403) in an impersonated session; approving also needs a sign-in session, so a personal access token cannot.
GET/t/{slug}/account/identitiesLinked upstream identities.
POST/t/{slug}/account/identities/linkStart linking an upstream identity: answers {"url"}, a /broker/{alias}/start?ticket=... address to send the browser to.
DELETE/t/{slug}/account/identities/{idp_id}Unlink one.
GET, POST/t/{slug}/account/tokensPersonal access tokens; mint one (shown once).
DELETE/t/{slug}/account/tokens/{token_id}Revoke a personal access token.
GET/t/{slug}/account/exportEverything held about the user as one JSON document.

A few behaviours the table leaves out:

  • Password change. PUT password needs the current password while one is set; sign_out_others: true ends every other session, with back-channel logout.
  • Contact changes. A six-digit code goes to the new address (or number); the right code moves the account over, verified, and the previous address is told. An address another account uses is a 409. Codes are hashed and single-use, good for ten minutes and five attempts, with three sends per ten minutes; a repeat inside twenty seconds reuses the pending code. The phone number cannot be removed while it backs an SMS second step (400).
  • Linking. The browser returns from the broker to the account console with ?linked=1 or ?link_error=<code>.
  • Personal access tokens. GET tokens also lists the scopes a new token may carry (account, plus the admin permissions the user holds) and the tenant’s maximum lifetime.
  • Export and deletion. The export holds the record, credential metadata, devices, sessions, consents, roles, groups and the audit trail, never secret material. DELETE me needs the username typed again as confirmation; see deletion for what follows.

Request and response bodies are in the rendered reference.

SCIM 2.0

A SCIM service provider per tenant at /scim/v2/{slug} (RFC 7643, RFC 7644). Every request needs Authorization: Bearer rscim_..., a provisioning token minted for that tenant (see SCIM provisioning). Bodies are application/scim+json; errors are SCIM error documents (Errors). Not rate limited by the guard.

MethodPathPurpose
GET/scim/v2/{slug}/ServiceProviderConfigSupported features.
GET/scim/v2/{slug}/ResourceTypesUser and Group.
GET/scim/v2/{slug}/SchemasCore schemas.
GET, POST/scim/v2/{slug}/UsersList (filter, startIndex, count up to 200) or create.
GET, PUT, PATCH, DELETE/scim/v2/{slug}/Users/{id}Read, replace, patch, soft-delete.
GET, POST/scim/v2/{slug}/GroupsList or create.
GET, PUT, PATCH, DELETE/scim/v2/{slug}/Groups/{id}Read, replace, patch, delete.

Headers on every response

A security-headers layer adds the browser hardening headers (and Strict-Transport-Security when PUBLIC_URL is https and HSTS_MAX_AGE is not 0); CORS is answered for the UI’s and the API’s own origins and for each client’s registered cors_origins. Rate-limited families add RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset.