SCIM 2.0 API: Provision Users and Groups
SCIM manages an Organization's directory membership independently of interactive SSO login. It is available when the deployment's enterprise_sso switch is enabled and an authorized administrator has enabled SCIM and issued its organization token.
An inactive Organization suspends every SCIM endpoint, including directory reads, discovery and OPTIONS. Requests receive the same generic 401 SCIM authentication error and Bearer challenge as an invalid token. Suspension preserves the token, configuration, directory resources and access mappings. Explicitly reactivating the Organization resumes an otherwise-valid token; it does not enable a disabled configuration, undo revocation or rotation, or reactivate an individually inactive directory user.
Provisioning mutations recheck Organization activity under the directory lock. A mutation that obtains the lock before deactivation may commit before deactivation completes; one admitted afterward is refused. Reads already admitted may finish. The application does not queue or replay refused requests: resume or reconcile provisioning through the identity provider after reactivation. Provider retry and connection-repair behavior varies; a 401 may require an administrator to resume provisioning even when the retained token remains valid.
Use the base URL supplied in the Organization's directory-provisioning settings:
https://your-deployment.example/scim/v2/{organization_slug}
This API has a separate bearer token. A normal platform X-Api-Key does not authenticate SCIM. Create, rotate, or revoke the token in Organization administration; the full token is displayed only at issuance. Rotation invalidates the previous token immediately. A token can address only its own Organization slug. Directory credentials and mappings are administrative UI operations, not ordinary Agent REST API resources.
Authorization: Bearer <SCIM_TOKEN>
Content-Type: application/scim+json
Accept: application/scim+json
The service accepts paths with or without a trailing slash. Resource IDs are SCIM UUIDs, distinct from local Django user IDs.
Endpoint reference
Paths are relative to the SCIM base URL. These 17 operations are separate from the downloadable Agent REST OpenAPI document.
| Method | Path | Success response |
|---|---|---|
| GET | /ServiceProviderConfig | 200, supported protocol features and bearer authentication |
| GET | /ResourceTypes | 200, discovery list of User and Group |
| GET | /ResourceTypes/{resource_type} | 200, one resource type |
| GET | /Schemas | 200, discovery list of User and Group schemas |
| GET | /Schemas/{schema_id} | 200, one core schema |
| GET | /Users | 200, paginated User list |
| POST | /Users | 201, User, Location, ETag |
| GET | /Users/{id} | 200, User, ETag |
| PUT | /Users/{id} | 200, replaced User, ETag |
| PATCH | /Users/{id} | 200, updated User, ETag |
| DELETE | /Users/{id} | 204, delete the directory resource and remove Organization access |
| GET | /Groups | 200, paginated Group list |
| POST | /Groups | 201, Group, Location, ETag |
| GET | /Groups/{id} | 200, Group, ETag |
| PUT | /Groups/{id} | 200, replaced Group and membership, ETag |
| PATCH | /Groups/{id} | 200, updated Group, ETag |
| DELETE | /Groups/{id} | 204, remove group and its managed grants |
Discovery requires the same bearer token. Query /ServiceProviderConfig before assuming an IdP feature is supported. PATCH, constrained equality filters, and ETags are supported. Bulk operations, sorting, and password changes are not supported.
Users
Create a user with an email-form userName. Supported profile input includes externalId, displayName, name.givenName, name.familyName, emails, and active (defaults to true).
curl --fail-with-body -X POST "$SCIM_BASE/Users" \
-H "Authorization: Bearer $SCIM_TOKEN" \
-H "Content-Type: application/scim+json" \
-d '{
"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName":"alex@example.com",
"externalId":"directory-user-123",
"name":{"givenName":"Alex","familyName":"Lee"},
"active":true
}'
User responses contain schemas, id, externalId, userName, displayName, name, emails, active, and meta. Each email has a valid value and optional type, display, and boolean primary; at most one entry may be primary. When no collection has been supplied, the response derives one primary work email from userName, including after a rename. An explicit collection is independent of userName; an empty collection stays empty. meta includes resourceType, created, lastModified, version, and location.
PUT supplies the replacement profile: omitted optional strings are cleared, omitted name parts become empty, omitted active defaults to true, and omitted emails restores the derived work-email default. Use PATCH to preserve omitted fields.
Setting active: false removes Organization access while retaining a readable directory resource. Reactivate it with active: true, subject to available seats. Initially inactive users are also blocked from Organization access. SSO, invitations, and manual administrator activation cannot override directory inactivity; those entrypoints report scim_reactivation_required. Revoking the bearer token does not remove this access policy.
DELETE removes the directory resource from user queries, group memberships, and settings counts. Future operations against its ID, including another DELETE, return 404. The underlying platform account and audit history remain. To reprovision, POST a new resource: unambiguous retained directory identifiers reuse the platform account with a new SCIM UUID, while conflicting account matches return 409. Previous group grants are not restored automatically. The service cannot take over protected owner, delegated-administrator, billing, or solutions-partner memberships.
Username and email changes
PUT and PATCH may change userName without changing the resource ID, platform account, or group associations. The normalized username and primary work email must not conflict with another directory identity or platform account. Conflicts return 409; accounts are never merged or rebound.
Uniqueness checks apply when introducing login addresses. A later collision involving an unchanged address does not prevent deactivation or DELETE of the existing resource. Login attempts already in progress when directory identifiers change require a new identity-provider sign-in.
These changes affect only this Organization's directory profile and SSO identity. They do not rewrite the platform account's global email, verified email addresses, or another Organization's identity. SSO continues to require an allowed, verified Organization domain. Existing SSO subjects remain bound to their original account; the current directory username or primary work email authorizes an asserted email change. Updating these login addresses requires fresh Organization authentication. Renames also work before the person's first SSO login.
Groups and grants
Create a group with displayName, optional externalId, and optional members, each with value set to a SCIM User UUID:
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"displayName": "Operations",
"externalId": "directory-group-456",
"members": [{"value": "04bafca2-621b-4cfb-8e3b-4b7f778f1734"}]
}
Group responses add id, meta, and each member's $ref and display. PUT replaces the member set; omitted members means an empty set.
An authorized Identity administrator maps a discovered group to an Organization role, an existing Team, or both. SCIM payloads do not directly grant arbitrary platform roles. Delegated Identity Admins can map Member or Viewer; Admin grants require an Owner or Admin. Removing group membership removes only Team grants managed by SCIM; independently granted Team access is preserved.
Pagination and filtering
startIndex is one-based and defaults to 1. count defaults to 100 and is clamped to 0–200. Lists return:
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults": 0,
"startIndex": 1,
"itemsPerPage": 0,
"Resources": []
}
Advance startIndex by itemsPerPage until all results are read. Users are ordered by user name and ID; Groups by display name and ID.
Only a single equality expression is supported: Users accept userName, externalId, or id; Groups accept displayName, externalId, or id. User-name and group-display-name matching is case-insensitive. Complex boolean expressions and other filter operators return invalidFilter.
curl --fail-with-body -G "$SCIM_BASE/Users" \
-H "Authorization: Bearer $SCIM_TOKEN" \
--data-urlencode 'filter=userName eq "alex@example.com"' \
--data-urlencode 'startIndex=1' --data-urlencode 'count=100'
PATCH and concurrency
Send the SCIM PatchOp schema and a nonempty Operations array. User add/replace supports userName, externalId, displayName, active, name, name.givenName, name.familyName, emails, and pathless object updates. Remove may clear optional name/display/external-ID fields and email collections; removing userName or active is rejected.
Email updates also support type selectors such as emails[type eq "work"].value, including the type, display, and primary subattributes. A selector that matches no entries returns noTarget. Removing a selected entry removes that email; removing its required value is invalid. Unsupported targeted paths return noTarget.
Group PATCH supports add/replace of displayName and externalId, membership add/remove/replace, and removing a specific member with members[value eq "<SCIM-user-uuid>"]. Removing members with no value clears the set.
Operations execute in array order against the preceding operation's result. For example, adding a member and then replacing members with an empty list leaves the group empty. Invalid operations roll back the whole request, including access changes. A successful PATCH increments the resource version once.
The regression fixtures cover Microsoft Entra's work-email PATCH format and Okta's user PUT and deactivation PATCH formats. These supported flows do not imply full provider certification, bulk support, or support for additional enterprise schema attributes.
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [{"op": "replace", "path": "active", "value": false}]
}
Copy the returned weak ETag, for example W/"3", into If-Match on PUT, PATCH, or DELETE. A stale version returns 412. Omitting If-Match or supplying * does not enforce a version match. After 412, re-read and reconcile; do not blindly overwrite a newer directory change.
Errors and troubleshooting
SCIM errors use application/scim+json with schemas, string-valued status, detail, and optional scimType:
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status": "400",
"detail": "This SCIM filter is not supported.",
"scimType": "invalidFilter"
}
Invalid/revoked token or wrong organization returns 401 with WWW-Authenticate: Bearer realm="SCIM". A disabled deployment feature returns 404. Invalid values, unsupported PATCH paths, protected-membership changes, and seat constraints return a SCIM error explaining the rejected operation. Uniqueness conflicts return 409; stale ETags return 412. Inspect Organization security activity when diagnosing provisioning and mapping failures.