Update the environment settings

PATCH /v1/environment-settings · permission environment-settings:write

Updates the environment settings with a JSON merge patch (application/merge-patch+json, RFC 7396). If-Match is required: missing gives 428, a stale version gives 412. If the settings were never written the version is "0" and the first write creates the row. A patch that changes nothing writes nothing and keeps the version.

Request

Field Type Required Description
If-Match header yes The ETag of the settings as last read ("0" if never written). Missing: 428; stale: 412.
locales.enabled string[] no Enabled language codes (registered languages), 1-64 entries, no duplicates. Cannot be null.
locales.default string no Default language; must be one of locales.enabled. Cannot be null.
default_zoneinfo string no IANA time zone name (default UTC). Cannot be null.
session.idle_timeout_s integer no Idle timeout in seconds, 60-8640000 (default 259200); not greater than session.absolute_timeout_s. Cannot be null.
session.absolute_timeout_s integer no Absolute lifetime in seconds, 60-31536000 (default 2592000). Cannot be null.
session.max_concurrent integer no Concurrent sessions per user, 0-500; 0 means the system limit (default). Cannot be null.
session.concurrent_mode string no evict_oldest (default) or deny_new. Cannot be null.
session.anomaly_action string no enforce (default) or log. Cannot be null.
signup.mode string no open (default), invite_only or closed. Cannot be null.
signup.email_domain_mode string no off (default), allowlist or denylist. Cannot be null.
signup.email_domains string[] | null no At most 1000 domains; replaces the whole list (RFC 7396 does not merge arrays). null or [] clears it. Entries are normalized (lowercase, IDNA A-label), duplicates are dropped and the list is stored sorted. Kept while the mode is off.
security_notifications.enabled boolean no Master switch (default true). Cannot be null.
security_notifications.events string[] | null no Subset of the 12 security events (password_changed, password_reset, email_changed, phone_changed, authenticator_added, authenticator_removed, recovery_code_used, recovery_codes_regenerated, account_locked, breached_password_detected, new_device_sign_in, account_recovery_started); replaces the whole list and is returned in canonical order. null or [] clears it (no event).
account_recovery.mode string no self_service_waiting (default), admin_only or disabled. Cannot be null.
account_recovery.waiting_hours integer no 24-168 hours (default 72); stored in every mode. Cannot be null.
account_recovery.support_url string no Empty or an https:// URL of at most 2048 bytes, without user info or fragment. Shown when recovery is disabled. Cannot be null.
mfa.email_second_factor_enabled boolean no Email second factor on or off (default false). Cannot be null.
mfa.email_second_factor_acr string no Assurance the email factor counts as: 1fa (default) or mfa. Cannot be null.
mfa.totp_issuer_label string no TOTP issuer label, at most 64 characters; empty means the environment's display name. No control characters, : or leading/trailing spaces. Cannot be null.
mfa.recovery_codes_enabled boolean no Recovery codes on or off (default true). Cannot be null.
mfa.recovery_code_count integer no Number of recovery codes, 8-16 (default 10). Cannot be null.
mfa.required boolean no Require a second factor at every sign-in in the environment (default false). Combined with OR with the application, organization and request requirements; an existing SSO session below it is sent to step up (interaction_required with prompt=none). Cannot be null.
mfa.min_acr string no Lowest assurance every sign-in in the environment must reach: 1fa (default, no effect), mfa or phr. The highest of the environment, application, organization and request thresholds applies, to new sign-ins and to SSO session reuse. Cannot be null.
mfa.enrollment_grace_days integer no Days a user without a second factor may keep signing in without enrolling, counted from the first time enrollment is required of them, 0-14 (default 0, no grace). The grace postpones only what the environment and organization axes require; an application's min_acr and the requested acr_values are never postponed. Out of range returns 422 validation_failed with out_of_range. Shortening it takes effect at once. Cannot be null.
frontchannel_logout_enabled boolean no Front-Channel Logout on or off (default false); turning it off keeps the registered URIs. Cannot be null.
allowed_first_factors string[] no Allowed first factors: at least one of password, passkey, email_otp, magic_link, sms_otp, social, enterprise, no duplicates. Replaces the whole list and is returned in this order. Default password, passkey, social, enterprise; email_otp, magic_link and sms_otp are on only when added explicitly. An empty list returns 422 required (an environment nobody could sign in to). Cannot be null.

Behavior

Response

200 OK with the updated settings and the new ETag.

{
  "locales": {"enabled": ["en", "tr"], "default": "tr"},
  "default_zoneinfo": "Europe/Istanbul",
  "session": {"idle_timeout_s": 3600, "absolute_timeout_s": 2592000, "max_concurrent": 5, "concurrent_mode": "evict_oldest", "anomaly_action": "enforce"},
  "signup": {"mode": "invite_only", "email_domain_mode": "allowlist", "email_domains": ["example.com"]},
  "security_notifications": {"enabled": true, "events": ["password_changed", "password_reset"]},
  "account_recovery": {"mode": "self_service_waiting", "waiting_hours": 72, "support_url": ""},
  "mfa": {"email_second_factor_enabled": false, "email_second_factor_acr": "1fa", "totp_issuer_label": "Acme", "recovery_codes_enabled": true, "recovery_code_count": 10, "required": false, "min_acr": "1fa", "enrollment_grace_days": 0},
  "frontchannel_logout_enabled": false,
  "allowed_first_factors": ["password", "passkey", "magic_link", "enterprise"],
  "updated_at": "2026-10-05T09:30:00.123456Z"
}

Errors

HTTP code When
400 unknown_field The patch contains an unknown or read-only field, or is not a JSON object (malformed_request).
412 precondition_failed If-Match does not match the current version.
415 unsupported_media_type The content type is not application/merge-patch+json.
422 validation_failed A value is invalid (the pointer names the field), or a field that cannot be null was set to null (code: invalid).
422 policy_conflict The new allowed_first_factors leaves no method in common with an application that has its own set.
428 precondition_required The If-Match header is missing.
401 unauthenticated The credential is missing, invalid or revoked.
403 insufficient_permissions The credential lacks the required permission.
429 rate_limited The rate limit was exceeded; retry after the Retry-After period.
503 service_unavailable A dependency is temporarily unavailable; retry after the Retry-After period.

Example

Turn on email magic links and turn off passwords:

curl -X PATCH https://api.eu.keenauth.com/v1/environment-settings \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/merge-patch+json" \
  -H 'If-Match: "0"' \
  -d '{"allowed_first_factors":["passkey","magic_link","social","enterprise"]}'

Session and sign-up settings:

curl -X PATCH https://api.eu.keenauth.com/v1/environment-settings \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/merge-patch+json" \
  -H 'If-Match: "0"' \
  -d '{"session":{"idle_timeout_s":3600},"signup":{"mode":"invite_only","email_domain_mode":"allowlist","email_domains":["example.com"]}}'