SCIM Groups API
Create, retrieve, list, update, and delete Groups in SpeakUp, and manage their membership by adding or removing Users. Groups let you assign permissions to many Users at once.
Deleting a Group removes the Group only. The Users who were members are unaffected.
{API_BASE_URL}/scim/v2/Groups
On this page: Authentication · Group attributes · Create a Group · Retrieve a Group · List Groups · Add members · Remove members · Update with PUT · Delete a Group · Status codes · Attribute mapping
Authentication
All requests need a bearer token. See what is SCIM provisioning and when should I use it? for how the connection is set up.
Authorization: Bearer <ACCESS_TOKEN>
Content-Type: application/scim+json
Accept: application/scim+json
Managing Groups requires Full access for Users and Groups on the App Integration. Read only access can retrieve Groups but cannot create, update, or delete them.
Group attributes
| Attribute | Type | Description |
|---|---|---|
id |
String | Unique identifier. System-generated and returned by the API. |
displayName |
String | The name of the Group. Required and must be unique. |
externalId |
String | External identifier, for example from your identity provider. Unique if provided. Can be set on create or update. |
members |
Array | The Users belonging to this Group. |
members[].value |
String | User ID of a member. This is the only field you send when adding members. |
members[].display |
String | The User's email. Returned in responses. |
members[].$ref |
String | Full URL to the User resource. Returned in responses. |
meta.resourceType |
String | Always "Group". |
meta.created |
String | ISO 8601 timestamp of creation. |
meta.lastModified |
String | ISO 8601 timestamp of last modification. |
meta.location |
String | Full URL to this Group resource. |
Create a Group
POST /scim/v2/Groups
displayName is required and must be unique. externalId and initial members are optional.
curl -X POST '{API_BASE_URL}/scim/v2/Groups' \
-H 'Content-Type: application/scim+json' \
-H 'Accept: application/scim+json' \
-H 'Authorization: Bearer <ACCESS_TOKEN>' \
--data-raw '{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"displayName": "Engineering Team",
"externalId": "705ad5b5-cb6b-4eba-a85d-10a64a7354dc",
"members": [
{ "value": "82" },
{ "value": "509" }
]
}'
Response, 201 Created
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"id": "1",
"displayName": "Engineering Team",
"externalId": "705ad5b5-cb6b-4eba-a85d-10a64a7354dc",
"members": [
{
"value": "82",
"display": "user1@speakup.com",
"$ref": "{API_BASE_URL}/scim/v2/Users/82"
},
{
"value": "509",
"display": "user2@example.com",
"$ref": "{API_BASE_URL}/scim/v2/Users/509"
}
],
"meta": {
"resourceType": "Group",
"created": "2026-02-23T10:30:00Z",
"lastModified": "2026-02-23T10:30:00Z",
"location": "{API_BASE_URL}/scim/v2/Groups/1"
}
}
You send only value for each member. The response expands each one with display and $ref.
Errors
| Code | scimType | Cause |
|---|---|---|
| 400 | invalidValue |
displayName is missing. |
| 400 | invalidValue |
One or more member Users were not found or are not eligible. |
| 409 | uniqueness |
A Group with that displayName already exists. |
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
"scimType": "uniqueness",
"detail": "A group with displayName 'Engineering Team' already exists",
"status": "409"
}
Retrieve a Group
GET /scim/v2/Groups/{id}
| Query parameter | Type | Description |
|---|---|---|
excludedAttributes |
String | Comma-separated attributes to omit, for example members or members,meta. |
curl -X GET '{API_BASE_URL}/scim/v2/Groups/1' \
-H 'Accept: application/scim+json' \
-H 'Authorization: Bearer <ACCESS_TOKEN>'
The Group is returned inside a ListResponse wrapper with a single entry in Resources, rather than as a bare Group object.
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults": 1,
"startIndex": 1,
"itemsPerPage": 1,
"Resources": [
{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"id": "1",
"displayName": "Engineering Team",
"externalId": "705ad5b5-cb6b-4eba-a85d-10a64a7354dc",
"members": [
{
"value": "82",
"display": "user1@speakup.com",
"$ref": "{API_BASE_URL}/scim/v2/Users/82"
}
],
"meta": {
"resourceType": "Group",
"created": "2026-02-23T10:30:00Z",
"lastModified": "2026-02-23T10:30:00Z",
"location": "{API_BASE_URL}/scim/v2/Groups/1"
}
}
]
}
Excluding members is useful for large Groups where you only need the name and metadata.
A Group ID that does not exist returns 404:
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
"detail": "Group not found",
"status": "404"
}
List Groups
GET /scim/v2/Groups
| Query parameter | Type | Default | Description |
|---|---|---|---|
startIndex |
Integer | 1 |
Starting position, 1-based. |
count |
Integer | 50 |
Results per page. |
filter |
String | Filter expression, URL-encoded. | |
excludedAttributes |
String | Comma-separated attributes to omit. |
Filter expressions
| Filter | Meaning |
|---|---|
displayName eq "Engineering Team" |
Exact match on display name |
displayName sw "Eng" |
displayName starts with the value |
displayName co "Team" |
displayName contains the value |
members.value eq "82" |
Groups containing the User with that ID |
members.display eq "user@example.com" |
Groups containing a member with that email |
The last two are how you find every Group a given User belongs to, without fetching the User first.
curl -X GET '{API_BASE_URL}/scim/v2/Groups?filter=displayName%20eq%20%22Engineering%20Team%22' \
-H 'Accept: application/scim+json' \
-H 'Authorization: Bearer <ACCESS_TOKEN>'
Add members to a Group
PATCH /scim/v2/Groups/{id}
Adds one or more Users without affecting existing members.
curl -X PATCH '{API_BASE_URL}/scim/v2/Groups/1' \
-H 'Content-Type: application/scim+json' \
-H 'Accept: application/scim+json' \
-H 'Authorization: Bearer <ACCESS_TOKEN>' \
--data-raw '{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "add",
"path": "members",
"value": [
{ "value": "3" },
{ "value": "4" },
{ "value": "5" }
]
}
]
}'
Returns the full Group with updated members and meta.lastModified.
| Code | Cause |
|---|---|
| 400 | A User does not exist or is not eligible. |
| 404 | The Group does not exist. |
Remove members from a Group
PATCH /scim/v2/Groups/{id}
Same shape as adding, with op set to remove.
curl -X PATCH '{API_BASE_URL}/scim/v2/Groups/1' \
-H 'Content-Type: application/scim+json' \
-H 'Accept: application/scim+json' \
-H 'Authorization: Bearer <ACCESS_TOKEN>' \
--data-raw '{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
"Operations": [
{
"op": "remove",
"path": "members",
"value": [
{ "value": "3" },
{ "value": "4" }
]
}
]
}'
Removing a member removes them from the Group only. The User account is unaffected.
Update with PUT
PUT /scim/v2/Groups/{id}
Full replacement. Send all writable attributes, because omitted attributes are effectively cleared.
Members not included in the payload are removed from the Group. To change membership without sending the full list, use PATCH.
curl -X PUT '{API_BASE_URL}/scim/v2/Groups/1' \
-H 'Content-Type: application/scim+json' \
-H 'Accept: application/scim+json' \
-H 'Authorization: Bearer <ACCESS_TOKEN>' \
--data-raw '{
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
"id": "1",
"displayName": "Backend Engineering Team",
"externalId": "705ad5b5-cb6b-4eba-a85d-10a64a7354dc",
"members": [
{ "value": "82" },
{ "value": "509" }
]
}'
Returns the full Group, with each member expanded to include value, display, and $ref.
Renaming a Group to a displayName already in use returns 409:
{
"schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
"scimType": "uniqueness",
"detail": "A group with displayName 'Backend Engineering Team' already exists",
"status": "409"
}
Delete a Group
DELETE /scim/v2/Groups/{id}
curl -X DELETE '{API_BASE_URL}/scim/v2/Groups/1' \
-H 'Accept: application/scim+json' \
-H 'Authorization: Bearer <ACCESS_TOKEN>'
Returns 204 No Content with no response body.
Deleting a Group removes the Group but does not delete the Users who were members. This differs from Users, which cannot be deleted through SCIM at all.
Status codes
| Code | Meaning | When it occurs |
|---|---|---|
| 200 | OK | Successful GET, PATCH, or PUT |
| 201 | Created | Successful POST |
| 204 | No Content | Successful DELETE |
| 400 | Bad Request | Missing required fields, invalid data, or Users not found |
| 401 | Unauthorized | Invalid or missing access token |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Group or User does not exist |
| 409 | Conflict | Duplicate displayName or constraint violation |
| 500 | Internal Server Error | Server-side error |
Attribute mapping
| SCIM attribute | SpeakUp property | Type | Notes |
|---|---|---|---|
id |
id |
String | Identifier for the resource as defined by the service provider. Returned in responses, read only. |
displayName |
name |
String | Human-readable name for the Group. Must be unique. |
externalId |
externalId |
String | External identifier, for example from your identity provider. Unique if set. Optional on create and update. |
members |
users |
Complex | Members of the Group. Each has value, and in responses display and $ref. |
meta.created |
createdAt |
String | When the Group was created. |
meta.lastModified |
updatedAt |
String | When the Group was last modified. |