Preview or apply the same settings to several tenants.
curl --request POST \ --url https://auth.example.com/api/admin/settings/bulk-apply \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --header 'Idempotency-Key: example' \ --data '{ "mode": "preview", "tenant_ids": [ "example" ], "changes": [ { "category": "example", "set": { "additionalProperty": "example" }, "clear": [ "example" ] } ], "on_existing_override": "skip", "expected": { "additionalProperty": { "additionalProperty": "example" } } }'Gives the same settings to several tenants in one request, for any setting a tenant can have that the bulk apply allows. mode: preview writes nothing and says, for each tenant and key, what the tenant has now (current.own is the tenant’s own value, current.effective what is in force), what is proposed, and what an apply would do (would_apply, would_clear, would_skip or would_reject), with the result of the same checks an apply runs (the Admin database reference check of the default Mapping Set included) and the version of the tenant’s document. A category can add what its change affects in impact: for identity-mapping, the number of the tenant’s apps that follow the default (those with no Mapping Set of their own), or available: false with a reason when the tenant’s database cannot be read. mode: apply writes, tenant by tenant and independently (a tenant that fails does not stop or undo another; there is no rollback of the whole), through the same save as the tenant PATCH, with the caller’s right to edit that tenant’s category checked again for each tenant. Each change reports previous (the tenant’s own value before; null when it had none) so that an apply can be undone, new (the value, or "clear"), version_before and, when written, version_after. A tenant that already has a value of its own for a key keeps it unless on_existing_override is overwrite (reported as skipped, reason existing_override; already_set when it is the same value and not_overridden for a clear of nothing). expected holds the versions a preview returned: a category whose version has changed is conflict (version_changed) and is not written. Settings the bulk apply does not give (a tenant’s URLs, domains, identifiers, brand text, references to its own records, secrets, structured documents, platform-only settings) are rejected with a reason. A tenant that does not exist, or is deleted, is rejected (tenant_not_found, tenant_deleted). A request names at most bulk_apply.max_tenants_per_request tenants (25 by default; duplicates count once) and bulk_apply.max_keys_per_request settings (20), both platform settings held between 1 and 100; more is a 400, and a larger job is sent in several requests. A key counts once for each category it is set or cleared in, and a request carries at most 20 changes (one for each category; a category that does not exist is a 400). A value is at most 2048 bytes as JSON (UTF-8, escapes counted), and all values together at most 32768. A value of more than 256 bytes is not repeated in the answer: it is shown as {"value_too_large": true, "type": ..., "length": ...} (also for previous, which then cannot be used to undo that key). A request whose answer could pass the 900,000 bytes the idempotency store keeps (an upper bound worked out from the tenant IDs, the keys and the proposed values) is a 400 (response_too_large) before anything is done. A preview reads the tenants’ Core databases for impact without leaving a trace: nothing is written for a tenant whose database cannot be routed. The result of a failed tenant carries a fixed code, nothing of the tenant or the database. An apply is recorded once (settings.bulk_apply: counts, categories and key names, never a value), and every tenant’s own audit record carries the bulk ID and, for the keys changed, what was done to each (changed_keys, results), never the values before or after; a failure record holds codes and key names only, not what a validation found. A refusal by a validation gives reason: validation_failed and, where there is one, a fixed detail_code; the sentence of the validation is never given (it can name what the tenant has saved, or repeat a piece of what was sent), and the allowed values of a setting are read from the settings metadata. Needs the platform’s authority over settings (a global assignment of a platform role with a global tenant scope, or a machine token for every tenant with the settings write permission); a tenant administrator, and an Agent, are refused with 403. Strict rate limit. Idempotency-Key is required (8 to 128 characters): the same key with the same body, by the same administrator or machine principal, answers with the stored result; with another body, 409; another principal’s request with the same key is another request. The stored result (which holds the settings and IDs of many tenants) is kept for 15 minutes in the platform’s Admin database, never in the database of any tenant, and removed when the time runs out (a scheduled sweep does it even if the administrator never calls again). Once an apply has started, its key is never released: if its result could not be kept, a repeat of the key is a 409 (result_unknown) and does not run again, so look at the tenants and use another key to run it again. A request that changed nothing (a preview, a refusal, a failure before the first change) is not kept, so that it can be repeated.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Header Parameters
Section titled “Header Parameters ”Required replay-protection key retained for 15 minutes.
Request Body required
Section titled “Request Body required ”object
The tenants to give the changes to; duplicates are dropped. At most bulk_apply.max_tenants_per_request (25 by default, between 1 and 100).
One entry per settings category (a category appears once). The keys set or cleared, over all entries, number at most bulk_apply.max_keys_per_request (20 by default).
object
A settings category that can be set for a tenant.
Setting keys and the values to give them.
object
Setting keys to take back to what the tenant inherits (the tenant’s own value is removed), as clear of a PATCH does.
What to do with a key the tenant already has a value of its own for: leave it (skip) or replace or clear it (overwrite).
For apply, optional: the version of each category a preview returned, by tenant ID and category. A category whose version is now another is not written.
object
object
Responses
Section titled “ Responses ”The preview of, or the result of, a bulk apply: one entry per tenant named, in the order of first appearance.
object
Present for an apply; in every audit record the apply wrote.
object
object
The number of tenants by status.
object
object
Apply: applied, cleared, skipped, rejected, conflict or failed (the worst outcome of the tenant’s changes leads; a tenant that cannot be worked on is rejected with a reason). Preview: ready, nothing_to_do, or rejected for a tenant that cannot be worked on.
Why the whole tenant was rejected (tenant_not_found, tenant_deleted).
A fixed code for a tenant that failed as a whole; nothing else.
object
object
What the tenant has now; null when it was not read (a refused key, or settings that could not be read).
object
The tenant’s own value; null when it has none.
The value in force.
Where the value in force comes from (kv is the tenant’s own).
The value, or "clear".
object
Null when the key was not checked (it would be skipped or refused already).
object
See detail_code of a result.
The version of the tenant’s document for the category, for expected.
object
For skipped: existing_override, already_set, not_overridden. For rejected: a reason the setting cannot be given in bulk (for example tenant_specific_url, tenant_ui_routing, tenant_brand_text, structured_value), forbidden, or validation_failed. For conflict: version_changed. For failed: a fixed code.
Why a validation refused the key, as a fixed code and never as the sentence of the validation: value_invalid, value_out_of_range, value_wrong_type, dependency_not_met, unknown_key, bounds_not_met, identity_mapping_* or human_verification_*. What a value may be (range, type, allowed values) is in the settings metadata, GET /api/admin/settings/meta/{category}.
The tenant’s own value before; null when it had none.
The value given, or "clear".
Set only when the key was written.
Preview only. What a category’s changes affect in the tenant, by category. For identity-mapping: apps_total and apps_following_default (the apps with no Mapping Set of their own), apps_with_own_set and apps_opted_out (apps a change of the default leaves alone: they name a Mapping Set, or opt out with {"mode":"none"}), apps_with_unusable_choice (apps whose selector names only a Destination Profile, which fail rather than follow the default), and warnings: with code implicit_activation_no_longer_applies, the tenant’s own active activations (activationId, setId, setName; nothing of their scope or claims) that once bound apps naming no Mapping Set and no longer do. warnings_unavailable: true says the Admin database could not be read for them. Or available: false and a reason.
object
Example
{ "mode": "preview", "on_existing_override": "skip", "results": [ { "changes": [ { "action": "would_apply" } ] } ]}Error response.
object
Example generated
{ "error": "example", "message": "example", "error_description": "example"}Error response.
object
Example generated
{ "error": "example", "message": "example", "error_description": "example"}Error response.
object
Example generated
{ "error": "example", "message": "example", "error_description": "example"}Error response.
object
Example generated
{ "error": "example", "message": "example", "error_description": "example"}Error response.
object
Example generated
{ "error": "example", "message": "example", "error_description": "example"}Error response.
object
Example generated
{ "error": "example", "message": "example", "error_description": "example"}Error response.
object
Example generated
{ "error": "example", "message": "example", "error_description": "example"}