Simulate SCIM provisioning
POST /v1/directories/{directory_id}:simulate · permission directories:write · development and staging only
Runs one SCIM simulator scenario against the directory's real SCIM endpoint. For the run only, KeenAuth mints a real
SCIM token, uses it, and then revokes it. Token checks, rate limits, the mass deprovision circuit breaker and the
provisioning log all run exactly as they do for your identity provider. The users are a fixed fictional set: 25 people
at example.test in 5 groups. They contain no real personal data.
The simulator is a development tool and changes the directory. The users and groups it writes stay in the directory like any other provisioned records. Use a directory you created for testing.
| Scenario | What it does |
|---|---|
seed |
Creates the 25 users and the 5 groups with their memberships. In a directory that already holds them, the first user gets a SCIM 409 and the run is failed. |
bulk_deactivate |
Sends active=false for 60% of the users (15 of 25). The circuit breaker trips only when the directory's mass_deprovision_guard.absolute_min is 15 or lower. With the default of 25 it does not trip. |
group_churn |
Moves a user from one group to another. |
attribute_update |
Changes every user's display name and family name. |
conflict_email |
Creates a user with an existing userName and expects a SCIM 409 uniqueness. |
When the directory is empty, every scenario except seed runs seed first.
The simulation token is never listed and does not count toward the 2 valid tokens. Using it does not start the
rotation of your identity provider's token. Only one run per directory can be in progress at a time. The run is audited
as directory.simulation_started and directory.simulation_finished.
Request
| Field | Type | Required | Description |
|---|---|---|---|
directory_id |
path | yes | SCIM directory ID (dir_…). |
scenario |
body, string | yes | seed, bulk_deactivate, group_churn, attribute_update or conflict_email. |
Idempotency-Key |
header | no | Replays the first response for a retried request; the scenario does not run again. |
{"scenario": "bulk_deactivate"}
Response
200 OK with the run's result. A SCIM error answer does not fail the request. It makes the run failed, fills
failure and leaves the counts at what succeeded before the error. The result has counts and states only: no user, no
SCIM ID and no token.
{
"directory_id": "dir_01j9xm2n4p6q8r0s2t4v6w8y0z",
"scenario": "bulk_deactivate",
"status": "completed",
"users_created": 0,
"groups_created": 0,
"users_updated": 0,
"groups_updated": 0,
"users_deactivated": 15,
"conflicts": 0,
"circuit_breaker_tripped": true,
"deprovision_hold": {"id": "0191e9b2-6f3a-7c4d-8e5f-0a1b2c3d4e5f", "pending_count": 6},
"failure": null,
"started_at": "2026-10-28T09:31:00.123456Z",
"completed_at": "2026-10-28T09:31:01.654321Z"
}
| Field | Description |
|---|---|
status |
completed, or failed when the endpoint answered with a SCIM error, the run took too long, or an answer was not what the scenario expected. |
users_updated |
User PATCHes other than deactivations. |
users_deactivated |
active=false PATCHes the endpoint accepted, held ones included. |
conflicts |
Expected 409 uniqueness answers (conflict_email). |
circuit_breaker_tripped |
A mass deprovision hold was opened during the run. |
deprovision_hold |
The directory's open hold after the run, or null. Approve or reject it with the deprovision-holds endpoints. |
failure |
null when completed. Otherwise reason (scim_error, timeout or unexpected_response), and for scim_error also scim_status (HTTP status) and scim_type (SCIM scimType, or null). |
Errors
| HTTP | code | When |
|---|---|---|
| 400 | unknown_field |
The body contains a field other than scenario. |
| 403 | test_feature_not_in_production |
The environment is a production environment. |
| 404 | not_found |
The directory does not exist in this environment. |
| 409 | conflict |
Another run of this directory is in progress, or the directory is deactivated or being deleted. |
| 422 | validation_failed |
scenario is missing or unknown (/scenario). |
| 422 | idempotency_key_reuse |
The Idempotency-Key was used for a different request. |
| 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 |
Directory sync or the simulator is temporarily unavailable or not configured in this deployment; retry after the Retry-After period. |
Example
curl -X POST "https://api.eu.keenauth.com/v1/directories/dir_01j9xm2n4p6q8r0s2t4v6w8y0z:simulate" \
-H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"scenario": "seed"}'
keenauth directories simulate dir_01j9xm2n4p6q8r0s2t4v6w8y0z --scenario bulk_deactivate