SCIM (System for Cross-domain Identity Management) keeps workspace access in sync with your organization's identity provider. Use it to create users, update group membership, and remove access when people leave. It needs its own workspace SCIM token, not an agent API key, and protects the final workspace owner from accidental deactivation.
SCIM has its own protocol surface
SCIM authentication and controller errors use application/scim+json with SCIM Error schemas. The pre-authentication failure throttle and the post-authentication request limiter are shared platform middleware, so their 429 responses use the shared problem JSON shape instead. The provider config advertises PATCH and filtering but not bulk, sort, ETag, or password change.
Supported filters are deliberately narrow
Filters take a single eq comparison. Users accept userName, emails, emails.value, emails[type eq "work"].value, externalId, or id; Groups accept displayName (case-insensitive), externalId, or id. Anything else is rejected with 400 invalidFilter rather than ignored. Lists default to count 50, cap it at 100, and use 1-based startIndex.
Roles combine explicit and group-derived grants
User roles accept owner, admin, member, or auditor. Omitted PUT fields are retained rather than reset. Existing-user POST upserts retain roles when omitted; a new user receives the default member role. Group display-name tokens can derive admin or auditor but never owner; effective role is the highest explicit or matching-group role, and reconciliation will not demote the last owner.
Before you begin
The command-line examples require curl and jq; any exception is identified in its introduction.
A workspace SCIM token minted by a workspace owner at /app/admin; an agk_, operator cookie, or partner key will not authenticate here.
An identity provider configured with base URL /scim/v2 and Bearer authentication.
Stable IdP externalId values for retry-safe user and group upserts.
How it works
Discover capabilities
Call ServiceProviderConfig with the SCIM token and configure the IdP to avoid unsupported bulk, sort, password, or ETag operations.
Upsert users with stable external IDs
POST Users with the core schema, email in userName, active state, and optional role/name/email fields. The response is HTTP 200 for both create and update. Existing-user POST upserts retain the explicit role if roles is omitted; new users default to member. PUT by ID likewise retains omitted fields.
Sync groups and verify effective roles
Upsert groups by external ID with user UUID members, then read affected users. Group writes reconcile roles; DELETE user deactivates its workspace membership rather than destroying the account.
Examples
These examples are illustrative. Replace the sample values with your own.
Keep credentials in environment variables or a secret manager, never in client-side code.
Upsert one workspace member
Repeated POSTs with the same externalId update the same SCIM-linked identity.
HTTP 200 returns the SCIM User resource with server UUID, effective roles, active state, external ID, normalized email fields, and SCIM metadata.
Troubleshooting
A machine key returns 401
SCIM accepts only the workspace SCIM bearer token. Mint or rotate it in enterprise workspace administration; do not exchange an agk_ key or reuse a metrics/partner credential.
A filter returns 400 invalidFilter
Only a single eq comparison is supported. Users: userName, emails, emails.value, emails[type eq "work"].value, externalId, or id. Groups: displayName, externalId, or id. Split compound (and/or) or non-eq filters into supported lookups.
A role change returns 409
The write conflicts with uniqueness rules, targets an existing account that does not belong to this workspace, or would demote/deactivate the last owner. Invite an existing account to the workspace first, preserve at least one active owner, and read the SCIM error detail before retrying.
An archived workspace is read-only
SCIM reads can still authenticate, but writes are refused with 403 SCIM JSON while the workspace remains archived.
Endpoint reference
Ready to integrate? Open an endpoint for its required permissions, request fields,
response format, and status codes. The examples above explain the workflow;
these details define each individual request.
GET
/scim/v2/ServiceProviderConfig
SCIM Token
Get SCIM service provider configuration
Description
Returns the SCIM 2.0 service provider configuration. Authenticate with
`Authorization: Bearer` using a workspace SCIM token minted or revoked
on Workspace admin (`/app/admin`) — not `/app/workspace`.
This response advertises `filter.maxResults` 200. List endpoints do not
honor that figure: `count` defaults to 50 and is capped at 100.
`filter` supports a single `eq` comparison on the attributes listed
under GET /scim/v2/Users and GET /scim/v2/Groups; any other filter is
`400` with `scimType` `invalidFilter`. Bulk, sort, ETag, and
changePassword are not supported.
Auth
SCIM Token
Responses
200
Service provider configuration (advertised capabilities)
Lists SCIM-linked workspace members as a SCIM ListResponse.
Pagination: `startIndex` is 1-based (default 1). `count` defaults to
50 and is capped at 100 (a requested 500 still returns at most 100).
Filter is not generic SCIM. Only a single `eq` comparison is applied:
`userName eq "…"`, `emails eq "…"`, `emails.value eq "…"`,
`emails[type eq "work"].value eq "…"` (all case-insensitive email
matches), `externalId eq "…"`, or `id eq "…"`. Any other expression
(other operators, `and`/`or`) is `400` with `scimType` `invalidFilter`,
never an unfiltered page.
Auth
SCIM Token
Parameters
Name
In
Type
Required
Description
startIndex
query
integer
No
1-based index of the first result (default 1)
count
query
integer
No
Page size (default 50, maximum 100)
filter
query
string
No
A single `eq` on userName, emails, emails.value, emails[type eq "work"].value, externalId, or id. Anything else is 400 invalidFilter.
Upserts a user by `externalId` (idempotent) or email. `userName` is
the email. `roles` is a list of `{value}` (or bare strings) in
`owner` | `admin` | `member` | `auditor`; omitted roles default to
`member` only for a newly created identity; existing identities retain their roles. Responses include the effective `roles` after group
reconciliation.
An address that already has an account is linked only when that
account already belongs to this workspace (a membership, or an
identity from this workspace's SCIM). Otherwise the response is `409`
with `scimType` `uniqueness`: invite the user to the workspace, and
once they accept, the IdP's retry links the account. A
marketplace-managed workspace cannot add members (`403`).
`409` `application/scim+json` is returned for uniqueness conflicts
(`Resource already exists`), an existing account outside the
workspace, and last-owner protection
(`Cannot modify the last workspace owner`).
Auth
SCIM Token
Request Body
Field
Type
Required
Description
active
boolean
No
Whether the membership is active (false deprovisions)
name
object
No
User name object. `formatted` is the display name.
emails
array<object>
No
User email addresses
schemas
array<object>
Yes
SCIM schema URIs
externalId
string
No
IdP external id. Used as the upsert idempotency key.
roles
array<object>
No
Workspace roles. Each item is `{value}` or a string: owner, admin, member, or auditor. Defaults to member only for a newly created identity; existing identities retain their roles.
Full replacement of a provisioned user. Omitted email/name/active/roles fields retain their existing values; `userName` is optional unless needed to identify a new address. Accepts the same attributes as
create, including `roles` (`owner` | `admin` | `member` | `auditor`)
and `emails`. Last-owner demotion/deactivation and uniqueness
conflicts are `409` application/scim+json.
Auth
SCIM Token
Request Body
Field
Type
Required
Description
active
boolean
No
Whether the membership is active
name
object
No
User name object (`formatted`)
emails
array<object>
No
User email addresses
schemas
array<object>
Yes
SCIM schema URIs
externalId
string
No
IdP external id
roles
array<object>
No
Workspace roles (`owner` | `admin` | `member` | `auditor`). Default keeps the existing role when omitted on update.
SCIM Error: uniqueness conflict or last-owner protection (application/scim+json)
422
Invalid user data
PATCH
/scim/v2/Users/:id
SCIM Token
Patch SCIM user
Description
SCIM PATCH. Honored `replace` paths: `active`, `name.formatted`,
`userName`, and `roles`. A pathless `replace` may also set
`active`, `userName`, `name`, `roles`, `emails`, and `externalId`.
Other operations are ignored. Last-owner and uniqueness conflicts
are `409` application/scim+json.
Auth
SCIM Token
Request Body
Field
Type
Required
Description
schemas
array<object>
Yes
SCIM schema URIs
Operations
array<object>
No
PATCH operations. replace on active | name.formatted | userName | roles, or a pathless replace map.
SCIM Error: uniqueness conflict or last-owner protection (application/scim+json)
422
Invalid user data
DELETE
/scim/v2/Users/:id
SCIM Token
Deactivate SCIM user
Description
Soft-deletes the membership (`status` disabled). Deactivating the last
workspace owner is `409` `Cannot modify the last workspace owner`
(`application/scim+json`). Already-disabled users succeed as 204.
Auth
SCIM Token
Parameters
Name
In
Type
Required
Description
id
path
string
Yes
User UUID
Responses
204
User deactivated
401
Invalid or missing SCIM token
403
Archived workspace cannot be mutated through SCIM
404
User not found
409
Cannot modify the last workspace owner (application/scim+json)
GET
/scim/v2/Groups
SCIM Token
List SCIM groups
Description
Lists provisioned groups stored on the workspace. Same pagination as
Users: `startIndex` default 1, `count` default 50, maximum 100.
`filter` accepts a single `eq` comparison: `displayName eq "…"`
(case-insensitive, the lookup Entra ID and Okta use before creating a
group), `externalId eq "…"`, or `id eq "…"`. Anything else is `400`
with `scimType` `invalidFilter`.
Auth
SCIM Token
Parameters
Name
In
Type
Required
Description
startIndex
query
integer
No
1-based index of the first result (default 1)
count
query
integer
No
Page size (default 50, maximum 100)
filter
query
string
No
A single `eq` on displayName (case-insensitive), externalId, or id. Anything else is 400 invalidFilter.
Upserts a group by `externalId`. `members` is a list of user UUIDs
(`{value}`). After write, membership roles are reconciled from each
group's `displayName` tokens:
- `admin` / `admins` / `administrator` / `administrators` → `admin`
- `auditor` / `auditors` → `auditor`
- names starting with `non-` / `not-` stay `member`
- `owner` is never granted from a group name (`Workspace Owners` stays member)
A user's effective role is the highest of their explicit SCIM `roles`
and every matching group (owner > admin > auditor > member). The last
workspace owner is never demoted by group sync.
Uniqueness conflicts are `409` `Resource already exists`.
Auth
SCIM Token
Request Body
Field
Type
Required
Description
members
array<object>
No
Member user UUIDs (`{value}` or equivalent)
schemas
array<object>
Yes
SCIM schema URIs
displayName
string
Yes
Group display name. Tokens grant admin or auditor as described above; owner is never granted from the name.
Replaces supplied `displayName` and `members` (omitted fields are retained; an explicit empty members list removes all members), then reconciles workspace roles
from displayName tokens (admin/auditor only; owner is never granted
from a group name). Uniqueness conflicts are `409`.
Auth
SCIM Token
Request Body
Field
Type
Required
Description
members
array<object>
No
Member user UUIDs
schemas
array<object>
Yes
SCIM schema URIs
displayName
string
No
Group display name. Tokens grant admin or auditor; owner is never granted from the name.
Honored `replace` paths: `displayName` and `members`. `add` and
`remove` on `members` (including `members[value eq "<id>"]`) apply in
request order. An unsupported `members` filter is `400`, and so is a
`members` replace whose value is not a list (`scimType`
`invalidValue`). Other operations are ignored. Role reconciliation
from the new displayName and members is the same as create/update.
Uniqueness conflicts are `409`.
Auth
SCIM Token
Request Body
Field
Type
Required
Description
schemas
array<object>
Yes
SCIM schema URIs
Operations
array<object>
No
PATCH operations. replace on displayName or members; add or remove on members.