Log in Create workspace
Skip to content

API reference

SCIM v2

Provision workspace users and groups from an identity provider using a dedicated SCIM 2.0 bearer token.

Jump to 13 endpoints
On this page

Overview

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

  1. Discover capabilities

    Call ServiceProviderConfig with the SCIM token and configure the IdP to avoid unsupported bulk, sort, password, or ETag operations.

  2. 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.

  3. 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.

bash
export AGENTOPS_BASE_URL="https://robotscenter.net"
export SCIM_TOKEN="workspace_scim_token_from_secret_manager"

curl --fail-with-body --silent --show-error \
  -X POST \
  -H 'content-type: application/scim+json' \
  -H 'accept: application/scim+json' \
  -H "Authorization: Bearer $SCIM_TOKEN" \
  --data '{"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"],"externalId":"idp-user-example-1042","userName":"jane.operator@example.com","active":true,"name":{"formatted":"Jane Operator"},"emails":[{"value":"jane.operator@example.com","primary":true}],"roles":[{"value":"admin"}]}' \
  "$AGENTOPS_BASE_URL/scim/v2/Users" | jq .
What to expect

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)

Returns: ServiceProviderConfig

Example
{
  "authenticationSchemes": [
    {
      "description": "Workspace SCIM bearer token",
      "name": "Bearer Token",
      "primary": true,
      "type": "oauthbearertoken"
    }
  ],
  "bulk": {
    "maxOperations": 0,
    "maxPayloadSize": 0,
    "supported": false
  },
  "changePassword": {
    "supported": false
  },
  "etag": {
    "supported": false
  },
  "filter": {
    "maxResults": 200,
    "supported": true
  },
  "patch": {
    "supported": true
  },
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"
  ],
  "sort": {
    "supported": false
  }
}
401

Invalid or missing SCIM token

GET
/scim/v2/Users SCIM Token

List SCIM users

Description

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.
Responses
200

Paginated user list

Returns: ScimUserList

Example
{
  "Resources": [
    {
      "active": true,
      "emails": [
        {
          "primary": true,
          "value": "admin@acme.com"
        }
      ],
      "externalId": "ext-user-001",
      "id": "0f1cdac9-305d-4d11-a53d-3fca310c0635",
      "meta": {
        "created": "2026-01-15T10:00:00Z",
        "lastModified": "2026-03-27T09:00:00Z",
        "resourceType": "User"
      },
      "name": {
        "formatted": "Jane Admin"
      },
      "roles": [
        {
          "value": "admin"
        }
      ],
      "schemas": [
        "urn:ietf:params:scim:schemas:core:2.0:User"
      ],
      "userName": "admin@acme.com"
    }
  ],
  "itemsPerPage": 1,
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:ListResponse"
  ],
  "startIndex": 1,
  "totalResults": 1
}
400

SCIM Error: unsupported filter, `scimType` invalidFilter

401

Invalid or missing SCIM token

POST
/scim/v2/Users SCIM Token

Create or update SCIM user

Description

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.
userName string No User email address; retained when omitted on PUT
Responses
200

User created or updated

Returns: ScimUser

Example
{
  "active": true,
  "emails": [
    {
      "primary": true,
      "value": "admin@acme.com"
    }
  ],
  "externalId": "ext-user-001",
  "id": "0f1cdac9-305d-4d11-a53d-3fca310c0635",
  "meta": {
    "created": "2026-01-15T10:00:00Z",
    "lastModified": "2026-03-27T09:00:00Z",
    "resourceType": "User"
  },
  "name": {
    "formatted": "Jane Admin"
  },
  "roles": [
    {
      "value": "admin"
    }
  ],
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User"
  ],
  "userName": "admin@acme.com"
}
401

Invalid or missing SCIM token

403

SCIM Error: archived workspace, or a marketplace-managed workspace cannot add members

409

SCIM Error: uniqueness conflict, an existing account outside this workspace, or last-owner protection (application/scim+json)

422

Invalid userName/email or user data

GET
/scim/v2/Users/:id SCIM Token

Get SCIM user

Description

Returns a provisioned user by user UUID. Non-UUID ids are 404.

Auth

SCIM Token
Parameters
Name In Type Required Description
id path string Yes User UUID
Responses
200

User details

Returns: ScimUser

Example
{
  "active": true,
  "emails": [
    {
      "primary": true,
      "value": "admin@acme.com"
    }
  ],
  "externalId": "ext-user-001",
  "id": "0f1cdac9-305d-4d11-a53d-3fca310c0635",
  "meta": {
    "created": "2026-01-15T10:00:00Z",
    "lastModified": "2026-03-27T09:00:00Z",
    "resourceType": "User"
  },
  "name": {
    "formatted": "Jane Admin"
  },
  "roles": [
    {
      "value": "admin"
    }
  ],
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User"
  ],
  "userName": "admin@acme.com"
}
401

Invalid or missing SCIM token

404

User not found

PUT
/scim/v2/Users/:id SCIM Token

Update SCIM user

Description

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.
userName string No User email address; retained when omitted on PUT
Parameters
Name In Type Required Description
id path string Yes User UUID
Responses
200

User updated

Returns: ScimUser

Example
{
  "active": true,
  "emails": [
    {
      "primary": true,
      "value": "admin@acme.com"
    }
  ],
  "externalId": "ext-user-001",
  "id": "0f1cdac9-305d-4d11-a53d-3fca310c0635",
  "meta": {
    "created": "2026-01-15T10:00:00Z",
    "lastModified": "2026-03-27T09:00:00Z",
    "resourceType": "User"
  },
  "name": {
    "formatted": "Jane Admin"
  },
  "roles": [
    {
      "value": "admin"
    }
  ],
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User"
  ],
  "userName": "admin@acme.com"
}
401

