← All docs

User provisioning (SCIM)

Let your identity provider manage who is in your organization. People assigned to LayerSift in Okta, Microsoft Entra ID, OneLogin or JumpCloud become members, renames follow them, and taking someone out of the app in the identity provider takes away their access here at once, sessions included.

This is SCIM 2.0 (RFC 7643, RFC 7644), Users only.

Base URL       https://<your LayerSift host>/api/scim/v2
Authorization  Bearer scim_live_…
Content-Type   application/scim+json

Before you start: single sign-on with a proved domain

Provisioning builds on single sign-on: set up the connection under Settings → Single sign-on and prove your email domain with its DNS record first.

The reason is the rule single sign-on itself lives by. An identity provider can assert any address, so it is believed only for domains your organization has proved it owns. Provisioning follows that rule:

  • On your verified domains it creates accounts and adds people. They sign in with Sign in with single sign-on; the account it creates has a password nobody knows (never an empty one), so single sign-on is its way in, and nobody can sign up with that address in the meantime and take it. An account that already exists on such an address, and whose address nobody has proved, is claimed before it is linked. Sign-up never proves an address, so someone may have registered the address with a password of their own — and a verification link proves it only for someone signed in to the account it was sent for, so the address's owner following one does not prove it for them. A reset link the owner follows claims the account just as below (its user.password_reset audit entry says secondFactorsRemoved). The claim replaces that password with one nobody knows, removes its authenticator app, recovery codes and passkeys, and ends every session it had. The person then signs in with single sign-on. The claim is audited as account.claimed. A single sign-on sign-in that proves the address makes the same claim. An account whose address is already proved is linked as it is, with its password, second factors and sessions. Owners are claimed too: nothing an owner was given proves the address. Keep one owner on an address outside the domains single sign-on speaks for — no claim touches it, enforcement does not apply to it, and it is the way back in when the provider fails.
  • Anywhere else it only links people who are already members (a contractor you invited, say, who set their own password). Adding anyone else takes an invitation they accept. The refusal is the same whether or not an account exists for the address, so a token cannot be used to find out.

Create a token

Tokens are minted with a management key that holds members:write, and shown once:

curl -X POST https://<host>/api/management/v1/scim-tokens \
  -H "Authorization: Bearer mk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Okta" }'
{
  "id": "sct_…",
  "name": "Okta",
  "token": "scim_live_…",
  "scimBaseUrl": "https://<host>/api/scim/v2",
  "createdAt": "2026-10-23T12:00:00.000Z",
  "warning": "Store this value now — it is not retrievable again."
}

Name it after the identity provider that will hold it: every change the token makes is audited under that name. Only the token's SHA-256 is stored.

GET  | POST    /api/management/v1/scim-tokens                 members:read | members:write
GET  | DELETE  /api/management/v1/scim-tokens/{tokenId}       members:read | members:write

GET lists them as scim_live_ab12cd…wxyz, revoked ones with ?includeRevoked=true. DELETE revokes: the provider's next request is 401, and the people it provisioned stay as they are. To rotate, mint a new token, put it in the provider, then revoke the old one. An organization holds at most ten active tokens.

A token lives as long as the management key that minted it. Revoking that key, in the dashboard or through the API, revokes its tokens in the same step, and so does deleting the environment the key belongs to. The provider's next request is 401.

Configure your identity provider

Every provider asks for the same three things:

SCIM base URL https://<host>/api/scim/v2 (the scimBaseUrl above)
Authentication HTTP header / Bearer token: the scim_live_… value
Unique identifier userName

Then turn on creating users, updating user attributes and deactivating users. There is nothing to configure for groups or passwords: neither is supported.

Okta. In the app's provisioning settings, choose SCIM 2.0 with HTTP Header authentication and userName as the unique identifier; enable Push New Users, Push Profile Updates and Deactivate Users (leave Push Groups and password sync off — a password Okta sends is ignored). Okta deactivates by PATCH {"op":"replace","value":{"active":false}}, which is supported as is.

