Groups
A group is a team, and it is the only way access is granted. It exists to hand a set of people the same reach and the same view of the data, without doing it one account at a time — and since there is no way to do it one account at a time, it is also the first thing you make.
A group belongs to an organization, not to a workspace. One group can reach as many of your workspaces as you like, with a different set of roles in each.
What a Group Does
A group carries two things, either of which can be empty:
- Grants. One per workspace the group should reach, each naming the roles the group holds there. The roles belong to the grant, so the same group can be Admin in one workspace and Member in another — and can hold several roles in one workspace, where what it grants is the union of their permissions.
- Access policies. Groups are the only place an access policy can be assigned, and they are what narrows which rows its members see.
Permissions are additive. Your effective permissions in a workspace are the union of the roles every group you belong to holds on that workspace. A group never takes a permission away; use access policies to restrict what its members can see.
A group carries no permissions of its own. If a team needs an unusual combination, write it down as a custom role and grant that — the combination then has a name, appears in the roles list, and can be granted to a second group later without being reconstructed from memory.
The Two System Groups
Every organization is created with two groups you cannot rename or delete:
| Group | What it grants |
|---|---|
| Organization admins | The Owner role on every workspace in the organization, now and in future |
| Organization viewers | The Member role on every workspace in the organization |
They are ordinary groups with one unusual grant: instead of naming a workspace, it names the whole organization. That is why a workspace created tomorrow is already reachable by your administrators with nothing to configure.
Membership of Organization admins is organization administration — it is what lets somebody create groups, invite people and manage grants. The API refuses a removal that would leave that group empty.
Creating a Group
Via the UI
Organization settings → Groups → New group. Enter a Name and, optionally, a Description. A group starts with just a name and a description; you attach its workspaces, roles, access policies and members from the group’s own page afterwards.
Group names are unique within an organization, and the comparison ignores case — “Analysts” and “analysts” collide.
Via the API
curl -X POST "$API_BASE_URL/api/v1/organizations/$ORG_ID/groups" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "EMEA Marketing",
"description": "Marketing team working the EMEA region"
}'The response is the group:
{
"id": "7c1f4e2a-9b3d-4f18-a0c6-2d5e8b1f9034",
"organization_id": "3f8a1c07-5b2e-4d69-9f31-6c4a7e0b2d85",
"name": "EMEA Marketing",
"description": "Marketing team working the EMEA region",
"system_key": "",
"is_system": false,
"member_count": 0,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-01-15T10:00:00Z"
}system_key is "org_admins" or "org_viewers" on the two seeded groups and empty on every other; is_system is the same fact as a boolean. Creating, editing and deleting a group requires organization administration.
Granting a Workspace
A grant is one call, and it states the complete set of roles the group holds on that workspace. Sending it again with a different set replaces the old one, so the same call both grants and revokes.
curl -X PUT "$API_BASE_URL/api/v1/organizations/$ORG_ID/groups/$GROUP_ID/workspaces/$WORKSPACE_ID" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"role_ids": ["9a2b6c14-7d83-4e50-b1f7-3c8e5a09d642"]}'Repeat the call per workspace to give one group access to several; there is no limit and each workspace keeps its own roles.
Revoking is the same address:
curl -X DELETE "$API_BASE_URL/api/v1/organizations/$ORG_ID/groups/$GROUP_ID/workspaces/$WORKSPACE_ID" \
-H "Authorization: Bearer $API_TOKEN"Both take effect on the group’s members’ next request. Revoking also removes that group’s access-policy assignments in that workspace, since there is no longer anything for them to filter.
Several roles on one workspace
Name more than one and the group’s members get everything all of them allow — the union of their permissions, never the narrower of the two:
curl -X PUT "$API_BASE_URL/api/v1/organizations/$ORG_ID/groups/$GROUP_ID/workspaces/$WORKSPACE_ID" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"role_ids": ["9a2b6c14-7d83-4e50-b1f7-3c8e5a09d642", "4f18c2e7-5a90-4b31-9d6e-27ab803f1c55"]}'Reach for it when a team needs a standing role plus one extra capability in a particular workspace, and inventing a combined custom role would mean maintaining two roles that must be kept in step. Reach for the custom role instead when the combination has a name people would recognise, recurs across workspaces, or is something you want to audit as one thing — a list of roles on a grant describes what somebody can do, but only a named role says why.
Because nothing subtracts, a second role can only ever widen access. To restrict what a group’s members see, use access policies, not a narrower role alongside a wider one.
Attached, granting nothing
An empty role_ids is legal and means something specific: the group is attached to the workspace and granted no permissions. Its members count as members of the workspace — which is what keeps its access policies there meaningful — and can do nothing. Use it for a group that exists to narrow what another grant shows. It is not the same as DELETE, which detaches the group altogether.
In the UI a grant can be made from either end: from Organization settings → Groups, which is where you go when you are thinking about a team, or from the workspace’s Workspace settings → Groups tab (Workspace settings is the last item under Governance in the sidebar), which is where you go when you are thinking about a workspace. They are two views of the same rows. On the workspace tab an organization administrator can change a group’s roles, Revoke its access, or use Give a group access to add another group; an organization-wide grant is marked every workspace and can only be changed from the group itself.
A group can only be granted workspaces in its own organization; the database enforces it, not just the API.
Members
Adding and Removing
In the UI, there are two places:
- Members on the group’s page, where you add someone from the organization or remove them
- Organization settings → Members, where an organization administrator can tick or untick a person’s groups from the groups picker on their row. Each change saves immediately, and joining or leaving Organization admins asks for confirmation first. See Managing Members
Over the API, one account per call:
# Add
curl -X POST "$API_BASE_URL/api/v1/organizations/$ORG_ID/groups/$GROUP_ID/members" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"account_id": "b41d9e73-0a62-4c85-9f17-8e2d3b6a0c59"}'
# Remove
curl -X DELETE "$API_BASE_URL/api/v1/organizations/$ORG_ID/groups/$GROUP_ID/members/$ACCOUNT_ID" \
-H "Authorization: Bearer $API_TOKEN"The account must already be a member of the organization — invite them first.
A person can belong to any number of groups, and their reach is the combination of all of them. Adding or removing someone takes effect on their next request; removing them from a group they belonged to for one workspace’s sake is what removes them from that workspace.
Removing the last member of Organization admins is refused with 409 last_org_admin; in the Members tab’s groups picker, the refusal is shown under the picker. The rule is a transition, not a state: with two administrators either removal is allowed, so it only bites on the one that would empty the group.
Assigning Access Policies
Access policies are assigned to a group as a set, per workspace — the call replaces whatever the group had there.
In the UI, use the Access policies picker on the group’s row in the workspace’s Workspace settings → Groups tab. It is available to anyone holding both subsets.read and subsets.write in that workspace; the policies themselves are written under User Access Policies in the sidebar.
# Read the group's access policies in this workspace
curl "$API_BASE_URL/api/v1/workspaces/$WORKSPACE_ID/groups/$GROUP_ID/subsets" \
-H "Authorization: Bearer $API_TOKEN"
# Replace them
curl -X PUT "$API_BASE_URL/api/v1/workspaces/$WORKSPACE_ID/groups/$GROUP_ID/subsets" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"subset_ids": ["…", "…"]}'These two stay workspace-scoped, because a policy is written against one workspace’s data. You may assign policies to a group that does not yet hold a grant here — that is how you prepare a grant you are about to make.
How Several Policies Combine
For the member’s effective filter, policies are grouped by category:
- Policies in the same category are combined with
OR— the member sees rows matching any of them - Across categories, the groups are combined with
AND— every category’s condition must hold
A category can be marked required. A member who holds no policy in a required category sees no rows at all, rather than all of them. See Access Policies for worked examples.
Owners and Admins of a workspace are exempt from access-policy filtering, and so are organization administrators, who hold Owner there.
Deleting a Group
Deleting a group removes its members from it, along with every grant it held and every access-policy assignment. The accounts themselves stay in the organization; they simply stop reaching whatever that group reached. If it was their only route into a workspace, they stop being a member of it.
The two system groups cannot be deleted.
Patterns Worth Copying
| Pattern | Group | What it carries |
|---|---|---|
| Regional teams | ”EMEA Team”, “APAC Team” | Member on the shared workspace, plus one access policy each in a Region category |
| Partner isolation | ”Partner Acme” | An access policy on partner_id, in a category marked required |
| Sync operators | ”Sync Operators” | A custom role carrying syncs.trigger and stores.trigger — run the pipeline, don’t redefine it |
| External auditors | ”External Auditors” | Member on one workspace, one access policy narrowing what they can read |
| Per-workspace team | ”Retail EU builders” | One grant, one workspace — the closest thing to the old direct membership |
| Cross-cutting | ”EMEA Marketing” | Membership of two groups — region from one, department from the other, combined with AND |
Endpoints
GET /api/v1/organizations/{orgId}/groups
POST /api/v1/organizations/{orgId}/groups
GET /api/v1/organizations/{orgId}/groups/{groupId}
PUT /api/v1/organizations/{orgId}/groups/{groupId}
DELETE /api/v1/organizations/{orgId}/groups/{groupId}
GET /api/v1/organizations/{orgId}/groups/{groupId}/members
POST /api/v1/organizations/{orgId}/groups/{groupId}/members
DELETE /api/v1/organizations/{orgId}/groups/{groupId}/members/{accountId}
PUT /api/v1/organizations/{orgId}/groups/{groupId}/workspaces/{workspaceId}
DELETE /api/v1/organizations/{orgId}/groups/{groupId}/workspaces/{workspaceId}
GET /api/v1/workspaces/{id}/groups
GET /api/v1/workspaces/{id}/groups/{groupId}/subsets
PUT /api/v1/workspaces/{id}/groups/{groupId}/subsetsNext Steps
- Access policies — write the policies you assign here
- Folder and destination access — restrict a folder or destination to a group
- Roles — build the custom role a grant carries
- Managing members — invite people before you group them