Patch tenant-scoped settings for a category.
curl --request PATCH \ --url https://auth.example.com/api/admin/tenants/example/settings/example \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "ifMatch": "v1", "changes": { "oauth.require_pkce": true } }'Update Setting settings in the Tenants area. The request body is JSON unless the detailed schema for this operation states otherwise. plugin.enabled (default false) allows the code of external (custom Dynamic Worker) plugins to run for the tenant; it does not affect the built-in email providers, CAPTCHA providers or TOTP. Turning it off first closes the tenant’s gate in ar-plugin-runner (which stops every external installation of the tenant from running, queued hooks included) and switches its external plugins off, then saves; if either fails the PATCH is refused with 503 and nothing is saved. A PATCH the settings manager would refuse (conflicting operations on the key, a version mismatch) does neither. The gate stops external code from starting, not code that has already started: a hook or sync call launched before the gate closed runs to completion (bounded by its timeout), and a few milliseconds can pass between the runner’s last gate check and the launch. Opening the gate is a compare-and-swap on its generation, so an enable that read the setting before a turn-off cannot reopen the gate after it. The gate is a projection written by this service, not a lease: a change not yet written is not seen by the runner. A periodic reconciliation (about every two minutes, a rolling pass over the tenants that have an open gate or an enabled external installation without one) compares each gate with the saved setting and corrects it, so a gate that drifted (a failed write after a save, a changed PLUGIN_ENABLED, a gate never written) is corrected within one pass, which takes ceil(tenants listed / 50) runs; external hooks queued while a gate has never been written are retried until then rather than discarded. Account-lifecycle settings are tenant-scoped. Retention changes apply only to guests created subsequently; saving never reschedules existing accounts. Disabling deletion leaves existing deadlines unchanged. The tenant category’s sign-in UI routing is validated: tenant.ui_base_url must be the platform’s or the tenant’s issuer origin (including its active primary custom domain), localhost, or an origin in ALLOWED_ORIGINS, and each tenant.ui_*_path must start with a single / and have no query or fragment (400 validation_failed otherwise). An empty tenant.ui_base_url is refused: clear the setting to use the platform’s UI. Upgrade holds are granted once per guest and are never extended by later settings changes. Guest upgrade method permissions are independent of signup and login permissions. For the login-ui category, login-ui.account_pages is a JSON-encoded Account Page document. Each screen placement accepts show_for_guests as a boolean; omitting it defaults to true. For the assurance category, assurance.scope_aal_requirements (scope to AAL) and assurance.upstream_acr_mappings (an external or SAML IdP’s acr to the AAL it is taken for) are strings holding a JSON object whose names are non-empty and unpadded and whose values are AAL1, AAL2 or AAL3 (400 validation_failed otherwise). assurance.outbound_acr_mappings (an acr of another vocabulary Authrim may return when a client asks for it, to the AAL it requires) follows the same rules, and its names must also be at most 512 characters, contain no whitespace and not be Authrim’s own urn:authrim: values. Likewise assurance.scope_ial_requirements maps scopes to IAL1, IAL2 or IAL3; assurance.ial_assurance_values maps IAL1, IAL2 or IAL3 to a non-empty list of at most 16 unique absolute URIs; assurance.saml_authn_context_aal maps absolute AuthnContextClassRef URIs to AAL1, AAL2 or AAL3; and assurance.ida_profile is {} or {trust_framework, assurance_levels?, claims} with claims drawn from name, given_name, family_name, middle_name, birthdate, gender, address, email and phone_number. An agent executing an approved settings change (assurance, security, tokens, oauth or session) may set only the fields its Tool binds and may not clear or disable settings; anything else returns 400 AGENT_ELEVATION_SETTINGS_OUT_OF_SCOPE. For the ciba and device-flow categories the whole PATCH is refused (400 validation_failed, naming the key) when any key of it would be rejected one by one: an unknown key, a value of the wrong type, a fractional value where the setting is whole-number, a value outside its range, a key named by more than one of set, clear and disable, or a disable of a setting that is not on/off. Nothing is saved then. Relations are checked on the document the save leaves: each min_expires_in must not exceed max_expires_in, and each min_interval must not exceed max_interval; expires_in must lie within [min_expires_in, max_expires_in], and poll_interval within [min_interval, max_interval]. A cleared key counts as the value it then inherits (the platform, an environment variable, else the default). For example, with the default device_flow.min_interval of 5, device_flow.poll_interval 2 is refused with 400 and nothing is saved; lower min_interval in the same request to save it. The device flow user code settings are checked together on the pair the save leaves: the code needs at least 34 bits of entropy, so device_flow.user_code_length must be at least 7 with device_flow.user_code_charset BASE31 (the default), 8 with BASE20 and 11 with NUMERIC. For example NUMERIC with length 10 (33.2 bits) is refused with 400, with a message naming the minimum for the charset, and so is length 4 on its own while the charset is BASE31; set both in one request to change them together. A cleared key counts as what it inherits. Runtime holds the same floor whatever is stored: a stored pair below it issues codes with the default pair (BASE31, 8). The request must include the current settings version in ifMatch; stale writes return 409. On the authentication-methods category, turning human verification on for a flow (authentication-methods.human_verification.login_enabled, signup_enabled, reauth_enabled), or switching the provider while a flow requires it, is refused with 400 when the provider’s plugin is not enabled or has no credentials (error: human_verification_provider_unavailable, with reason provider_disabled or provider_unconfigured); enable and configure the plugin first. A save that does not create such a mismatch is unaffected, even when the tenant is already in one. Known limit: this check and a plugin disable that run at the same time can both pass; serialising them would need a shared coordinator. For the identity-mapping category, identity_mapping.oidc_default_set_id names the Mapping Set that applies to apps of the scope that choose none of their own (unset: no default, so no mapping applies; there is no environment variable; apps that choose none of their own use it for the ID token, UserInfo and the consent screen, and a value that cannot be used stops them from signing in). The value is a Mapping Set ID (letters, digits and _ . : -, at most 200 characters, no surrounding whitespace) or, for a tenant, the reserved value none, which refuses the platform’s default; a string only (the disabled marker, false and null are refused, and disable is refused for the key; clear it to remove it). The set must exist and be usable, or the whole PATCH is refused with 400 validation_failed and nothing is saved: a tenant may name a set it owns or one the platform owns, the platform only one the platform owns (any other set answers the same as one that does not exist); the set must be active, with an active activation that applies to the tenant (or to every tenant) and an active compiled snapshot, and must release into exactly one active OIDC Destination Profile; a platform-owned set must also hold only what a platform set may hold. 503 temporarily_unavailable when the Admin database cannot be read to check it (nothing is saved). The category is not available for apps (400), and cannot be rolled back through the settings history routes (400). The category also holds platform-only limits of the search for what names a Mapping Set (identity_mapping.reference_scan_tenant_limit 1 to 1000, reference_scan_row_limit 10 to 5000, reference_scan_concurrency 1 to 16 and reference_display_limit 1 to 100, whole numbers; defaults 200, 500, 8 and 20; a value outside the range is refused with 400 on the platform and is not accepted for a tenant). A value in the environment or the store that is outside the range is ignored and the default is used. The institution category (institution.scopes and institution.display_names) says which institution the tenant stands for and is the platform’s to change: reading it needs the usual tenant view access, but saving it (set, clear, and so on) needs platform authority, a role that may edit settings held through a global assignment, or a machine token that may write the settings of every tenant; the tenant’s own administrators (including its ar_tenant_admin), tokens limited to some tenants, and agents are refused with 403. It is a tenant category only (400 for the platform and for apps), has no environment variable, is not inherited, is not given to several tenants by the bulk apply, is not copied when a tenant is cloned, and cannot be rolled back through the settings history routes (400). institution.scopes is a comma-separated list of 1 to 20 distinct lowercase domain names of at least two labels (letters, digits and hyphens; a non-ASCII name in its xn— form; no wildcard, port, path, trailing dot or IP address), the first being the default; an empty list is refused (clear the setting for none). institution.display_names is JSON text of an object from a lowercase language tag (ja, en, en-gb, …) to the institution’s name (1 to 200 characters, unpadded, no control characters), with at least ja and en and at most 16 languages. Neither can be disabled (400). Both are checked as above, or the whole PATCH is refused with 400 validation_failed and nothing is saved. While institution.scopes is set, the SAML IdP releases eduPersonScopedAffiliation and eduPersonPrincipalName only under one of the scopes (a value under another scope is withheld and recorded in the audit log as saml.attribute.withheld with a reason code and the scope, never the value; a value with no scope gets the default), lists the scopes in its metadata (shibmd:Scope), and releases the default as schacHomeOrganization to a service provider whose field mapping set maps the system source Home Organization to it. While institution.display_names is set, the SAML IdP metadata names the organization with it and the English name is released as the organization name attribute (o) to a service provider whose field mapping set maps the system source Organization Name to it. A tenant that has set neither releases no schacHomeOrganization and no organization name (o), whatever a mapping produces for them (withheld, reason institution_not_registered), and releases eduPersonScopedAffiliation and eduPersonPrincipalName as before.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”Request Body required
Section titled “Request Body required ”object
Expected settings version for optimistic concurrency.
Setting keys and values to apply. For login-ui.primary_locales, use null for automatic defaults or an array of zero to six unique supported locale codes. The login-ui.show_english_language_names value must be boolean. The login-ui.account_pages value is a JSON-encoded Account Page document whose placements accept a boolean show_for_guests value; omission defaults guest visibility to true.
object
JSON-encoded authrim.account_pages.v1 document. Every screen placement may set show_for_guests to control whether its Account Page widget is rendered for guest accounts. The value defaults to true when omitted.
Example
{ "ifMatch": "v1", "changes": { "oauth.require_pkce": true }}Responses
Section titled “ Responses ”Settings v2 patch response.
Settings v2 patch result. Includes applied, cleared, disabled, rejected, and version metadata from the settings manager. A setting whose dependency (dependsOn) is met by an inherited value, the environment or the default is accepted; dependencies are checked against effective values, not only the values stored at the requested scope.
object
Present when the change was saved but could not yet be copied to the store runtime workers read. A scheduled retry copies it, normally within a minute; until then runtime keeps the previous values. Absent when the change already applies.
Only on a tenant plugin category save that touched plugin.enabled: the change was saved but ar-plugin-runner’s gate for external plugin code could not be put in step afterwards. After a turn-on the gate stays as it was (closed unless it had been opened before), so external code does not run on the strength of an unconfirmed “on”. After a turn-off the gate was closed before the save, but a concurrent enable may have reopened it. Either way saving plugin.enabled again, or enabling an external plugin, retries, and the periodic reconciliation puts the gate where the saved setting says within one pass. Absent when the gate is in step.
Example
{ "applied": [ "oauth.require_pkce" ], "cleared": [], "disabled": [], "rejected": {}, "version": "v2"}Error response.
object
Example generated
{ "error": "example", "message": "example", "error_description": "example"}The settings changed since ifMatch (conflict, with currentVersion).
object
Example generated
{ "error": "example", "message": "example", "error_description": "example"}The update could not be admitted or its completion could not be confirmed. Reload the current settings before retrying with their latest version.
object
Only with temporarily_unavailable, on the tenant plugin category when plugin.enabled is turned off: ar-plugin-runner’s gate for external plugin code could not be closed (external_plugin_gate_unavailable), or the tenant’s external plugins could not all be switched off (external_plugin_shutdown_failed). Nothing was saved; retry.
With external_plugin_shutdown_failed: the external plugins that were switched off before the failure (they stay off).
Example
{ "error": "backup_mutation_unavailable", "reason": "external_plugin_gate_unavailable"}