Microsoft Entra ID. In the enterprise application's Provisioning, set the mode to Automatic, the Tenant URL to the base URL and the Secret Token to the token; Test Connection reads /Users. Keep the default user mappings (userPrincipalName → userName, mail → emails[type eq "work"].value, Switch([IsSoftDeleted]…) → active); the enterprise extension attributes it sends are accepted and ignored. Turn group provisioning off. Entra ID sends booleans as "True"/"False" strings and ops as "Replace": both are understood.

OneLogin, JumpCloud and other SCIM 2.0 clients: the same three settings, SCIM 2.0 with a bearer token, userName as the identifier.

Turn on assignment required (Entra ID: Properties → Assignment required?) so the provider only signs in the people it provisions.

What each request does

A SCIM User is a person's membership of this organization. Its id is the provisioning record's (scim_…), not the account's: one account can belong to many organizations, and a provider speaks only for its own.

Request Effect
POST /Users Finds the account by address, or creates it (verified domains only); makes them a member, or links the membership they already have. New members join as member — as viewer if single sign-on makes newcomers viewers. Never admin or owner. Pending invitations for the address are revoked. Counts against your plan's seats.
GET /Users, GET /Users/{id} The people this organization's provider manages.
PUT /Users/{id} Replaces the profile. Attributes left out are cleared, except active and emails, which stay as they are.
PATCH /Users/{id} add, replace, remove, with or without a path; all operations or none. Without a path, the value may name attributes under the core User URN: {"urn:ietf:params:scim:schemas:core:2.0:User": {"active": false}}.
active: false Deactivates (below).
active: true Re-activates: the membership comes back with the role it had (at most member).
DELETE /Users/{id} Removes the membership and ends their sessions here. The account is never deleted — it is the person's, and may belong to other organizations.

Deactivation keeps the user resource but takes away access:

  • the membership is removed — it is what every page, action and API call checks, so nothing of the organization is reachable from that moment;
  • the sessions they had open on the organization end, and so do any sessions your single sign-on issued them, wherever they are now; sessions on other organizations they belong to are theirs to keep;
  • pending invitations to the organization for their address are revoked;
  • while they are deactivated nothing else can make them a member again — not a sign-in through single sign-on (which otherwise joins people on their first sign-in), not an invitation. Only re-activating them in the provider, or the provider deleting them, lifts that.

A request that deactivates always lands. If the same request also asks for a change that cannot be made (a userName another person already has, or an address that is not one), that change is left out and the answer is 200 with the resource as stored. The audit entry names what was left out. Owners and admins still cannot be deactivated (below).

A provider that sends active: true for someone an admin removed in the dashboard brings them back: when provisioning is on, the identity provider is the source of truth for who works here. Remove people in the provider.

What a token cannot do

A token acts with the authority of the management key that minted it — an admin's — and no more:

  • Owners and admins can be linked and read, and the profile the provider sends for them is recorded, but a token cannot deactivate or delete them (403). Change their role or remove them in the dashboard. Like single sign-on's enforcement, which never shuts an owner out, this keeps a way back in on the day the provider is down or misconfigured. The organization's last owner is refused on its own as well (409).
  • Another organization's people: invisible. Their ids are 404, as for anything that does not exist.
  • Addresses. What the provider says a person's addresses are is recorded on their SCIM resource and answered back as sent. It does not change the account: the account keeps the address it signs in with, and single sign-on finds an account by that address only. An address change at the provider therefore shows in GET /Users, and the person keeps signing in as before. A name follows the provider while the person's address there is the account's own and is on your verified domains; for linked people elsewhere the account's own name is left alone.
  • Passwords: never set, never changed.

Attributes

