Skip to Content
SecuritySCIM 2.0 Provisioning

SCIM 2.0 Provisioning

Asiri implements SCIM 2.0 (RFC 7644) so your identity provider can create, suspend, and delete users in your Asiri workspace without manual intervention. Combine it with SSO for end-to-end identity lifecycle: on-boarding flows from your IdP into Asiri, sign-in goes through SAML or OIDC, off-boarding revokes access in both directions.

The token that authorises SCIM requests is per-workspace and minted at Settings → SSO → SCIM token.

Base URL

https://api.asiri.ng/scim/v2

SCIM lives at the root of the API host — it does not sit under /v1. That’s required by RFC 7644; the protocol pins both the URL shape and the application/scim+json content type.

Authentication

Every SCIM request must include your per-workspace token as a bearer header:

GET /scim/v2/Users HTTP/1.1 Host: api.asiri.ng Authorization: Bearer scim_<your-token> Accept: application/scim+json

Tokens are prefixed scim_, are shown to you once at mint time, and are hashed at rest — Asiri cannot recover the value for you. Minting a new token rotates and invalidates any previous one. Revoke any token you suspect has leaked by minting a replacement.

Supported endpoints

MethodPathPurpose
GET/ServiceProviderConfigCapability discovery (unauthenticated).
GET/UsersList users with optional filter and pagination.
POST/UsersProvision a new user.
GET/Users/{id}Read a single user.
PATCH/Users/{id}Activate/suspend a user or replace their role.
DELETE/Users/{id}Soft-delete a user (de-provision).
GET/GroupsList the fixed set of Asiri role groups.
GET/Groups/{role}Read one role group.
POST/GroupsIdempotently validate a fixed role group.
PUT/Groups/{role}Replace members of a fixed role group.
PATCH/Groups/{role}Add, remove, or replace role-group members.
DELETE/Groups/{role}Clear assignments for a fixed role group.

PUT /Users/{id} (full replace) is not supported. Groups are platform-defined — member operations mutate workspace role assignments, not platform role definitions.

Filter syntax

GET /Users accepts a single equality filter on one of two attributes:

?filter=userName eq "alice@acme.com" ?filter=externalId eq "9b1c…"

Any other operator (and, or, sw, gt, …) returns 400 invalidFilter. Pagination uses startIndex (1-indexed) and count; the maximum page size is 100.

GET /scim/v2/Users?startIndex=1&count=50 HTTP/1.1

User lifecycle

Provisioning (POST /Users)

{ "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], "userName": "alice@acme.com", "name": { "givenName": "Alice", "familyName": "Okafor" }, "active": true }

userName must be an email address. The user is created (or attached, if a global Asiri user with the same email already exists) and granted membership in your workspace with the role set in Settings → SSO → Default role. Provisioning a user who is already a member returns 409 uniqueness.

The response includes a SCIM resource id — Asiri uses the same id as the underlying user record, so it round-trips through externalId as well.

You can assign a role at create time with the SCIM roles array:

{ "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], "userName": "alice@acme.com", "roles": [{ "value": "dpo", "primary": true }] }

If roles is omitted, Asiri uses Settings → SSO → Default role.

Suspending, reactivating, and changing role (PATCH /Users/{id})

PATCH /Users/{id} supports replace on active and roles:

{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], "Operations": [{ "op": "replace", "path": "active", "value": false }] }
{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], "Operations": [{ "op": "replace", "path": "roles", "value": [{ "value": "auditor" }] }] }

Setting active: false suspends the user — they can no longer sign in but their data and history are preserved. Setting it back to true reactivates them. Replacing roles updates the user’s TenantMembership.role. Any operation other than replace, or any path other than active or roles, returns 400 invalidPath.

De-provisioning (DELETE /Users/{id})

Soft-deletes the user’s workspace membership. The user’s global identity is preserved, so re-adding the same email later (via SCIM or invitation) restores their previous audit history under the same id. Returns 204 No Content.

Groups and role push

GET /Groups returns one entry per SCIM-assignable Asiri role. Roles are a closed enum and cannot be created as new custom roles through SCIM. Asiri still accepts the common SCIM group-push lifecycle that IdPs use for fixed groups:

  • POST /Groups with displayName: "dpo" validates and returns the existing fixed group.
  • PATCH /Groups/{role} with add, remove, or replace mutates members assigned to that role.
  • PUT /Groups/{role} replaces the full member list for that role.
  • DELETE /Groups/{role} clears assignments for that role by suspending matching tenant memberships. It does not delete users, and it does not delete the Asiri role.
idDescription
adminTenant administrator — full access
dpoData Protection Officer — compliance + DSR oversight
engineerEngineering operator — implementation work
auditorRead-only auditor access
cs_agentCustomer support agent — limited access

Most IdPs use the /Groups listing to map their own groups onto Asiri roles in their provisioning rules.

Add a member to a role group

{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], "Operations": [{ "op": "add", "path": "members", "value": [{ "value": "user-uuid" }] }] }

Remove a member from a role group

{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], "Operations": [{ "op": "remove", "path": "members[value eq \"user-uuid\"]" }] }

Removing a user from their current role group suspends their membership for fail-closed access. If the IdP intends to move the user to another role, send an add or replace operation for the new role.

Configurable group-to-role mapping

By default, group ids are the Asiri role ids: admin, dpo, engineer, auditor, and cs_agent. Advanced tenants can map IdP group names to Asiri roles by setting attributeMap.scimGroupRoleMap in the SSO configuration, for example:

{ "scimGroupRoleMap": { "ASIRI DPOs": "dpo", "ASIRI Read Only": "auditor" } }

The dpco_principal role exists on the platform but is not assignable via SCIM (GET /Groups/dpco_principal returns 404). It grants cross-tenant visibility to a DPCO firm’s principal, so auto-provisioning it via an IdP group-mapping would be a privilege-escalation vector. It is granted by a platform administrator directly on the membership record, never through SCIM or SSO.

Service Provider configuration

GET /ServiceProviderConfig reports Asiri’s SCIM capabilities. The relevant facts:

  • patch.supported: true
  • filter.supported: true, maxResults: 100
  • bulk.supported: false
  • changePassword.supported: false — passwords are handled by SSO / Better Auth
  • sort.supported: false
  • etag.supported: false
  • authenticationSchemes: OAuth bearer token (scim_*)

Troubleshooting

401 Token is not a SCIM token

The bearer token doesn’t start with scim_. You may be sending an Asiri API key (asiri_live_*) — those are not interchangeable. Mint a SCIM token at Settings → SSO.

400 Unsupported filter

Only userName eq "..." and externalId eq "..." are supported. Re-shape your IdP’s matching rule, or fetch the full page and match client-side.

409 User is already a member

The user exists in your workspace. Either skip the create (if the IdP already has the user id, use it directly) or PATCH the existing record to reactivate.

400 PATCH path "..." not supported

The user PATCH surface accepts replace on active or roles. Group PATCH accepts add, remove, or replace for members.