SCIM 2.0 reference

Endpoints, attributes, filters, PATCH semantics, provisioning rules, error codes, and example requests for provisioning workspace users and groups from an identity provider.

Written for
integration-developer, admin
Roles
owner, admin
Requires
Owner or Admin to create the SCIM token; Business or Enterprise plan (or trial) to provision; deprovisioning works on every plan
Feature
authentication

SAQ implements SCIM 2.0 (RFC 7643 and 7644) so that an identity provider can create, update, deactivate, and group workspace users. This page is the wire-level reference; an Owner or Admin turns SCIM on and creates the token as described under SCIM provisioning.

Base URL and authentication

  • Base URL: https://saq.no/scim/v2.
  • Authentication: Authorization: Bearer saq_scim_…, a token created under Settings → Authentication → SCIM provisioning. It is shown once and can be revoked there. Cookies are ignored; API tokens (saq_…) are refused.
  • Content type: application/scim+json in responses; JSON request bodies up to 256 KiB (413 above).
  • Responses use the standard SCIM error shape: { "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"], "status": "400", "detail": "...", "scimType": "invalidValue" }.

The token carries the authority of the Owner or Admin who created it: a token created by an Admin can grant Admin or Member, only a token created by an Owner can grant Owner, and an Admin-created token can neither demote nor deactivate an Owner (refused with 403).

Endpoints

Endpoint Methods Notes
/ServiceProviderConfig, /ResourceTypes, /Schemas GET Discovery
/Users GET, POST List with filter, startIndex, count; create answers 201 with a Location header
/Users/{id} GET, PUT, PATCH, DELETE DELETE deactivates and answers 204
/Groups GET, POST List with filter, startIndex, count
/Groups/{id} GET, PUT, PATCH, DELETE Rename, replace or change members, delete

Filters and paging

Only attribute eq "value" is supported. Users filter by userName or externalId; groups by displayName or externalId. Anything else, including emails.value, displayName on users, and userName on groups, answers 400 invalidFilter.

Paging is 1-based: startIndex (default 1) and count (default 100, maximum 200). List responses carry totalResults, startIndex, itemsPerPage, and Resources.

User attributes

Attribute Behaviour
userName Required. The person's email address and login key. Immutable: a PUT or PATCH that changes it answers 409 with scimType: "mutability"
externalId Stored; unique per workspace (409 uniqueness on a duplicate)
displayName, name.givenName, name.familyName, name.formatted Stored. The display name is displayName, else name.formatted, else given and family name, else the local part of the address. SAQ renames only identities it created itself; an adopted existing account keeps the name its owner chose
active true on create unless given; a PUT that omits it leaves it unchanged. false deactivates
emails Read-only; derived from userName. Not consulted on input
groups Read-only; the groups the user belongs to
urn:arktiq:saq:role Read-only; the workspace role in effect: owner, admin, or member
Other attributes (title, phoneNumbers, addresses, …) Accepted and ignored

Group attributes and members

A group has displayName (required, unique per workspace regardless of case), externalId, and members (a list of { "value": "<user id>" }). Member values must be SCIM user ids from this workspace; anything else answers 400 invalidValue. A PUT that omits members keeps the current list.

The workspace maps group names to roles under Settings → Authentication → SCIM provisioning. A user's role is the highest role among the mapped groups they belong to, else the default role. Group changes apply at once, in both directions.

PATCH semantics

Operations add and replace accept either a path or a value object. Supported user paths: active, displayName, externalId, name, name.givenName, name.familyName, name.formatted. Paths may carry the schema URN prefix. remove clears externalId, displayName, or name.*; removing active is refused; removing an attribute SAQ does not store is accepted and ignored so a bundled deactivation still applies.

String booleans are accepted for active ("True", "False"), as Microsoft Entra ID sends them.

Group PATCH: add/replace/remove on members (a list or a single member), remove with members[value eq "<id>"], and replace of displayName or externalId.

