Skip to content

SCIM API

Identity providers call these endpoints for you; most administrators only need SCIM provisioning. This page is for people connecting an identity provider (IdP) that has no ready-made SCIM connector, and for security reviews.

There are two sets of endpoints:

  • /scim/v2/…, which the IdP calls with a SCIM bearer token. They follow SCIM 2.0 (RFC 7643 and RFC 7644).
  • /api/v1/settings/sso/scim…, which a signed-in administrator uses to create and revoke those tokens and to say whether SCIM may give privileged roles.
Base URL Your SpeculaPro address followed by /scim/v2. GET /api/v1/settings/sso/scim returns it as base_url. Self-hosted: it is built from SP_SSO_PUBLIC_URL
Authentication Authorization: Bearer <token>. A SCIM token works only here: it is not a session and not an API key, and no cookie is read
Scope A token belongs to one workspace. Other workspaces’ people and groups don’t exist as far as it can tell
Content type Answers are application/scim+json
Rate limits About 3,000 requests a minute per token. Thirty wrong tokens a minute from one address. Beyond either: 429 with Retry-After: 30

A token looks like scim_ followed by 12 characters, an underscore and the secret. The first part (up to the second underscore) is the prefix shown in the app.

Discovery

Endpoint Returns
GET /scim/v2/ServiceProviderConfig What is supported: PATCH and filters yes (at most 200 results); bulk, password change, sort and ETags no
GET /scim/v2/ResourceTypes, GET /scim/v2/ResourceTypes/{id} User and Group
GET /scim/v2/Schemas, GET /scim/v2/Schemas/{id} The attributes kept for each

People

Endpoint Does Success
GET /scim/v2/Users List people, optionally filtered 200
POST /scim/v2/Users Create an account 201 with a Location header
GET /scim/v2/Users/{id} One person 200
PUT /scim/v2/Users/{id} Replace the attributes SpeculaPro keeps 200
PATCH /scim/v2/Users/{id} Change some attributes 200
DELETE /scim/v2/Users/{id} Deactivate the account and end its sessions. Nobody is deleted 204

Groups

Endpoint Does Success
GET /scim/v2/Groups List groups, optionally filtered 200
POST /scim/v2/Groups Create a group, with members if given 201 with a Location header
GET /scim/v2/Groups/{id} One group 200
PUT /scim/v2/Groups/{id} Replace the name and the whole member list 200
PATCH /scim/v2/Groups/{id} Add or remove members, or rename 200
DELETE /scim/v2/Groups/{id} Delete the group. Members stay and lose the roles its name gave them 204

Tokens (signed-in administrators)

Endpoint Does Permission Success
GET /api/v1/settings/sso/scim The base URL, the tokens, newest first (never a secret), and the privileged-roles setting sso.manage or settings.view 200
PUT /api/v1/settings/sso/scim/privileged-roles Let SCIM give and take away privileged roles, or stop it sso.manage 204, or 202 when sent for approval
POST /api/v1/settings/sso/scim/tokens Create a token sso.manage 201, or 202 when sent for approval
DELETE /api/v1/settings/sso/scim/tokens/{id} Revoke a token at once. Never needs approval sso.manage 204
Attribute Meaning
id SpeculaPro’s identifier for the person
userName The email address they sign in with. Required, unique in the workspace, stored trimmed and in lower case
externalId The IdP’s own identifier, kept as sent
displayName The name shown in SpeculaPro. Shortened to 120 characters; when it is missing or a single character, the email address is used
name.formatted, name.givenName, name.familyName Used for the name when displayName isn’t sent. Returned as name.formatted only
emails Read-only: the sign-in address, as one work, primary entry
active false when the account is disabled
groups Read-only: the groups the person is in
meta resourceType, created, lastModified, location

Anything else in a request (a password, phone numbers, the enterprise extension) is accepted and ignored.

{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
"userName": "[email protected]",
"externalId": "00u1abcd",
"name": { "givenName": "Ama", "familyName": "Owusu" },
"active": true
}

The account is created Invited, with no password. It gets the single sign-on default role when one is set and applies, unless that role is privileged and the workspace doesn’t let SCIM give privileged roles; roles from groups follow when groups are pushed. "active": false creates it disabled. An email already in the workspace answers 409 with scimType uniqueness.

Parameter Meaning
filter userName eq "…" or externalId eq "…". Nothing else is supported
startIndex 1-based. Default 1
count Default 100, at most 200. 0 returns only totalResults

Operations add, replace and remove (any letter case). Both common shapes are understood:

{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [{ "op": "replace", "value": { "active": false } }] }
{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [{ "op": "Replace", "path": "active", "value": "False" }] }
Path Notes
active true or false, also as the strings "True" and "False"
userName Must be an email address. Can’t be removed
displayName, name.formatted Set the name
name.givenName and name.familyName Set the name only when both are sent
externalId remove, or a null value, clears it
emails… Ignored: the sign-in address is userName
Anything else Ignored

A path may carry the schema URN in front.

