SCIM 2.0
SCIM keeps the tenant’s user roster in sync with the customer’s directory, so joiners and leavers arrive without a support ticket. Provisioning is inbound: their directory is the source of truth.
Set it up
Section titled “Set it up”Mint a token, scoped to a connection so that revoking directory sync does not disturb the customer’s SSO:
POST /v1/organizations/org_01J…/directory/scim_tokenAuthorization: Bearer sk_live_…
{ "connection_id": "conn_…" }{ "token": "scim_…", "scim_base_url": "https://acme.oauth.work/scim/v2"}Both values go into the customer’s provisioning configuration. The token is shown once, and
authorizes the SCIM endpoints only — it cannot call /v1.
The customer’s IT admin can mint this themselves through an Admin Portal link with
intent: "dsync".
Authentication
Section titled “Authentication”GET /scim/v2/Users HTTP/1.1Host: acme.oauth.workAuthorization: Bearer scim_…A connection-scoped token may only mutate resources belonging to its own connection. Two directories syncing into one organization cannot overwrite each other’s users or groups.
What is supported
Section titled “What is supported”GET /scim/v2/ServiceProviderConfig{ "patch": { "supported": true }, "bulk": { "supported": false }, "filter": { "supported": true, "maxResults": 200 }, "changePassword": { "supported": false }, "sort": { "supported": false }, "etag": { "supported": false }, "authenticationSchemes": [{ "type": "oauthbearertoken", "name": "OAuth Bearer Token" }]}| Resource | Operations |
|---|---|
/scim/v2/Users | GET (list, filter), POST, GET/PUT/PATCH/DELETE by id |
/scim/v2/Groups | GET (list, filter), POST, GET/PUT/PATCH/DELETE by id |
Filtering
Section titled “Filtering”A supported filter is one or more attribute eq value terms joined by and:
GET /scim/v2/Users?filter=userName eq "ada@acme.com"GET /scim/v2/Users?filter=externalId eq "ext-42" and active eq trueGET /scim/v2/Groups?filter=displayName eq "Engineering"| Resource | Filterable attributes |
|---|---|
| Users | userName, externalId, id, active |
| Groups | displayName, externalId, id |
String values are quoted; active takes the bare booleans true or false. Attribute names and
the eq operator are case-insensitive.
Anything outside that grammar — or, ne, co, sw, pr, grouping, negation, or an attribute not
in the table — is rejected, not ignored:
GET /scim/v2/Users?filter=emails.value eq "ada@acme.com"{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"], "status": "400", "scimType": "invalidFilter", "detail": "filtering on 'emails.value' is not supported (supported: userName, externalId, id, active)"}That is deliberate. A filter the server cannot fully evaluate is refused rather than partially
applied, because answering a narrower question than the one you asked is indistinguishable from
success — a reconciliation sweep would quietly draw the wrong conclusion about who exists and who is
deactivated. Branch on scimType (RFC 7644 §3.12): invalidFilter means the query was rejected,
not that the resource is missing.
Results are capped at 200, matching the maxResults in the configuration document. Bulk operations,
sorting, and ETags are not supported, as that document also states.
Deprovisioning
Section titled “Deprovisioning”Setting active: false — usually through a PATCH when someone leaves — deactivates the user, and
their sessions are killed on the next request rather than at token expiry. That is the
property that matters on a termination: revocation is immediate, not eventual.
{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], "Operations": [{ "op": "replace", "path": "active", "value": false }]}DELETE is also supported. Prefer deactivation: it preserves the audit trail and the user’s history,
and it is what most directories send anyway.
Groups
Section titled “Groups”Groups sync as groups — created, listed, patched for membership, and deleted.
One user, many paths
Section titled “One user, many paths”SCIM provisioning, SAML SSO, and OIDC federation all converge on the same user records, keyed by external id and connection. A user who is synced by the directory and then signs in through SSO is one account, not two — which is what makes deprovisioning actually cut off access rather than leaving a second, SSO-created record behind.
scim_token_created, scim_user_provisioned, scim_user_deprovisioned, and scim_group_updated.
Watching scim_user_deprovisioned is a straightforward way to confirm that offboarding is reaching
you. See audit logs.
Rate limits
Section titled “Rate limits”600 requests per minute, keyed on the token rather than the IP — so a large initial sync is not throttled by unrelated traffic from the same network. See errors and rate limits.