Provisioning rules

  • Verified domain. A userName whose domain the workspace has not verified by DNS is refused with 400 invalidValue ("The domain of userName is not verified in this workspace"). Invite guests on foreign domains instead.
  • Adoption. A create for an address that already has an account adopts it: the person keeps their profile, and a role above what the mapping says is kept. A create for a resource that was deactivated reactivates it under the same id. A create for an active resource answers 409 uniqueness.
  • Exclusions. Agent accounts and client users of the workspace cannot be provisioned (409 uniqueness). An account that was deleted globally answers 409 mutability.
  • Seats. Creating a member beyond the plan's seats answers 409 (seat_limit).
  • Deactivation (active: false or DELETE) removes the membership, revokes the person's tokens, project memberships, and viewer grants, and pseudonymises their workspace-local profile ("Former member N"). The global account and other workspaces are untouched; the SCIM resource keeps its id so the provider can reactivate it. The last Owner cannot be deactivated (409).
  • Role changes that cross between Member and Owner/Admin revoke the person's personal API tokens.

Unsupported features

ServiceProviderConfig reports them: no sorting, no bulk operations, no ETags, no changePassword. Filters other than eq are refused.

Plan gating and read-only workspaces

SCIM provisioning is included from the Business plan (and in the trial). On a plan without it, existing tokens may only deprovision: DELETE and active: false pass, everything else answers 403 ("SCIM provisioning is not included in this plan"). A read-only workspace behaves the same way ("This workspace is read-only"). A workspace where SCIM is turned off answers 403 ("SCIM provisioning is turned off for this workspace"); an unknown or revoked token answers 401.

Rate limits

600 requests per minute per client address and 120 per token; both answer 429.

Examples

Create a user:

curl -X POST "https://saq.no/scim/v2/Users" \
  -H "Authorization: Bearer saq_scim_YOUR_TOKEN" \
  -H "Content-Type: application/scim+json" \
  -d '{
    "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
    "userName": "kari@acme.example",
    "externalId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
    "name": { "givenName": "Kari", "familyName": "Nordmann" },
    "active": true
  }'

Response (201):

{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "id": "01924c3e-6b2a-7d11-9d0e-3a1f4b5c6d7e",
  "externalId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "userName": "kari@acme.example",
  "displayName": "Kari Nordmann",
  "name": { "formatted": "Kari Nordmann" },
  "active": true,
  "emails": [{ "value": "kari@acme.example", "primary": true }],
  "groups": [],
  "urn:arktiq:saq:role": "member",
  "meta": {
    "resourceType": "User",
    "created": "2026-09-19T10:15:00.000Z",
    "lastModified": "2026-09-19T10:15:00.000Z",
    "location": "https://saq.no/scim/v2/Users/01924c3e-6b2a-7d11-9d0e-3a1f4b5c6d7e"
  }
}

Deactivate a user:

curl -X PATCH "https://saq.no/scim/v2/Users/01924c3e-6b2a-7d11-9d0e-3a1f4b5c6d7e" \
  -H "Authorization: Bearer saq_scim_YOUR_TOKEN" \
  -H "Content-Type: application/scim+json" \
  -d '{
    "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
    "Operations": [{ "op": "replace", "path": "active", "value": false }]
  }'

Create a group:

curl -X POST "https://saq.no/scim/v2/Groups" \
  -H "Authorization: Bearer saq_scim_YOUR_TOKEN" \
  -H "Content-Type: application/scim+json" \
  -d '{
    "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
    "displayName": "SAQ admins",
    "externalId": "3f2504e0-4f89-11d3-9a0c-0305e82c3301",
    "members": []
  }'

Add a member to a group:

curl -X PATCH "https://saq.no/scim/v2/Groups/01924c3e-0000-7000-8000-000000000010" \
  -H "Authorization: Bearer saq_scim_YOUR_TOKEN" \
  -H "Content-Type: application/scim+json" \
  -d '{
    "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
    "Operations": [{
      "op": "add",
      "path": "members",
      "value": [{ "value": "01924c3e-6b2a-7d11-9d0e-3a1f4b5c6d7e" }]
    }]
  }'

Microsoft Entra ID notes

  • Entra sends userPrincipalName as userName and mail as the primary email; SAQ keys on userName only, which is why emails.value filters are refused.
  • Entra sends active as the strings "True" and "False"; both are accepted.
  • Group PATCH values use SAQ's SCIM user ids (the id in each User resource), not Entra object ids. Put Entra object ids in externalId for lookups.
  • Group names must be unique in the workspace regardless of case; map the names Entra sends under Settings → Authentication.