Role & Group Tools
A role is the permission set one collaborator holds; a group is a bundle several collaborators share.
A role is the permission set one collaborator holds; a group is a bundle several collaborators share. These tools create and edit both, with permissions written as the same ability strings a token carries.
Tools
create_groupCreate GroupDESTRUCTIVEcreate_roleCreate RoleDESTRUCTIVEdelete_groupDelete GroupDESTRUCTIVEdelete_roleDelete RoleREADlist_groupsList GroupsREADlist_rolesList RolesDESTRUCTIVEsync_group_membersSync Group MembersDESTRUCTIVEupdate_groupUpdate GroupDESTRUCTIVEupdate_roleUpdate Rolecreate_group
DESTRUCTIVECreate a permission group on a team. Starts empty; add people with sync_group_members.
Creates a group: a named permission bundle several collaborators in a team can share. Someone can be in more than one.
The distinction from a role is what each attaches to. A role is what one collaborator is — everyone holds exactly one. A group is a bundle a set of people share. Both carry permissions. Neither bundles projects.
A group starts empty. Add people with sync_group_members — membership is deliberately not a field here, because naming a group and deciding who it applies to are different operations with different consequences.
Choose the code deliberately
code is unique within the team and cannot be changed afterwards —
permissions are addressed by it. Only name and the permission set stay
mutable.
Growth-tier capability. On a lower tier this returns TEAM_TIER_REQUIRED and
creates nothing.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Fires one event
Delivered to every endpoint subscribed to it once the change is made.
Annotations
A client that honours annotations asks a person before running it. It reaches beyond Subscriby: a connector, a provider or a member.
Arguments
team_id*stringUUID of the team the group belongs to.
code*stringStable identifier, alpha-dash. Unique per team and immutable once created.
name*stringHuman-readable group name.
permissionsarrayoptionalPermission codes (entity:action) the group grants.
What it returns
{ "data": { "id": "1f68d92a-04c5-4e83-97b1-3d6a05e2f847", "team_id": "a83f0d51-4c92-4b7e-8615-2fd9e70a3c86", "code": "billing-team", "name": "Billing Team", "permissions": ["project-subscription:view-any"] }}Emits group.created.
How it fails
AUTHENTICATION_REQUIREDno authenticated user on the request.
TOKEN_MISSING_ABILITYtoken lacks group:create.
TEAM_TIER_REQUIREDthe caller's platform tier does not include Teams.
RESOURCE_NOT_FOUNDno such team, or the caller is not a member.
VALIDATION_FAILEDcode already used on that team, malformed code, or an unknown permission string.
create_role
DESTRUCTIVECreate a role on a team with a permission set. The code is immutable.
Creates a role on a team. A role is what one collaborator is — everyone in a team holds exactly one. For a named bundle several people share, use create_group instead.
Subscriby seeds admin, manager and viewer on every new team; this adds your own.
Choose the code deliberately
code is unique within the team and cannot be changed afterwards —
invitations and role assignments address it. Only name, description and
the permission set stay mutable.
Growth-tier capability. On a lower tier this returns TEAM_TIER_REQUIRED and
creates nothing.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Fires one event
Delivered to every endpoint subscribed to it once the change is made.
Annotations
A client that honours annotations asks a person before running it. It reaches beyond Subscriby: a connector, a provider or a member.
Arguments
team_id*stringUUID of the team the role belongs to.
code*stringStable identifier, alpha-dash. Unique per team and immutable once created.
name*stringHuman-readable role name.
descriptionstringoptionalOptional description of what the role is for.
permissionsarrayoptionalPermission codes (entity:action) to grant. Omit for a role with none.
What it returns
{ "data": { "id": "d05e1a83-7c46-4f29-b613-8ae407c9d251", "team_id": "a83f0d51-4c92-4b7e-8615-2fd9e70a3c86", "code": "auditor", "name": "Auditor", "description": "Read-only access to subscriptions and payments", "permissions": ["project:view-any", "project-subscription:view-any"] }}Emits role.created.
How it fails
AUTHENTICATION_REQUIREDno authenticated user on the request.
TOKEN_MISSING_ABILITYtoken lacks role:create.
TEAM_TIER_REQUIREDthe caller's platform tier does not include Teams.
RESOURCE_NOT_FOUNDno such team, or the caller is not a member.
VALIDATION_FAILEDcode already used on that team, malformed code, or a permission string that is not in the catalog.
delete_group
DESTRUCTIVEDelete a group. Members stay in the team but lose what it granted.
Deletes a group. Everyone in it loses whatever it granted, all at once, but stays in the team.
Not tier-gated, unlike creating and updating. A creator whose tier lapsed has to be able to take access away. See what the tier gates.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Fires one event
Delivered to every endpoint subscribed to it once the change is made.
Annotations
A client that honours annotations asks a person before running it. It reaches beyond Subscriby: a connector, a provider or a member.
Arguments
group_id*stringUUID of the group to delete. Irreversible.
What it returns
{ "data": { "id": "1f68d92a-04c5-4e83-97b1-3d6a05e2f847", "deleted": true }}Emits group.deleted.
How it fails
AUTHENTICATION_REQUIREDno authenticated user on the request.
TOKEN_MISSING_ABILITYtoken lacks group:delete.
RESOURCE_NOT_FOUNDno such group in any team the caller belongs to.
delete_role
DESTRUCTIVEDelete a role. Collaborators holding it stay in the team.
Deletes a role. Anyone holding it stays in the team but loses whatever the role granted.
Not tier-gated, unlike creating and updating. A creator whose tier lapsed has to be able to take access away. See what the tier gates.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Fires one event
Delivered to every endpoint subscribed to it once the change is made.
Annotations
A client that honours annotations asks a person before running it. It reaches beyond Subscriby: a connector, a provider or a member.
Arguments
role_id*stringUUID of the role to delete. Irreversible.
What it returns
{ "data": { "id": "d05e1a83-7c46-4f29-b613-8ae407c9d251", "deleted": true }}Emits role.deleted.
How it fails
AUTHENTICATION_REQUIREDno authenticated user on the request.
TOKEN_MISSING_ABILITYtoken lacks role:delete.
RESOURCE_NOT_FOUNDno such role in any team the caller belongs to.
list_groups
READList groups across every team the caller owns or belongs to. Lighter-weight than roles — no permission payload.
List groups across every team the caller owns or belongs to. Groups cluster users without a permission payload — lighter-weight than roles. Ordered newest-first.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Annotations
It reads and never changes anything.
Arguments
This tool takes no arguments.
What it returns
{ "data": [ { "id": "1f68d92a-04c5-4e83-97b1-3d6a05e2f847", "team_id": "a83f0d51-4c92-4b7e-8615-2fd9e70a3c86", "code": "ops", "name": "Ops", "created_at": "2026-05-18T10:05:00Z" } ], "meta": { "total": 1 }}How it fails
AUTHENTICATION_REQUIREDno authenticated user on the request.
TOKEN_MISSING_ABILITYtoken lacks group:view-any.
list_roles
READList roles across every team the caller owns or belongs to. Each role carries a flat permission list.
List roles across every team the caller owns or belongs to. Each role carries a flat permissions array — the same ability codes a token would use. Ordered newest-first.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Annotations
It reads and never changes anything.
Arguments
This tool takes no arguments.
What it returns
{ "data": [ { "id": "d05e1a83-7c46-4f29-b613-8ae407c9d251", "team_id": "a83f0d51-4c92-4b7e-8615-2fd9e70a3c86", "code": "manager", "name": "Manager", "description": "Manage plans, resources, and subscribers", "permissions": ["project:view", "project:update", "project-user:update"], "created_at": "2026-05-18T10:05:00Z" } ], "meta": { "total": 3 }}How it fails
AUTHENTICATION_REQUIREDno authenticated user on the request.
TOKEN_MISSING_ABILITYtoken lacks role:view-any.
sync_group_members
DESTRUCTIVEReplace a group's membership wholesale. A sync, not an add.
Replaces a group's membership with exactly the supplied user ids.
This is a sync, not an add
Anyone absent from user_ids is removed, and an empty array empties the
group. Read the current membership with
list_groups and send the full intended list — not
just the people you want to add.
Every id must already belong to the group's team. A group grants permissions inside one tenant, so an outsider is refused rather than silently skipped — and the whole call fails rather than partly applying, so you never end up with a membership you did not ask for.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Fires one event
Delivered to every endpoint subscribed to it once the change is made.
Annotations
A client that honours annotations asks a person before running it. It reaches beyond Subscriby: a connector, a provider or a member.
Arguments
group_id*stringUUID of the group whose membership is being replaced.
user_ids*arrayComplete list of user UUIDs the group should contain. An empty array clears it.
What it returns
{ "data": { "id": "1f68d92a-04c5-4e83-97b1-3d6a05e2f847", "team_id": "a83f0d51-4c92-4b7e-8615-2fd9e70a3c86", "member_ids": [ "2a91c4e7-6f38-4b52-8e0d-9c1a7b3f5d80", "6f9b2e37-c184-4a05-8d72-30e16bc9f458" ] }}Emits group.members_synced, carrying added_ids and removed_ids as well as the final list — an access-control mirror should not have to diff two snapshots to work out what moved.
A sync that changes nothing emits nothing.
How it fails
AUTHENTICATION_REQUIREDno authenticated user on the request.
TOKEN_MISSING_ABILITYtoken lacks group:update.
TEAM_TIER_REQUIREDthe sync adds somebody and the caller's tier does not include Teams. Nothing changes.
RESOURCE_NOT_FOUNDno such group in any team the caller belongs to.
VALIDATION_FAILEDone or more ids are not in the group's team. The offending ids are named, and nothing is applied.
Tier
Gated on Growth only when the sync adds somebody. A sync that only removes people succeeds on any tier.
This is the one write in the identity surface whose gate depends on its argument. Deciding from the tool alone would leave a lapsed creator unable to take one person out of a group without deleting the whole group — and deleting is not gated, so the gate would only be pushing them toward the more destructive option. See what the tier gates.
update_group
DESTRUCTIVERename a group or replace its permission set. Membership is a separate tool.
Renames a group or replaces the permissions it grants. code is immutable, so a rename never breaks a reference.
Who is in the group is sync_group_members, not this — a rename is cosmetic, while changing the permission set silently re-scopes everyone already in the group.
Permissions replace, they do not merge
Sending permissions sets the group's permissions to exactly that list.
Omit the key entirely to leave the existing set untouched while changing
only the name.
Growth-tier capability. On a lower tier this returns TEAM_TIER_REQUIRED and
changes nothing.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Fires one event
Delivered to every endpoint subscribed to it once the change is made.
Annotations
A client that honours annotations asks a person before running it. It reaches beyond Subscriby: a connector, a provider or a member.
Arguments
group_id*stringUUID of the group to update.
namestringoptionalNew group name. Omit to keep the current one.
permissionsarrayoptionalComplete replacement permission set. Omit to leave permissions untouched.
What it returns
{ "data": { "id": "1f68d92a-04c5-4e83-97b1-3d6a05e2f847", "team_id": "a83f0d51-4c92-4b7e-8615-2fd9e70a3c86", "code": "billing-team", "name": "Billing & Finance", "permissions": ["project-subscription:view-any", "payment:view-any"] }}Emits group.updated.
How it fails
AUTHENTICATION_REQUIREDno authenticated user on the request.
TOKEN_MISSING_ABILITYtoken lacks group:update.
TEAM_TIER_REQUIREDthe caller's platform tier does not include Teams.
RESOURCE_NOT_FOUNDno such group in any team the caller belongs to.
VALIDATION_FAILEDan unknown permission string.
update_role
DESTRUCTIVERename a role or replace its permission set. The code stays fixed.
Renames a role, changes its description, or replaces the permissions it grants. code is immutable, so a rename never breaks an assignment.
Permissions replace, they do not merge
Sending permissions sets the role's permissions to exactly that list. Omit
the key entirely to leave the existing set untouched while changing only the
name — sending an empty array strips every permission from everyone holding
the role.
Growth-tier capability. On a lower tier this returns TEAM_TIER_REQUIRED and
changes nothing.
Requires ability
The token behind the MCP session must hold it, or the call is refused with TOKEN_MISSING_ABILITY.
Runs the same action as
The REST endpoint and this tool share one action, so validation, permissions and events are identical.
Fires one event
Delivered to every endpoint subscribed to it once the change is made.
Annotations
A client that honours annotations asks a person before running it. It reaches beyond Subscriby: a connector, a provider or a member.
Arguments
role_id*stringUUID of the role to update.
namestringoptionalNew role name. Omit to keep the current one.
descriptionstringoptionalNew description. Omit to keep the current one.
permissionsarrayoptionalComplete replacement permission set. Omit to leave permissions untouched.
What it returns
{ "data": { "id": "d05e1a83-7c46-4f29-b613-8ae407c9d251", "team_id": "a83f0d51-4c92-4b7e-8615-2fd9e70a3c86", "code": "auditor", "name": "Auditor & Finance", "description": "Read-only access to subscriptions and payments", "permissions": ["project:view-any", "project-subscription:view-any"] }}Emits role.updated.
How it fails
AUTHENTICATION_REQUIREDno authenticated user on the request.
TOKEN_MISSING_ABILITYtoken lacks role:update.
TEAM_TIER_REQUIREDthe caller's platform tier does not include Teams.
RESOURCE_NOT_FOUNDno such role in any team the caller belongs to.
VALIDATION_FAILEDa permission string that is not in the catalog.
How is this guide?