User groups
A user group scopes a rule to a set of users. Groups come in two flavors: local groups you create and manage directly through this API, and sso groups synced from your identity provider (IdP). A rule references a group via its scope, and you control who the rule applies to by editing the group's membership. Use a local group when you want a rule's audience driven by your own logic (a script, a CMDB, a rollout schedule).
On this page: membership model, limits, examples, list members, endpoint reference, tips.
Membership model
The provider field tells you where a group comes from:
provider | Origin | Can you edit membership via the API? |
|---|---|---|
local | Created through this API (or the console) | Yes, you own it |
sso | Synced from your IdP using the Cloud Identity Engine | No, membership is governed upstream |
Use local groups for automation. When you want a rule's audience to be driven by your own logic (a script, a CMDB, a rollout schedule), create a local user group and manage its members through the API. SSO groups mirror the IdP; change them there, not here.
Limits
| What | Limit |
|---|---|
| Users in one local user group | 25,000 |
User groups one rule can scope (scope.users.userGroups) | 1000 |
limit on GET /user-groups and GET /user-groups/{id}/members | Default 100; values above 1000 are capped at 1000 |
Examples
Retrieve
List and filter
GET /seb-api/v1/user-groups
curl -sS -G "$PB_API_BASE/user-groups" \
-H "Authorization: Bearer $PB_TOKEN" \
--data-urlencode "limit=50"
Response (200):
{
"metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } },
"pageInfo": { "hasNextPage": false, "cursor": "", "totalCount": 1 },
"data": [
{
"id": "0UG01PILOTXXXXXXXXXXXXXXXXXXX",
"name": "Pilot - DevTools block",
"lastUpdated": "2026-01-15T10:00:00Z",
"createdAt": "2026-01-15T10:00:00Z",
"provider": "local"
}
// ... more groups
]
}
Results are cursor-paginated: the response carries a pageInfo object. Pass the next cursor value to page through large result sets.
| Query param | Notes |
|---|---|
limit | Number of groups to return. Default 100; values above 1000 are capped at 1000. |
cursor | Opaque string marking where to resume listing. |
Get one
GET /seb-api/v1/user-groups/{id}
curl -sS "$PB_API_BASE/user-groups/$UG_ID" \
-H "Authorization: Bearer $PB_TOKEN"
Response (200):
{
"id": "0UG01PILOTXXXXXXXXXXXXXXXXXXX",
"name": "Pilot - DevTools block",
"lastUpdated": "2026-01-15T10:00:00Z",
"createdAt": "2026-01-15T10:00:00Z",
"provider": "local",
"metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } }
}
Create
POST /seb-api/v1/user-groups
curl -sS -X POST "$PB_API_BASE/user-groups" \
-H "Authorization: Bearer $PB_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Pilot - DevTools block",
"userIds": [
"0UR01ALICEXXXXXXXXXXXXXXXXXXX",
"0UR01BOBXXXXXXXXXXXXXXXXXXXXX"
]
}'
Response (201). The userGroupId field is a deprecated alias for id:
{
"id": "0UG01PILOTXXXXXXXXXXXXXXXXXXX",
"userGroupId": "0UG01PILOTXXXXXXXXXXXXXXXXXXX"
}
Capture the ID for later snippets:
export UG_ID='0UG01PILOTXXXXXXXXXXXXXXXXXXX'
Key fields
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | Yes | Display name. |
userIds | array | Yes | Member user IDs (0UR...). |
Update
Membership edits use PUT with a list of per-user actions. This is incremental: you send only the users to add or remove, not the full roster.
PUT behaves like a PATCH here. It accepts only incremental per-user membership actions, not a complete replacement of the full roster. You send a list of users to add or remove, and only those changes are applied.
PUT /seb-api/v1/user-groups/{id}
# Add one user, remove another
curl -sS -X PUT "$PB_API_BASE/user-groups/$UG_ID" \
-H "Authorization: Bearer $PB_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"users": [
{ "userId": "0UR01CAROLXXXXXXXXXXXXXXXXXXX", "action": "add" },
{ "userId": "0UR01BOBXXXXXXXXXXXXXXXXXXXXX", "action": "remove" }
]
}'
Response (200):
{
"id": "0UG01PILOTXXXXXXXXXXXXXXXXXXX",
"userGroupId": "0UG01PILOTXXXXXXXXXXXXXXXXXXX"
}
Changes to a user group, including membership edits, land in the draft and go live only when you publish. Your automation must publish after every write.
Delete
DELETE /seb-api/v1/user-groups/{id}
curl -sS -X DELETE "$PB_API_BASE/user-groups/$UG_ID" \
-H "Authorization: Bearer $PB_TOKEN"
Response (200):
{
"id": "0UG01PILOTXXXXXXXXXXXXXXXXXXX",
"userGroupId": "0UG01PILOTXXXXXXXXXXXXXXXXXXX"
}
Publish with partial publish
The typical gradual rollout loop is: edit group membership, then partial publish only that group so unrelated draft edits are left untouched. Partial publish is available through the API only. It is a preview feature: send x-prisma-browser-preview: partial-publish. The id to publish comes back in the response of the write itself, so no extra lookup is needed.
curl -sS -X POST "$PB_API_BASE/configuration-management/draft/partial-publish" \
-H "Authorization: Bearer $PB_TOKEN" \
-H "x-prisma-browser-preview: partial-publish" \
-H "Content-Type: application/json" \
-d '{ "entityIds": ["'"$UG_ID"'"], "description": "ServiceNow INC0042311: contractor offboarding" }'
Returns 201 with the new configuration version and the IDs that went live. Creating and deleting groups follow the same pattern: perform the write, then publish the id it returned.
If the group has no pending change, the call returns 404. This happens when the write changed nothing (for example, it added a user who was already a member), in which case there is nothing to publish:
{
"error": {
"code": "NOT_FOUND",
"message": "No pending change found for one or more of the specified entities.",
"timestamp": "2026-08-06T09:15:00Z",
"details": {
"errors": [
{
"index": 0,
"field": "entityIds",
"error": "no pending change in the current draft",
"value": "0UG01PILOTXXXXXXXXXXXXXXXXXXX"
}
]
}
}
}
See Publish a single object (partial publish) for the full error matrix.
List the members of a group
GET /seb-api/v1/user-groups/{id}/members
Preview. Returns the members of a user group is a preview feature. Send x-prisma-browser-preview: user-group-members on every call; without it the request returns 400.
curl -sS -G "$PB_API_BASE/user-groups/$UG_ID/members" \
-H "Authorization: Bearer $PB_TOKEN" \
-H "x-prisma-browser-preview: user-group-members" \
--data-urlencode "limit=2"
Response (200):
{
"data": [
{ "id": "0UR01ALICEXXXXXXXXXXXXXXXXXXX", "name": "Alice Example", "email": "alice@example.com" },
{ "id": "0UR01BOBXXXXXXXXXXXXXXXXXXXXX", "name": "Bob Example", "email": "bob@example.com" }
],
"pageInfo": { "hasNextPage": true, "cursor": "eyJ2ZXJzaW9uIjoxfQ", "totalCount": 45 },
"metadata": { "configurationVersion": { "id": "0CV01EXAMPLEXXXXXXXXXXXXXXXXX", "status": "draft", "number": 0 } }
}
| Query param | Notes |
|---|---|
configurationVersion | draft (default) or active. Applies to local groups. SSO group membership comes from directory sync and is always read live. Send the same value on every page. |
limit | Default 100; values above 1000 are capped at 1000. |
cursor | pageInfo.cursor from the previous page. |
includeTotalCount | pageInfo.totalCount is filled on the first page by default; set true or false to override. |
Each member is a user reference with id, name, and email. API service accounts are not listed.
How it differs from GET /users?groupId=:
GET /user-groups/{id}/members | GET /users?groupId= | |
|---|---|---|
| Membership read | Draft by default, or active with configurationVersion=active | Always active |
| Returns | id, name, email per member | Full user objects |
Use the members endpoint to check a membership edit before you publish it. metadata is omitted when the tenant has no configuration versioning; on such a tenant, do not send configurationVersion, or the request returns 400.
The path id must be a user group ID such as 0UG01PILOTXXXXXXXXXXXXXXXXXXX. A malformed ID returns 400; an unknown group returns 404.
Read back what is live
Reads default to the draft, so a group you have edited but not published still comes back from a plain GET. To verify what is actually enforced, ask for the active version:
curl -sS "$PB_API_BASE/user-groups/$UG_ID?configurationVersion=active" \
-H "Authorization: Bearer $PB_TOKEN"
Endpoint reference
| Method | Path | Purpose |
|---|---|---|
GET | /user-groups | List user groups (paginate) |
GET | /user-groups/{id} | Read one user group |
GET | /user-groups/{id}/members | List the members of a user group (preview: user-group-members) |
POST | /user-groups | Create a user group |
PUT | /user-groups/{id} | Add or remove members (incremental) |
DELETE | /user-groups/{id} | Delete a user group |
All paths are under the /seb-api/v1 base.
Tips and gotchas
One call changes who a rule covers. Editing a group's membership changes the audience of every rule that references it, without touching the rules themselves. Publish just that group with partial publish (API only today, preview) so the membership change does not ship unrelated draft edits. See Publish a single object (partial publish).
userGroupId is deprecated. Responses still return a userGroupId field for backward compatibility; use id.
Reference from a rule's scope. A rule scopes to a group via scope.users.userGroups. Add or remove groups on a rule with addUserGroups / removeUserGroups (see Rules and Manage users on a rule).
Related
- Building blocks: Rules
- Concepts: Partial publish
- Use cases: Add or remove users on a rule, Roll out a rule gradually, Change a rule's scope