Request Result
active: false, or DELETE The account is disabled and its sessions end in the same transaction. The API keys the person created are marked rotation due, or revoked when the workspace setting revoke_api_keys_on_deactivation is on
active: true on an account SCIM disabled Re-enabled: Invited if the person never signed in, otherwise Active
active: true on an account an administrator disabled 409: … was disabled in SpeculaPro, not over SCIM: an administrator re-enables the account there
Deactivating a break-glass administrator, or changing their userName 403, scimType mutability
Deactivating the last active administrator 409
Attribute Meaning
id SpeculaPro’s identifier for the group
displayName The name (1 to 512 characters), unique in the workspace. It is matched against the single sign-on Group → role table
externalId The IdP’s identifier
members Each {"value": "<user id>"}. Every value must be the id of a person in this workspace; an unknown one refuses the whole request

A group has no powers of its own. While Set roles from IdP groups is on and the group’s name is in the Group → role table, a change to its members, its name or its existence sets each affected person’s roles from all their groups. See Roles from groups.

Roles that hold administration, approval or security permissions are the exception. Unless the workspace allows it (see Privileged roles), a group change neither gives nor removes such a role. The request still answers 200 or 201: the roles left out are recorded in the audit log, not reported to the IdP.

filter takes displayName eq "…" or externalId eq "…". startIndex and count work as for people. excludedAttributes=members, on a list or a single group, leaves the member list out.

Operation Path Effect
add members Adds the members in value
replace members Replaces the whole member list
remove members with a value Removes those members
remove members with no value Removes every member
remove members[value eq "<user id>"] Removes that member
add, replace displayName Renames the group
add, replace, remove externalId Sets or clears it
add, replace none, with an object in value Sets displayName, externalId and members from the object

Resending a state the group already has changes nothing and answers 200. A group holds at most 10,000 members, and one request names at most 10,000.

{
"base_url": "https://speculapro.yourbank.com.gh/scim/v2",
"max_tokens": 10,
"tokens": [
{ "id": "…", "name": "Okta provisioning", "prefix": "scim_…",
"created_at": "…", "last_used_at": "…", "expires_at": null, "revoked_at": null }
],
"allow_privileged_roles": false,
"role_mapping_enabled": true,
"privileged_mappings": [
{ "group": "SpeculaPro Admins", "role": "admin", "role_name": "Administrator" }
]
}
Field Meaning
allow_privileged_roles Whether SCIM may give and take away privileged roles. false unless someone switched it on
role_mapping_enabled Whether Set roles from IdP groups is on. Off, groups change nobody’s roles
privileged_mappings The rows of the Group → role table that lead to a privileged role: group, role (the role’s key) and role_name. A privileged single sign-on default role appears with "default": true and no group. Empty when nothing maps to a privileged role

PUT /api/v1/settings/sso/scim/privileged-roles

Section titled “PUT /api/v1/settings/sso/scim/privileged-roles”
{ "allow_privileged_roles": true }

A role is privileged when it holds any administration permission, any approval permission, or a security permission (managing people, single sign-on, API keys, webhooks or workspace settings). That is worked out from the role’s permissions, so it covers custom roles.

false (the default) SCIM never gives a privileged role and never takes one away, whatever a pushed group maps to. A person’s other mapped roles still apply
true SCIM gives and removes privileged roles like any other
Switching on A single sign-on change: where the workspace requires approval for those, send X-Change-Reason and the answer is 202. The request goes stale if the privileged mappings change before it is approved
Switching off Applies at once, never through approval. Answers 204

Changing the setting changes nobody’s roles by itself; it applies to the next change the IdP pushes. Sending the value it already has answers 204 and records nothing. Otherwise the change is audited as scim.privileged_roles_update.

Body: name (1 to 120 characters) and, optionally, expires_at (in the future). Answers 201 with token and secret. The secret is returned once; only its SHA-256 is stored.

A workspace holds at most 10 working tokens. Creating one is a single sign-on change: where the workspace requires approval for those, send X-Change-Reason and the answer is 202 with the request. Once approved, the person who asked collects the secret once. See the Change requests API.

DELETE /api/v1/settings/sso/scim/tokens/{id}

Section titled “DELETE /api/v1/settings/sso/scim/tokens/{id}”

Answers 204. The next request with that token is refused.

SCIM endpoints answer in SCIM’s error format:

{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
"status": "409", "scimType": "uniqueness",
"detail": "someone with the userName [email protected] is already in the workspace" }
Status scimType Meaning
400 invalidSyntax, invalidValue, invalidFilter, invalidPath, noTarget, mutability, tooMany The request can’t be understood or names something that can’t be changed
401 The token is missing, wrong, expired or revoked, or its workspace isn’t served at this address. Every such refusal is the same, with WWW-Authenticate: Bearer realm="SCIM"
403 mutability A break-glass administrator can’t be deactivated or have their sign-in address changed
404 No such person or group in this workspace
405 The method isn’t supported on that path
409 uniqueness, or none A duplicate userName or group name; reactivating an account an administrator disabled; deactivating the last administrator
429 Too many requests. Retry-After: 30
503 On a multi-region service, the workspace is read-only while it is moved to another region. Retry-After: 120. Reads still work
500 Something failed inside SpeculaPro. The detail carries a reference, never the cause

The token endpoints use the ordinary API error format: 403 without the permission, 404 for an unknown or already revoked token, 422 for a bad name or expiry or when ten tokens already work.