Create a bulk user deletion job

POST /v1/jobs/user-deletions · permissions users:delete and jobs:write

Deletes up to 10,000 users in one asynchronous job. Each user goes through the same deletion as DELETE /v1/users/{user_id}: the user becomes pending_deletion and the permanent deletion runs afterwards; the deletion record notes that it came from a bulk job. Deletion cannot be undone, so the endpoint works in two steps:

  1. Send the request with "dry_run": true. Nothing is written; the response holds matched_count, the number of users the request would delete.
  2. Send the same selection with "confirm_count" set to that number. If the number of matching users has changed in between, the request fails with 409 confirmation_mismatch and no job is created; run the dry run again.

Select users in one of two ways: user_ids (repeated ids count once; results keep the order of first appearance) or organization_id (the organization's active and suspended members at the moment the job is created). Ids that do not match a user are kept in the job and reported as not_found.

Only one bulk user deletion job runs at a time in an environment; while one is running, a new job is refused with 429 rate_limited and Retry-After. The job is written to the management audit log when it is created and when it completes; each deleted user also has its own deletion audit entry. Follow the job with Get a bulk user deletion job and read per-user outcomes with List bulk user deletion results.

Request

Headers: Idempotency-Key (required, 16–64 characters A-Z a-z 0-9 _ -). Repeating the request with the same key and the same body returns the first job; the same key with a different body fails with 422 idempotency_key_reuse. The body may be up to 512 KiB.

Field Type Required Description
user_ids array of strings one of the two User ids prefixed usr_, 1 to 10,000.
organization_id string one of the two Organization id prefixed org_; at most 10,000 members.
dry_run boolean no true: only count the users; nothing is written. Default false.
confirm_count integer yes, unless dry_run The matched_count of the dry run (0–10,000).

Response

Dry run: 200 OK with {"dry_run": true, "matched_count": n}.

Job created: 202 Accepted with the count and the job.

{
  "dry_run": false,
  "matched_count": 2,
  "job": {
    "id": "job_01h455vb4pex5vsknk084sn02q",
    "status": "running",
    "filter": "user_ids",
    "total": 3,
    "succeeded": 0,
    "not_found": 0,
    "failed": 0,
    "pending": 3,
    "created_at": "2026-01-15T09:30:00Z"
  }
}

Errors

Every error is an application/problem+json body with a stable code.

HTTP code When
400 malformed_request, unknown_field The Idempotency-Key header is missing or invalid, or the body is not valid JSON or has an unknown field.
401 unauthenticated The API key is missing, invalid or revoked.
403 insufficient_permissions The key lacks users:delete or jobs:write.
409 confirmation_mismatch confirm_count is not the current number of matching users.
409 idempotency_in_progress A request with the same Idempotency-Key is still being processed.
413 payload_too_large The body is larger than 512 KiB.
422 validation_failed Both or neither of user_ids and organization_id, a malformed id, more than 10,000 users (too_many, too_many_users), an organization without members (no_users) or a missing confirm_count (required).
422 idempotency_key_reuse The Idempotency-Key was used with a different body.
429 rate_limited Another bulk user deletion job is running, or the rate limit was exceeded; honor Retry-After.
503 service_unavailable A dependency is temporarily unavailable; retry later.

Example

curl -X POST https://api.eu.keenauth.com/v1/jobs/user-deletions \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"organization_id": "org_01h455vb4pex5vsknk084sn02q", "dry_run": true}'

CLI: keenauth jobs user-deletions create --organization-id org_01h455vb4pex5vsknk084sn02q --dry-run --yes, then keenauth jobs user-deletions create --organization-id org_01h455vb4pex5vsknk084sn02q --confirm-count 42 --yes. Long id lists go in a file: --data @users.json.