SCIM API reference
SSOGEN speaks SCIM 2.0 in two directions. As a server, it lets an identity source such as Microsoft Entra ID or Okta create, update and deactivate people and groups in SSOGEN. As a client, it creates and updates accounts in an application that has a SCIM interface. This page is the reference for both: every endpoint, attribute, filter, operation and error answer.
To set either direction up in the console, follow Inbound provisioning with SCIM or Outbound provisioning. This page does not repeat those steps.
1. The SCIM server at a glance
Section titled “1. The SCIM server at a glance”| Item | Value |
|---|---|
| Base URL | Your SSOGEN address followed by /scim, for example https://sso.example.com/scim. There is no version segment in the path. |
| Authentication | Authorization: Bearer <provisioning token> on every request, the discovery endpoints included. |
| Request body type | application/scim+json or application/json |
| Answer type | application/scim+json, with Cache-Control: no-store |
| Resources | User and Group |
| PATCH | Supported, at most 200 operations in one request |
| Filter | Supported, eq only (section 5) |
| Bulk, sort, ETag preconditions, password change | Not supported |
Endpoints
Section titled “Endpoints”| Method and path | What it does |
|---|---|
GET /scim/ServiceProviderConfig |
What the server supports |
GET /scim/ResourceTypes |
The two resource types |
GET /scim/Schemas, GET /scim/Schemas/{id} |
The User and Group schemas |
GET /scim/Users |
List people, or find one with filter |
POST /scim/Users |
Create a person |
GET /scim/Users/{id} |
Read a person |
PUT /scim/Users/{id} |
Replace a person’s profile and status |
PATCH /scim/Users/{id} |
Change part of a person |
DELETE /scim/Users/{id} |
Remove a person (the account is archived, see section 4.5) |
GET /scim/Groups |
List groups, or find one with filter |
POST /scim/Groups |
Create a group |
GET /scim/Groups/{id} |
Read a group |
PUT /scim/Groups/{id} |
Replace a group’s members |
PATCH /scim/Groups/{id} |
Add or remove members |
DELETE /scim/Groups/{id} |
Delete a group |
2. Authentication
Section titled “2. Authentication”Send the provisioning token an administrator issued for this client as a bearer token. The token starts with ssogen_scim_. A browser session is never accepted on these endpoints.
GET /scim/Users?filter=userName%20eq%20%22jane.smith%40acme.example%22 HTTP/1.1Host: sso.example.comAuthorization: Bearer ssogen_scim_<your token>Accept: application/scim+jsonA request with no token, or with a token that is wrong or revoked, is answered:
HTTP/1.1 401 UnauthorizedWWW-Authenticate: Bearer realm="SSOGEN SCIM"Content-Type: application/scim+json
{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"], "detail": "A valid provisioning token is required.", "status": "401"}Issuing and revoking a token is recorded in the audit log as scim_client.issued and scim_client.revoked.
3. Discovery
Section titled “3. Discovery”GET /scim/ServiceProviderConfig answers with these capabilities. A client that reads them configures itself correctly.
| Capability | Answer |
|---|---|
patch |
supported: true |
bulk |
supported: false, maxOperations: 0, maxPayloadSize: 0 |
filter |
supported: true, maxResults: 200 |
changePassword |
supported: false |
sort |
supported: false |
etag |
supported: false |
authenticationSchemes |
One scheme: type oauthbearertoken, name OAuth Bearer Token, primary: true |
GET /scim/ResourceTypes lists User (endpoint /Users) and Group (endpoint /Groups). GET /scim/Schemas/{id} answers 404 with the detail No such schema. for any other identifier.
4. Users
Section titled “4. Users”4.1 The User resource
Section titled “4.1 The User resource”| Attribute | Notes |
|---|---|
schemas |
Always ["urn:ietf:params:scim:schemas:core:2.0:User"] |
id |
Issued by SSOGEN. Never changes. Use it in every later request. |
externalId |
Your identifier for the person. Present when one was set. Unique. |
userName |
Required. Unique. The name the person signs in with. |
name |
Always present. Holds givenName and familyName when set, and formatted, which carries the display name. |
displayName |
Present when set |
emails |
Present when the person has an address: one entry with value, "type": "work" and "primary": true. SSOGEN keeps one address per person. |
active |
true only when the account is active. A disabled or locked account answers false. |
groups |
Read-only. Present only when the person is a member of a group: value, display and $ref for each. |
meta |
resourceType, created, lastModified, location and version. location is a path relative to your SSOGEN address, for example /scim/Users/<id>. |
4.2 Create a person
Section titled “4.2 Create a person”POST /scim/Users HTTP/1.1Host: sso.example.comAuthorization: Bearer ssogen_scim_<your token>Content-Type: application/scim+json
{ "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], "externalId": "00u1abcd", "userName": "jane.smith@acme.example", "name": { "givenName": "Jane", "familyName": "Smith" }, "displayName": "Jane Smith", "emails": [{ "value": "jane.smith@acme.example", "type": "work", "primary": true }], "active": true}HTTP/1.1 201 CreatedLocation: /scim/Users/3f0c2c1e-5d2a-4c53-9f0e-2a7d6b1f4e11Content-Type: application/scim+json
{ "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], "id": "3f0c2c1e-5d2a-4c53-9f0e-2a7d6b1f4e11", "externalId": "00u1abcd", "userName": "jane.smith@acme.example", "name": { "givenName": "Jane", "familyName": "Smith", "formatted": "Jane Smith" }, "displayName": "Jane Smith", "emails": [{ "value": "jane.smith@acme.example", "type": "work", "primary": true }], "active": true, "meta": { "resourceType": "User", "created": "2026-11-09T10:15:30Z", "lastModified": "2026-11-09T10:15:30Z", "location": "/scim/Users/3f0c2c1e-5d2a-4c53-9f0e-2a7d6b1f4e11", "version": "W/\"1\"" }}Rules for a create:
userNameis required. Without it the answer is400,scimTypeinvalidValue, detailuserName is required.- When
activeis left out, the person is created active. - When several addresses are sent in
emails, SSOGEN stores the one markedprimary, or the first one when none is marked. - A request with no body is answered
400,scimTypeinvalidSyntax, detailNo body.
4.3 Replace a person (PUT)
Section titled “4.3 Replace a person (PUT)”PUT /scim/Users/{id} replaces the display name, given name, family name, primary email address and status. It does not change userName or externalId. When active is left out of a PUT, the person is made active, so always send it.
4.4 Change part of a person (PATCH)
Section titled “4.4 Change part of a person (PATCH)”The body is a SCIM PatchOp message. op is add, replace or remove, in any letter case. Attribute names are not case-sensitive, and a path may carry the full schema prefix.
PATCH /scim/Users/3f0c2c1e-5d2a-4c53-9f0e-2a7d6b1f4e11 HTTP/1.1Host: sso.example.comAuthorization: Bearer ssogen_scim_<your token>Content-Type: application/scim+json
{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], "Operations": [ { "op": "replace", "path": "active", "value": false } ]}The answer is 200 with the whole User resource, now with "active": false.
An operation with no path carries an object, which is merged:
{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], "Operations": [ { "op": "replace", "value": { "displayName": "Jane Baker", "name": { "familyName": "Baker" } } } ]}| Path SSOGEN acts on | Effect |
|---|---|
active |
Activates or deactivates the account. remove deactivates it. |
displayName |
Sets the display name. remove clears it. |
name.givenName, name.familyName |
Sets or clears the name part |
emails or emails.value |
Sets the address. The value may be a string, one object with value, or a list of such objects (the one marked primary wins, otherwise the first). |
Every other path is accepted and ignored, so a client that also sends attributes SSOGEN does not keep is not refused.
A PATCH is all or nothing: when one operation is refused, nothing is changed.
4.5 Remove a person (DELETE)
Section titled “4.5 Remove a person (DELETE)”DELETE /scim/Users/{id} answers 204 with no body. The account is archived, not erased:
- Every later request for that
idanswers404with the detailNo such user. - The person leaves every list, filter result,
totalResultscount and group member list. - The
userNameandexternalIdstay taken. A create that names either is answered409(section 7), and an administrator can restore the archived account in the SSOGEN console.
To suspend a person and keep the record visible to your client, send active: false instead of DELETE.
4.6 Accounts SSOGEN protects
Section titled “4.6 Accounts SSOGEN protects”| Situation | Answer |
|---|---|
| The account’s name and email address are owned by a connected directory | Profile values in a PUT are ignored and active is applied. A PATCH that changes them is answered 400, scimType mutability: This user's name and email are managed by the directory <name> and are read-only here. Send only active to change the account's status. |
| The account holds an SSOGEN administrator role and this client did not create it | 403: This account holds a SSOGEN administrator role and was not provisioned by this client. A Super Admin changes it in the SSOGEN console. |
| The account holds an administrator role and the request changes its user name or email | 403: This account holds a SSOGEN administrator role. A SCIM client may end it or rename it, but not change its user name or email; a Super Admin does that in the SSOGEN console. |
| Deactivating or removing the account would leave nobody able to sign in to the admin console | 409: Nobody else could sign in to the SSOGEN admin console under its sign-in policy, so this account was not deactivated. A Super Admin gives another account that can sign in to the console the Super Admin role first. Nothing was changed. |
These answers carry no scimType. They are final for that request: retrying does not help until an administrator acts. Each one is recorded in the audit log (section 8).
5. Listing, paging and filtering
Section titled “5. Listing, paging and filtering”GET /scim/Users?startIndex=1&count=100 HTTP/1.1Host: sso.example.comAuthorization: Bearer ssogen_scim_<your token>{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"], "totalResults": 2, "startIndex": 1, "itemsPerPage": 2, "Resources": [ { "...": "a User resource" }, { "...": "a User resource" } ]}| Parameter | Behaviour |
|---|---|
startIndex |
First result to return, counted from 1. Default 1. |
count |
Page size. Default 100. A value above 200 is treated as 200. A value of 0 or less returns totalResults and an empty Resources list. |
filter |
One comparison, at most 512 characters |
itemsPerPage is the number of resources in this answer.
Filters
Section titled “Filters”The only form accepted is <attribute> eq "<value>". The attribute name and eq are not case-sensitive, and the value must be in double quotes.
| Resource | Attributes you can filter on |
|---|---|
| Users | userName, externalId, emails.value, displayName |
| Groups | displayName, externalId |
A filtered read finds no resource or one. No match is a normal 200 answer with "totalResults": 0, which is how a client learns that it must create the person.
Any other filter is answered 400 with scimType invalidFilter, and the detail says what was wrong:
| Detail | Cause |
|---|---|
the filter is empty |
filter= with no value |
the filter is longer than 512 characters |
Filter too long |
expected <attribute> eq "<value>", got: … |
Not three parts |
only the eq operator is supported, not "co" |
Another operator (co, sw, and, …) |
the value must be in double quotes, got: … |
Unquoted value |
the value is empty |
eq "" |
filter on "department" is not supported; this directory filters on userName, externalId, emails.value and displayName |
Another attribute |
6. Groups
Section titled “6. Groups”6.1 The Group resource
Section titled “6.1 The Group resource”| Attribute | Notes |
|---|---|
schemas |
Always ["urn:ietf:params:scim:schemas:core:2.0:Group"] |
id |
Issued by SSOGEN |
externalId |
Present when set |
displayName |
Required. Unique. |
members |
Always present, empty when the group has nobody. Each member has value (the person’s SSOGEN id), display and $ref. |
meta |
As for a User, with resourceType Group |
6.2 Create a group
Section titled “6.2 Create a group”POST /scim/Groups HTTP/1.1Host: sso.example.comAuthorization: Bearer ssogen_scim_<your token>Content-Type: application/scim+json
{ "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], "displayName": "Finance", "externalId": "grp-finance", "members": [{ "value": "3f0c2c1e-5d2a-4c53-9f0e-2a7d6b1f4e11" }]}HTTP/1.1 201 CreatedContent-Type: application/scim+json
{ "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], "id": "8a4d1b7c-0e6f-4a39-b1c2-5d9e3f7a6b20", "externalId": "grp-finance", "displayName": "Finance", "members": [ { "value": "3f0c2c1e-5d2a-4c53-9f0e-2a7d6b1f4e11", "display": "Jane Smith", "$ref": "/scim/Users/3f0c2c1e-5d2a-4c53-9f0e-2a7d6b1f4e11" } ], "meta": { "resourceType": "Group", "created": "2026-11-09T10:20:00Z", "lastModified": "2026-11-09T10:20:00Z", "location": "/scim/Groups/8a4d1b7c-0e6f-4a39-b1c2-5d9e3f7a6b20", "version": "W/\"1\"" }}6.3 Change members
Section titled “6.3 Change members”PATCH /scim/Groups/{id} acts on members only.
| Operation | Effect |
|---|---|
{"op":"add","path":"members","value":[{"value":"<user id>"}]} |
Adds the listed people |
{"op":"replace","path":"members","value":[…]} |
Makes the list the whole membership |
{"op":"remove","path":"members[value eq \"<user id>\"]"} |
Removes one person |
{"op":"remove","path":"members"} with no value |
Removes everyone |
PUT /scim/Groups/{id} replaces the member list. It does not rename the group.
DELETE /scim/Groups/{id} answers 204 and deletes the group. This is recorded as directory.group.deleted.
6.4 Member rules
Section titled “6.4 Member rules”| Situation | Answer |
|---|---|
A member value that is not in the form of an SSOGEN id |
Left out without an error |
A member value in the right form that names no current person (for example an archived account) |
400, scimType invalidValue, detail user_missing. Nothing is changed. |
| The group’s membership is set by a rule in SSOGEN | 400, scimType mutability, detail group_membership_is_ruled |
| The change would add or remove an SSOGEN administrator this client did not create | 403: This change would add or remove a SSOGEN administrator this client did not provision. A Super Admin changes their groups in the SSOGEN console. |
displayName missing on create |
400, invalidValue, detail displayName is required |
| A group with that name exists | 409, uniqueness, detail a group with that displayName already exists |
7. Error answers
Section titled “7. Error answers”Every error uses the SCIM error message. status is a string, as the specification requires.
{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"], "scimType": "uniqueness", "detail": "a user with that userName already exists", "status": "409"}| Status | scimType |
When | What the client should do |
|---|---|---|---|
400 |
invalidSyntax |
No body (No body.), or a PATCH that cannot be read |
Fix the request. Do not retry unchanged. |
400 |
invalidFilter |
A filter SSOGEN does not support (section 5) | Use an eq filter on a supported attribute |
400 |
invalidValue |
A required value is missing or a member does not exist | Fix the value |
400 |
mutability |
The attribute is owned by a directory, or the group is rule-managed | Send only what the client may change |
401 |
none | Missing, wrong or revoked token | Issue a new token in the console |
403 |
none | A protected administrator account (sections 4.6 and 6.4) | An administrator acts in the console |
404 |
none | No such user., No such group., No such schema. |
Look the resource up again with a filter |
409 |
uniqueness |
The userName, externalId or group name is taken |
Find the existing resource and update it |
409 |
none | The change would lock everyone out of the admin console | An administrator acts in the console |
503 |
none | Try again later. |
Retry after a pause |
The two 409 uniqueness details for a user name are a user with that userName already exists and a user with that userName was archived and the name is not reused; an administrator can restore the account in the SSOGEN console. The same two exist for externalId.
PATCH details, all with 400 and invalidSyntax:
| Detail | Cause |
|---|---|
the request has no body |
Empty request |
a PATCH must carry a non-empty Operations array |
Operations missing or empty |
more than 200 operations in one request |
Split the request |
an operation is not an object, an operation has no op, unknown op: … |
Malformed operation |
a remove operation must name a path |
remove with no path |
an operation with no path must carry an object to merge |
add or replace with no path and a value that is not an object |
unbalanced brackets in path: … |
A path with [ that does not end in ] |
only a [value eq "..."] filter is supported in a path, not: … |
A path filter on something other than value |
only the eq operator is supported in a path filter, not: … |
Another operator inside the brackets |
the value in a path filter must be quoted: …, the value in a path filter is empty: … |
Malformed filter value |
expected a boolean, got nothing, expected a boolean, got: … |
active with a value that is not true or false |
8. What the audit log records
Section titled “8. What the audit log records”| Event | Recorded when |
|---|---|
scim_client.issued, scim_client.revoked |
An administrator issues or revokes a provisioning token |
directory.user.provisioned |
A SCIM client creates or updates a person |
directory.user.deprovisioned |
A SCIM client deactivates or removes a person. The result is denied when the request was refused because it would have locked everyone out of the admin console. |
admin.target_action_refused |
A request on a protected administrator account was refused |
directory.group.deleted |
A group was deleted |
See Audit log for searching and exporting, and the Audit event reference for the fields.
9. Outbound: what SSOGEN sends to an application
Section titled “9. Outbound: what SSOGEN sends to an application”When SSOGEN provisions into an application, it is the SCIM client. Use this section to check that the application’s SCIM interface will work, or to read its access log during a first run.
9.1 Every request
Section titled “9.1 Every request”| Item | Value |
|---|---|
| Authentication | Authorization: Bearer <the token you saved for the application> |
Accept |
application/scim+json, application/json |
Content-Type |
application/scim+json, on requests with a body |
| Time allowed | 15 seconds for each request |
| Answer size | Up to 1,000,000 bytes. A larger answer is refused. |
| Redirects | Never followed. The base URL must answer directly. |
9.2 The requests
Section titled “9.2 The requests”| Step | Request |
|---|---|
| Find a person | GET <base>/Users?filter=userName eq "<value>" |
| Create a person | POST <base>/Users |
| Update a person | PATCH <base>/Users/<id> with a PatchOp message |
| Deactivate a person | PATCH <base>/Users/<id> with {"op":"replace","path":"active","value":false} |
| Find a group | GET <base>/Groups?filter=displayName eq "<name>" |
| Read a group | GET <base>/Groups/<id> |
| Create a group | POST <base>/Groups with displayName and members (each {"value":"<id>"}) |
| Update a group | PATCH <base>/Groups/<id>: replace on displayName, add on members, and one PATCH for each removed member with {"op":"remove","path":"members[value eq \"<id>\"]"} |
SSOGEN never sends DELETE or PUT to an application. A person who loses access is deactivated, and the account stays in the application.
The application must therefore support: an eq filter on userName and on displayName, PATCH with add, replace and remove, and the members[value eq "<id>"] path.
9.3 A create, as sent
Section titled “9.3 A create, as sent”With the default mapping, SSOGEN sends:
{ "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], "userName": "jane.smith@acme.example", "name": { "givenName": "Jane", "familyName": "Smith" }, "emails": [{ "value": "jane.smith@acme.example", "type": "work" }], "active": true}The application must answer with the resource and its id. SSOGEN keeps that id and uses it for every later PATCH.
9.4 Attribute mapping
Section titled “9.4 Attribute mapping”The default mapping sends userName from the SSOGEN user name, name.givenName, name.familyName, and emails[type eq "work"].value from the email address. You can change it per application, with at most 32 entries. A fixed value is at most 256 characters.
| Group | Target attributes you can map to |
|---|---|
| Core | userName, displayName, nickName, title, userType, preferredLanguage, locale, timezone, profileUrl, externalId |
| Name | The parts of name, for example name.givenName and name.familyName |
emails[type eq "work"].value, and the same with home or other |
|
| Phone | phoneNumbers[type eq "work"].value, and the same with mobile, home or other |
Enterprise extension (urn:ietf:params:scim:schemas:extension:enterprise:2.0:User) |
employeeNumber, costCenter, organization, division, department |
9.5 How SSOGEN reads the application’s answers
Section titled “9.5 How SSOGEN reads the application’s answers”| The application answers | SSOGEN does |
|---|---|
A search with no match, or 404 to a search |
Treats the person or group as absent and creates it |
| A search with more than one match | Stops for that person: the application holds more than one match |
A resource with no id |
Stops for that person: the application returned a resource with no id |
429, 502, 503, 504, or no answer, to a GET or PATCH |
Tries once more after 1 second |
| Any failure to a POST | Does not retry, so a person is never created twice |
401 or 403 |
Stops the whole run. Check the token saved for the application. |
404 for an account SSOGEN had linked |
Forgets the link and looks the person up again by user name |
400 with scimType noTarget when removing a member |
Counts the member as removed |
| Any other error status | Reports the application answered <status> (<scimType>) |
Other messages you can meet on a run: the application could not be reached, the application returned an oversized response and the application returned a response that is not SCIM. The outcome of each run is shown on the application’s provisioning tab, see Outbound provisioning.