Change an Environment's sign-in settings
/api/v1/auth-settingsUpdates the sign-in settings of the request's Environment, applying any supplied fields (mfa, allow_self_signup, self_signup_require_captcha, login_require_captcha, password_login_enabled, email_otp_login_enabled, passkey_login_enabled, self_signup_daily_cap, access_token_ttl_seconds, refresh_token_ttl_days, public_refresh_token_ttl_days, allowed_web_origins, sso_return_urls, auth_links_base_url); omitted fields are left unchanged, allowed_web_origins are normalized and de-duplicated on save, and auth_links_base_url is canonicalized to a bare origin (send null to clear it). While auth_links_base_url is set, verification, password-reset and invitation emails link to that origin at /verify-email, /reset-password and /accept-invite instead of Canopy's hosted pages. Send the Environment's version (returned by the GET) in If-Match to refuse a stale save: a mismatch answers 409 version_mismatch, and without the header the check is skipped. Emits an environment.auth_settings_updated audit event naming the settings whose value changed.
Authentication
Authorization Option A JWT access token. Never send alongside X-API-Key: a request carrying both is refused.
X-API-Key Option B API key for management-tier access. Never send alongside an Authorization header: a request carrying both is refused.
auth_settings.manageAuthenticationChange the Environment's sign-in settings. Held only when named: full access does not include it. A scoped API key must hold it as a scope; a full-access key holds every scope within its Environment except the named-only and account scopes; a developer's token needs the Console capability that covers it. A credential without it is refused with 403.
Headers
If-MatchOptimistic-lock version of the resource. Send the `version` field returned on read; the server rejects with 409 if the row has changed since. Leave it out to skip the check; a header that names no version (not a whole number) is refused with 400 `if_match.invalid`.
X-Canopy-EnvironmentThe id of the Environment this request acts in. Required with an account API key, which belongs to the Account and names its Environment per request; an Environment key may send it only to name its own Environment. A missing header for an account key answers `400` (`api_key.environment_required`), one naming no Environment of the key's Account `400` (`api_key.environment_not_found`), and one naming another Environment than an Environment key's own `400` (`api_key.environment_mismatch`).
Request body
application/json
mfaobject Optional MFA enforcement keys persisted under `env.settings`. Send the subset of keys you want to change; omitted keys retain their prior value.
allow_self_signupboolean Optional Per-environment opt-in for browser-direct, publishable-key self-signup (`POST /v1/identity/auth/register`). Off by default; persisted under `env.settings.allow_self_signup`.
self_signup_require_captchaboolean Optional Require a solved CAPTCHA on self-signup for this Environment. Off by default; persisted under `env.settings.self_signup_require_captcha`. The hosted sign-up page renders Canopy's proof-of-work widget by itself; a customer-built page must render `<canopy-captcha>` and send its `captcha_token`, or registrations will be refused.
login_require_captchaboolean Optional Require a solved CAPTCHA on login for this Environment. Off by default; persisted under `env.settings.login_require_captcha`. The hosted sign-in page renders Canopy's proof-of-work widget by itself; a customer-built page must render `<canopy-captcha>` and send its `captcha_token`, or logins will be refused.
password_login_enabledboolean Optional Enable password (email + password) login for this Environment. On by default; persisted under `env.settings.password_login_enabled`. Set false for a passwordless-only Environment.
email_otp_login_enabledboolean Optional Enable passwordless email-OTP login for this Environment. Off by default (opt-in); persisted under `env.settings.email_otp_login_enabled`. When on, identities can sign in with an emailed 6-digit code via /login/email-otp/*.
passkey_login_enabledboolean Optional Enable passkey sign-in for this Environment. Off by default (opt-in); persisted under `env.settings.passkey_login_enabled`. When on, an identity holding a passkey signs in with it alone via /login/passkey/*, and the passkey counts as its second factor.
self_signup_daily_capnumber Optional Maximum self-signup registrations allowed per UTC day for this Environment. 0 (the default) means no cap. When exceeded, registration returns 429. Persisted under `env.settings.self_signup_daily_cap`.
access_token_ttl_secondsnumber Optional Lifetime in seconds of the access tokens (JWTs) this Environment issues to identities. Defaults to 900 (15 minutes). Persisted under `env.settings.access_token_ttl_seconds`; applies to tokens minted after the change.
refresh_token_ttl_daysnumber Optional Lifetime in days of the refresh tokens this Environment issues to identities — effectively how long a session survives without re-authentication. Defaults to 180. Persisted under `env.settings.refresh_token_ttl_days`; applies to tokens minted after the change.
public_refresh_token_ttl_daysnumber Optional Lifetime in days of a public client's session (a single-page or mobile app with no client secret), counted from sign-in and never extended by a refresh: when it ends the person signs in again. Defaults to 30. Confidential clients keep `refresh_token_ttl_days`, which slides forward on each refresh. Persisted under `env.settings.public_refresh_token_ttl_days`; applies to sessions signed in after the change.
m2m_access_token_ttl_secondsnumber Optional Lifetime in seconds of the access tokens a machine client obtains with `client_credentials`. Defaults to 300 (5 minutes). A machine token has no refresh, so this is how long a client keeps acting after its secret is rotated or it is disabled. Persisted under `env.settings.m2m_access_token_ttl_seconds`; applies to tokens minted after the change.
m2m_default_audiencestring Optional The audience a machine client's token names when the request sends no `audience`: the identifier of your API, which checks `aud` when it verifies the token. A client may also request any audience it lists in `allowed_audiences`. `null` clears it, after which a request must name an audience.
organization_integrations_enabledboolean Optional Whether an organization's administrators (an identity holding `canopy:organization.manage` at the organization node) may create machine clients of their own, its integrations, pinned to that organization. Off by default. An integration always requests the Environment's `m2m_default_audience` and holds only roles from `organization_integration_role_ids`, so turning this on without both, or clearing either while it is on, is refused with `400 environment.integrations_audience_required` or `400 environment.integrations_roles_required`.
organization_integration_role_idsstring[] Optional The roles an organization's administrators may give its integrations, by id: active roles of this Environment, shared by every organization (not one organization's own), carrying neither `canopy:organization.manage` nor `canopy:access.manage`; anything else is refused with `400 environment.integration_role_invalid`. Separate from `idp_arrival_roles`. Replaces the list. While `organization_integrations_enabled` is on the list cannot be empty (`400 environment.integrations_roles_required`). Removing a role leaves integrations already holding it unchanged; a deleted role leaves the list.
allowed_web_originsstring[] Optional Browser Origins allowed to call this Environment's public identity-auth API cross-origin (CORS), e.g. `https://app.example.com`. Required to use the Direct-API flow from a browser SPA; empty means no browser client is permitted (server-to-server with an API key is unaffected). Persisted under `env.settings.allowed_web_origins`.
sso_return_urlsstring[] Optional Allowlist of return URLs for publishable-key (Direct-API) SSO. After the IdP round-trip, `/v1/identity/auth/sso/*` redirects back to one of these with a single-use `sso_token`; an unregistered return URL is rejected (exact match — no open redirect). Persisted under `env.settings.sso_return_urls`.
idp_arrival_rolesobject Optional The roles a customer's identity provider may grant and the one a newcomer arrives with, inherited by every organization in this Environment that has no grantable list of its own. Setting it opens directory sync for those organizations and is what the organization portal's SSO setup preselects. Refused with `409` when a withdrawn role is still mapped by an inheriting organization's directory, or when clearing it would leave an inheriting live directory with no arrival role. `null` clears it.
auth_links_base_urlstring Optional Bare origin (https, or http on localhost) that auth-email links point at. Takes precedence over the Account's hosted pages. `null` clears it.
initiate_login_uristring Optional The route in your application that starts a sign-in by sending the person to Canopy's `/oauth/authorize`. Canopy's hosted pages that end with nothing to return to (an accepted invitation, a verified email, a finished password reset) offer it as **Sign in** when the OAuth client involved names no `initiate_login_uri` of its own. https, or http on localhost. `null` clears it.
organizations_create_on_signupboolean Optional Create an organization for every self-signup through `POST /v1/identity/auth/register` and add the new identity as its first member. Off by default; persisted under `env.settings.organizations_create_on_signup`. The organization starts with roles copied from the Environment's organization role templates, and the new identity holds its copy of the default admin template. Requires the organizations container and a default admin template; refused with `400 role_template.default_admin_missing` while there is none.
organizations_membership_requiredboolean Optional Refuse a session to any identity that belongs to no organization in this Environment, at login, refresh and SSO completion, with `403 auth.organization_membership_required`. Off by default; persisted under `env.settings.organizations_membership_required`. Requires the organizations container. Identities already in no organization are refused from the next session issuance on.
Responses
application/json
data *EnvironmentAuthSettingsResponseDto
application/json
error *ApiErrorBodyDto
application/json
error *ApiErrorBodyDto
application/json
error *ApiErrorBodyDto
application/json
error *ApiErrorBodyDto
Errors
When the request can't be completed, the response body includes a stable error code you can branch on, and a correlation_id (also sent as the X-Correlation-Id header). Quote the correlation id when you contact support: it identifies the exact request in Canopy's logs. A request refused by an authorization check answers 403 with the code authorization.denied, and its data names the denial_reason and the permission checked.
authorization.deniedForbiddenThe credential does not hold the permission this operation checks: a scoped API key without the scope, a developer without the Console capability that covers it, or a permission granted somewhere other than the node the request targets. The error's data names the permission.
Add the permission named in the Authentication section to the key's scopes, or ask an account administrator to grant the covering capability, then retry. For a node-scoped operation, make sure the grant reaches the node you are acting on.
Returned object
curl -X PATCH "https://auth.canopy-io.com/api/v1/auth-settings" \ -H "If-Match: "3"" \ -H "X-Canopy-Environment: value" \ -H "X-API-Key: $CANOPY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "mfa": { "mfa_required": false, "mfa_grace_period_days": 0, "mfa_trusted_device_days": 0, "mfa_factor_allowlist": [ "totp" ], "mfa_after_sso": "exempt", "webauthn_rp_id": "acme.com", "webauthn_origins": [ "https://portal.acme.com" ] }, "allow_self_signup": false, "self_signup_require_captcha": false, "login_require_captcha": false, "password_login_enabled": false, "email_otp_login_enabled": false, "passkey_login_enabled": false, "self_signup_daily_cap": 0, "access_token_ttl_seconds": 0, "refresh_token_ttl_days": 0, "public_refresh_token_ttl_days": 0, "m2m_access_token_ttl_seconds": 0, "m2m_default_audience": "https://api.example.com", "organization_integrations_enabled": false, "organization_integration_role_ids": [ "string" ], "allowed_web_origins": [ "string" ], "sso_return_urls": [ "string" ], "idp_arrival_roles": { "role_ids": [ "string" ], "default_role_id": "string" }, "auth_links_base_url": "https://app.example.com", "initiate_login_uri": "https://app.example.com/login", "organizations_create_on_signup": false, "organizations_membership_required": false }'
{ "data": { "environment_slug": "string", "version": 0, "mfa_required": false, "mfa_grace_period_days": 0, "mfa_trusted_device_days": 0, "mfa_factor_allowlist": [ "string" ], "mfa_after_sso": "exempt", "webauthn_rp_id": "string", "webauthn_origins": [ "string" ], "allow_self_signup": false, "self_signup_require_captcha": false, "login_require_captcha": false, "password_login_enabled": false, "email_otp_login_enabled": false, "passkey_login_enabled": false, "self_signup_daily_cap": 0, "access_token_ttl_seconds": 0, "refresh_token_ttl_days": 0, "public_refresh_token_ttl_days": 0, "m2m_access_token_ttl_seconds": 0, "m2m_default_audience": "string", "organization_integrations_enabled": false, "organization_integration_role_ids": [ "string" ], "allowed_web_origins": [ "string" ], "sso_return_urls": [ "string" ], "idp_arrival_roles": { "role_ids": [ "string" ], "default_role_id": "string" }, "auth_links_base_url": "string", "initiate_login_uri": "string", "organizations_create_on_signup": false, "organizations_membership_required": false, "identities_without_organization_count": 0 } }
Tell us how we can improve this guide.