SCIM LayerSift
id The provisioning record (scim_…)
userName Unique in the organization, case-insensitively
externalId The provider's id, case-exact
name.givenName, familyName, formatted, middleName, honorificPrefix, honorificSuffix Kept on the record
displayName Kept, and the account's name on your verified domains
emails Kept as the provider sent them, and answered as they were sent. Until it sends some, the account's address, as one work email. The address the person signs in with is the account's own and does not change here
active Whether they can use the organization
meta.created, lastModified, location

Other core attributes (title, phoneNumbers, addresses, locale, password, groups, roles, …) and extension attributes are accepted and ignored, so default mappings work unchanged. An attribute that does not exist on a User at all is 400 invalidPath.

Lists and filters

GET /Users?filter=…&startIndex=1&count=100. Pages are 1-based, in creation order; count is at most 200 (0 returns just totalResults). Resources is always present, even when empty.

Filters support eq ne co sw ew gt ge lt le pr, and, or, not ( … ), parentheses and value paths, on id, externalId, userName, displayName, active, name.*, emails, emails.value/type/primary and meta.*:

userName eq "[email protected]"
emails[type eq "work"].value eq "[email protected]"
externalId eq "00u1abcd"
active eq false
userName sw "a" and not (externalId pr)

userName and email comparisons ignore case; id and externalId do not. Anything else is 400 invalidFilter. Sorting, attributes and excludedAttributes are not supported and are ignored.

A list without a filter is paged in the database, and totalResults is counted there. A filter that requires userName, externalId or id to equal a value (at its top level, joined by and to the rest) reads only the matching people, so the lookup a provider makes before it creates someone stays quick however large the organization is. The whole filter still decides what matches.

Discovery

GET /api/scim/v2/ServiceProviderConfig     patch, filter (max 200); no bulk, sort, etag, password change
GET /api/scim/v2/ResourceTypes             User
GET /api/scim/v2/Schemas                   the core User schema

All need the token. /Groups and anything else under the base URL answer a SCIM 404 saying so; /Bulk and /Me are 501.

Errors

Every error is in SCIM's format, with a detail written for the admin reading the provider's provisioning log:

{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
  "status": "400",
  "scimType": "invalidFilter",
  "detail": "Filtering on \"title\" is not supported."
}
Status When
400 invalidSyntax (not JSON, no Operations), invalidValue, invalidFilter, invalidPath, noTarget, mutability
401 No token, an unknown one, or a revoked one
403 An address not on your verified domains; an owner or admin; no seats left; the organization already keeps 1,000 deactivated people (tooMany)
404 No such user in this organization
409 uniqueness (the userName, or the person, is already provisioned; an address taken); the last owner
413 A body over 64 KB
429 Over the rate limit, or more than 60 new people a minute for the organization (tooMany); honour Retry-After

Limits

  • 1,800 requests a minute per token — 30 a second, above the 25 Microsoft asks a SCIM service to sustain.
  • Requests without a valid token are counted per source address and refused for a while after 30 in a minute.
  • An organization can bring at most 60 new people into it a minute: a person whose address has no account yet. Linking someone who already has an account is not counted. Past that, 429 with tooMany.
  • An organization keeps at most 1,000 deactivated people provisioned. Creating another one as inactive — a person whose address has no account yet — is 403 with tooMany, and the detail says so. Linking someone who already has an account as inactive is not refused: it makes no account, and there are only as many as people have made on your verified domains.
  • Bodies up to 64 KB.

Audit

Every change is in the audit log under scim:<token name>, with the token's id in its metadata:

Action
scim_token.created, scim_token.revoked By the management key, under key:<name>
scim.user.created Says whether it linked an existing member and whether it created the account
scim.user.updated Names the attributes that changed; a userName change with from and to; the addresses as sent, with from and to
scim.user.deactivated With how many sessions it ended, and any change it left out (skipped)
scim.user.reactivated With the role given back
scim.user.deleted
account.claimed An account nobody had proved, claimed when a provider linked it or single sign-on proved its address: its password, second factors and sessions were taken away. Names the organization and the reason

Requests that change nothing — providers re-send state — are not audited.