Groups
Groups add structure inside an organization. They form a tree, and membership is recursive: a member of a group is effectively a member of every group above it, and inherits the roles mapped to each of them.
That is what makes “everyone in Engineering can read the build logs, and the Platform sub-team can also deploy” expressible without assigning roles user by user.
The group tree
A group belongs to one organization and points at a parent through parent_group_id. A null or missing parent means the group sits at the top level.
Acme Corp (organization)
└── Engineering roles: build:read
├── Platform roles: deploy
└── Frontend
A user added to Platform is a member of Platform and of Engineering, and holds both deploy and build:read. A user added to Engineering holds only build:read.
Names are unique among siblings: two groups under the same parent, or two top-level groups, cannot share a name. Deleting a group deletes its sub-groups, with their memberships, role mappings and attributes.
Group fields
| Field | Type | Description |
|---|---|---|
id | UUID v7 | Identifier |
organization_id | UUID | The organization the group belongs to |
parent_group_id | UUID or null | Parent group. null for a top-level group |
name | String | Group name, required, at most 255 characters |
description | String | Free-text description |
created_at | DateTime (UTC) | Creation timestamp |
updated_at | DateTime (UTC) | Last-modified timestamp |
Updating a group can move it: sending a parent_group_id reparents it under that group. The new parent must belong to the same organization, and cannot be the group itself or one of its descendants (400). Leaving parent_group_id out, or sending null, keeps the current parent. The API has no way to move a group back to the top level.
Endpoints
All of them are scoped to a realm and an organization: the paths start with /realms/{realm_name}/organizations/{organization_id}.
Groups
| Method | Endpoint | Description |
|---|---|---|
GET | /realms/{realm_name}/organizations/{organization_id}/groups | List the organization’s groups |
POST | /realms/{realm_name}/organizations/{organization_id}/groups | Create a group. Answers 201 |
GET | /realms/{realm_name}/organizations/{organization_id}/groups/{group_id} | Read one group |
PUT | /realms/{realm_name}/organizations/{organization_id}/groups/{group_id} | Update or move a group |
DELETE | /realms/{realm_name}/organizations/{organization_id}/groups/{group_id} | Delete a group and its sub-groups. Answers 204 |
The list is a flat, paginated page, not a tree. The contract is described in API listings. Each item carries the group’s own fields and a child_count, the number of direct sub-groups:
{
"data": [
{
"id": "0199a4f2-3c1e-7b8a-9f00-1a2b3c4d5e6f",
"organization_id": "01936b2e-1234-7000-abcd-000000000001",
"parent_group_id": "0199a4f2-0000-7b8a-9f00-1a2b3c4d5e00",
"name": "Platform",
"description": "Runs the deploy pipeline",
"created_at": "2026-07-10T09:00:00Z",
"updated_at": "2026-07-10T09:00:00Z",
"child_count": 0
}
],
"metadata": {
"page": 1,
"limit": 20,
"total": 1,
"total_pages": 1,
"first_page": 1,
"last_page": 1,
"next_page": null,
"prev_page": null
}
}
To walk the tree, ask for the top level with is_root=true, then for the children of each group with parent_group_id. The other filters are:
| Parameter | Matches |
|---|---|
search | Case-insensitive substring of the name or the description |
name, description | Case-insensitive substring of that field |
parent_group_id | The direct children of that group |
is_root | true keeps top-level groups, false keeps nested ones. is_root=true cannot be combined with parent_group_id |
ids | A comma-separated list of at most 100 group ids |
created_from, created_to | RFC 3339 date-times, inclusive and exclusive |
order_by accepts name, created_at (the default) and updated_at.
Members
| Method | Endpoint | Description |
|---|---|---|
GET | /realms/{realm_name}/organizations/{organization_id}/groups/{group_id}/members | List the group’s members, paginated |
POST | /realms/{realm_name}/organizations/{organization_id}/groups/{group_id}/members | Add a user to the group |
DELETE | /realms/{realm_name}/organizations/{organization_id}/groups/{group_id}/members/{user_id} | Remove a user from the group |
Adding a member takes { "user_id": "…" }, answers 201 with the membership, and refuses a user from another realm or a user already in the group. Removing a user who is not in the group answers 404.
The member list is enriched, so a client can render it without walking the user directory. Each entry carries id, group_id, user_id, username, email, firstname, lastname, enabled and created_at, the date the user joined. It filters on search (username or email), username, email, enabled, created_from and created_to, and sorts on username, email or created_at (the default).
Roles
| Method | Endpoint | Description |
|---|---|---|
GET | /realms/{realm_name}/organizations/{organization_id}/groups/{group_id}/roles | List the roles mapped to the group |
POST | /realms/{realm_name}/organizations/{organization_id}/groups/{group_id}/roles | Map a role to the group |
DELETE | /realms/{realm_name}/organizations/{organization_id}/groups/{group_id}/roles/{role_id} | Unmap a role |
Mapping takes { "role_id": "…" } and answers 204. The role must belong to the same realm. The list is not paginated: it returns the full role objects in a plain array. Every member of the group, and of every group below it, inherits the roles mapped here.
Attributes
| Method | Endpoint | Description |
|---|---|---|
GET | /realms/{realm_name}/organizations/{organization_id}/groups/{group_id}/attributes | List the group’s attributes |
PUT | /realms/{realm_name}/organizations/{organization_id}/groups/{group_id}/attributes/{key} | Upsert an attribute |
DELETE | /realms/{realm_name}/organizations/{organization_id}/groups/{group_id}/attributes/{key} | Delete an attribute. Answers 204 |
Same key-value shape and same upsert behavior as organization attributes, except that the list is a plain array of attributes rather than an object with a data key.
Permissions
Groups use the permissions of the organization module. Reading groups, their members, roles and attributes needs view_organizations, manage_organizations or manage_realm. Creating, updating and deleting groups, and changing their members, roles and attributes, needs manage_organizations or manage_realm. manage_users and view_users are not enough. There are no group-specific permissions.