Invalid or missing SCIM token

403

Archived workspace cannot be mutated through SCIM

404

User not found

409

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.
Parameters
Name In Type Required Description
id path string Yes User UUID
Responses
200

User patched

Returns: ScimUser

Example
{
  "active": true,
  "emails": [
    {
      "primary": true,
      "value": "admin@acme.com"
    }
  ],
  "externalId": "ext-user-001",
  "id": "0f1cdac9-305d-4d11-a53d-3fca310c0635",
  "meta": {
    "created": "2026-01-15T10:00:00Z",
    "lastModified": "2026-03-27T09:00:00Z",
    "resourceType": "User"
  },
  "name": {
    "formatted": "Jane Admin"
  },
  "roles": [
    {
      "value": "admin"
    }
  ],
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User"
  ],
  "userName": "admin@acme.com"
}
401

Invalid or missing SCIM token

403

Archived workspace cannot be mutated through SCIM

404

User not found

409

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.
Responses
200

Paginated group list

Returns: ScimGroupList

Example
{
  "Resources": [
    {
      "displayName": "Engineering",
      "externalId": "ext-group-eng",
      "id": "b1c2d3e4-f5a6-7890-bcde-f12345678901",
      "members": [
        {
          "value": "0f1cdac9-305d-4d11-a53d-3fca310c0635"
        }
      ],
      "meta": {
        "resourceType": "Group"
      },
      "schemas": [
        "urn:ietf:params:scim:schemas:core:2.0:Group"
      ]
    }
  ],
  "itemsPerPage": 1,
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:ListResponse"
  ],
  "startIndex": 1,
  "totalResults": 1
}
400

SCIM Error: unsupported filter, `scimType` invalidFilter

401

Invalid or missing SCIM token

POST
/scim/v2/Groups SCIM Token

Create or update SCIM group

Description

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.
externalId string No External group ID (upsert key)
Responses
200

Group created or updated

Returns: ScimGroup

Example
{
  "displayName": "Engineering",
  "externalId": "ext-group-eng",
  "id": "b1c2d3e4-f5a6-7890-bcde-f12345678901",
  "members": [
    {
      "value": "0f1cdac9-305d-4d11-a53d-3fca310c0635"
    }
  ],
  "meta": {
    "resourceType": "Group"
  },
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:Group"
  ]
}
401

Invalid or missing SCIM token

403

Archived workspace cannot be mutated through SCIM

409

SCIM Error: Resource already exists (application/scim+json)

422

Invalid group data

GET
/scim/v2/Groups/:id SCIM Token

Get SCIM group

Description

Returns a provisioned group by id or externalId.

Auth

SCIM Token
Parameters
Name In Type Required Description
id path string Yes Group UUID or externalId
Responses
200

Group details

Returns: ScimGroup

Example
{
  "displayName": "Engineering",
  "externalId": "ext-group-eng",
  "id": "b1c2d3e4-f5a6-7890-bcde-f12345678901",
  "members": [
    {
      "value": "0f1cdac9-305d-4d11-a53d-3fca310c0635"
    }
  ],
  "meta": {
    "resourceType": "Group"
  },
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:Group"
  ]
}
401

Invalid or missing SCIM token

404

Group not found

PUT
/scim/v2/Groups/:id SCIM Token

Update SCIM group

Description

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.
Parameters
Name In Type Required Description
id path string Yes Group UUID
Responses
200

Group updated

Returns: ScimGroup

Example
{
  "displayName": "Engineering",
  "externalId": "ext-group-eng",
  "id": "b1c2d3e4-f5a6-7890-bcde-f12345678901",
  "members": [
    {
      "value": "0f1cdac9-305d-4d11-a53d-3fca310c0635"
    }
  ],
  "meta": {
    "resourceType": "Group"
  },
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:Group"
  ]
}
401

Invalid or missing SCIM token

403

Archived workspace cannot be mutated through SCIM

404

Group not found

409

SCIM Error: Resource already exists (application/scim+json)

PATCH
/scim/v2/Groups/:id SCIM Token

Patch SCIM group

Description

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.
Parameters
Name In Type Required Description
id path string Yes Group UUID
Responses
200

Group patched

Returns: ScimGroup

Example
{
  "displayName": "Engineering",
  "externalId": "ext-group-eng",
  "id": "b1c2d3e4-f5a6-7890-bcde-f12345678901",
  "members": [
    {
      "value": "0f1cdac9-305d-4d11-a53d-3fca310c0635"
    }
  ],
  "meta": {
    "resourceType": "Group"
  },
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:Group"
  ]
}
400

SCIM Error: Unsupported PATCH path, or a non-list members replace (application/scim+json)

401

Invalid or missing SCIM token

403

Archived workspace cannot be mutated through SCIM

404

Group not found

409

SCIM Error: Resource already exists (application/scim+json)

DELETE
/scim/v2/Groups/:id SCIM Token

Delete SCIM group

Description

Deletes a provisioned group and re-runs role reconciliation for remaining groups.

Auth

SCIM Token
Parameters
Name In Type Required Description
id path string Yes Group UUID
Responses
204

Group deleted

401

Invalid or missing SCIM token

403

Archived workspace cannot be mutated through SCIM

404

Group not found

Where to go next

see also