Skip to Content

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:

  1. 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.
  2. 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:

GroupWhat it grants
Organization adminsThe Owner role on every workspace in the organization, now and in future
Organization viewersThe 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

PatternGroupWhat 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}/subsets

Next Steps

Last updated on