Groups API
Use the Permit.io Groups API to give a set of users the same access in one call: assign a role to a group, and every member of the group derives that role. This page is for developers who manage relationship-based access control (ReBAC) groups through the API. To manage groups from Python code, see Manage groups with the Python SDK. To manage groups in the Permit dashboard instead, see Groups UI.
How groups work
A group is a resource instance of a group resource type, in one tenant. For example, the marketing instance of the teams resource type is a group. Groups build on ReBAC role derivation:
- Group members. When you add a user to a group, Permit assigns the user the
memberrole on the group instance. When you remove the user, Permit removes that role, and the user stops deriving the group's roles. - Group roles. When you assign a role on a resource instance to a group, Permit creates three objects: a relation between the group resource type and the resource type, a relationship between the group instance and the resource instance, and a role derivation from the group's
memberrole to the assigned role. Every member of the group derives the assigned role on the resource instance, including users you add later. - Group hierarchy. When you assign one group to another group, Permit links the two groups with a relationship and a derivation between their
memberroles. Group-to-group assignment works only between groups of the same resource type.
Example: a marketing team that edits a training video
The marketing team must edit the training_video instance of the social_media resource type.
- Create a group with the instance key
marketingand the resource typeteams. - Assign the
editorrole onsocial_media:training_videoto themarketinggroup. - Add the marketing users to the group.
Every user in the marketing group, including users you add later, derives the editor role on social_media:training_video. Give a user group access to a resource instance sets up this example step by step.
Prerequisites
- An environment API key. See Get your API key.
- The keys or IDs of your project and environment. See Get the project and environment.
- A tenant for the group, the resource instances, and the users. A group, the resource instances it gets roles on, and its users must all use the same tenant.
- Familiarity with ReBAC in Permit.
Groups API endpoints
All paths start with https://api.permit.io/v2/schema/{proj_id}/{env_id}.
| Task | Method and path | Notes |
|---|---|---|
| Create a group | POST /groups | |
| List groups | GET /groups/direct | Use instead of the deprecated GET /groups |
| Get a group | GET /groups/direct/{group_instance_key} | Use instead of the deprecated GET /groups/{group_instance_key} |
| Delete a group | DELETE /groups/{group_instance_key} | |
| Assign a role to a group | POST /groups/{group_instance_key}/roles | |
| Remove a role from a group | DELETE /groups/{group_instance_key}/roles | |
| List the roles of a group | GET /groups/{group_instance_key}/roles | Early access |
| Add a user to a group | PUT /groups/{group_instance_key}/users/{user_id} | |
| Remove a user from a group | DELETE /groups/{group_instance_key}/users/{user_id} | |
| List the users of a group | GET /groups/{group_instance_key}/users | Early access |
| Assign a group to a group | PUT /groups/{group_instance_key}/assign_group | |
| Remove a group from a group | DELETE /groups/{group_instance_key}/assign_group | |
| List child groups | GET /groups/{group_instance_key}/children | Early access |
| List parent groups | GET /groups/{group_instance_key}/parents | Early access |
group_instance_key in a path names the group in one of three ways: resource_type:instance, for example teams:marketing; the group's ID, the id that the list and get endpoints return; or the instance key alone, for example marketing, which the API reads as group:marketing, so it finds only groups of the default group resource type. The API reference marks the GET /groups and GET /groups/{group_instance_key} endpoints as deprecated. Use the /groups/direct endpoints for new code.
Give a user group access to a resource instance
This procedure sets up the marketing team example: members of the marketing group get the editor role on the training_video instance of social_media.
In every command, replace API_SECRET_KEY with your environment API key, and {proj_id} and {env_id} with your project and environment keys or IDs. The samples use the tenant default. Use one tenant key of your own in every call.
1. Create the group
Send a POST request with the group instance key, the resource type key, and the tenant. If you omit group_resource_type_key, the group's resource type is group.
curl 'https://api.permit.io/v2/schema/{proj_id}/{env_id}/groups' \
-H 'authorization: Bearer API_SECRET_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{"group_instance_key": "marketing", "group_resource_type_key": "teams", "group_tenant": "default"}'
2. Create the resource and resource instance
Create the social_media resource with the actions the group's members need. A resource with no actions grants nothing, so a later check against it always returns false:
curl 'https://api.permit.io/v2/schema/{proj_id}/{env_id}/resources' \
-H 'authorization: Bearer API_SECRET_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{"key": "social_media", "name": "social media", "actions": {"view": {"name": "view"}, "edit": {"name": "edit"}}}'
Create the training_video resource instance. Resource instances are facts objects, so the v2 path is /v2/facts/{proj_id}/{env_id}/resource_instances:
curl 'https://api.permit.io/v2/facts/{proj_id}/{env_id}/resource_instances' \
-H 'authorization: Bearer API_SECRET_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{"key": "training_video", "tenant": "default", "resource":"social_media"}'
3. Create the editor role
The role you assign to a group must be a role of the target resource type. permissions takes the action keys the role grants on that resource type, so the editor role below grants view and edit on any social_media instance. A role created without permissions grants nothing. For the resource role endpoint, see ReBAC API calls.
curl 'https://api.permit.io/v2/schema/{proj_id}/{env_id}/resources/social_media/roles' \
-H 'authorization: Bearer API_SECRET_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{"key": "editor", "name": "editor", "permissions": ["view", "edit"]}'
4. Assign the editor role to the group
Send a POST request to /groups/{group_instance_key}/roles with the resource type, the resource instance, the role, and the tenant. All four fields are required. The group instance key in the path uses the type:instance format, here teams:marketing.
curl 'https://api.permit.io/v2/schema/{proj_id}/{env_id}/groups/teams:marketing/roles' \
-H 'authorization: Bearer API_SECRET_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{"resource": "social_media", "resource_instance": "training_video", "role": "editor", "tenant": "default"}'
5. Add users to the group
Send a PUT request for each user, with the tenant in the body. In the path, the group instance key is teams:marketing and {user_id} is the user key or ID.
curl -X PUT \
'https://api.permit.io/v2/schema/{proj_id}/{env_id}/groups/teams:marketing/users/{user_id}' \
-H 'authorization: Bearer API_SECRET_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{"tenant": "default"}'
What Permit creates when you assign a role to a group
After step 4, Permit creates these objects:
- A role derivation from the
memberrole onteamsto theeditorrole onsocial_media. - A relation between the
teamsresource type and thesocial_mediaresource type. - A relationship between the
teams:marketinginstance and thesocial_media:training_videoinstance.
Every user with the member role on teams:marketing derives the editor role on social_media:training_video, including users you add to the group later.
6. Verify the group setup
List the roles of the group. The response includes the editor role on social_media:training_video.
curl -X GET \
'https://api.permit.io/v2/schema/{proj_id}/{env_id}/groups/teams:marketing/roles' \
-H 'authorization: Bearer API_SECRET_KEY'
List the users of the group. The response includes the users you added in step 5.
curl -X GET \
'https://api.permit.io/v2/schema/{proj_id}/{env_id}/groups/teams:marketing/users' \
-H 'authorization: Bearer API_SECRET_KEY'
Then run permit.check() for a user you added in step 5, the edit action, and the social_media:training_video instance, in the default tenant. The check returns true, because the user's member role on teams:marketing derives the editor role on that instance, and editor grants edit. A check for an action the editor role doesn't grant returns false. See Check permissions.
Assign a group to another group
Assign one group to another group to build a group hierarchy, for example a support team inside an organization. In this example, the support group and the org1 group both use the default group resource type, and the members of support become members of org1.
Limitation: groups must share a resource type
You can assign a group only to a group of the same resource type. For example, you can assign group:org1 to group:support, but not to teams:marketing.
1. Create the two groups
Create the support group:
curl 'https://api.permit.io/v2/schema/{proj_id}/{env_id}/groups' \
-H 'authorization: Bearer API_SECRET_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{"group_instance_key": "support", "group_tenant": "default"}'
Create the org1 group:
curl 'https://api.permit.io/v2/schema/{proj_id}/{env_id}/groups' \
-H 'authorization: Bearer API_SECRET_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{"group_instance_key": "org1", "group_tenant": "default"}'
2. Assign one group to the other
Send a PUT request to /groups/{group_instance_key}/assign_group. The path names the group whose members join the other group, here group:support. The body names the group they join, by its instance key alone or by its ID, here org1. The body doesn't take the type:instance format: the API looks the body's key up among the groups of the path group's resource type, so group:org1 matches no group and the API returns 404.
curl -X PUT \
'https://api.permit.io/v2/schema/{proj_id}/{env_id}/groups/group:support/assign_group' \
-H 'authorization: Bearer API_SECRET_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{"group_instance_key": "org1"}'
After this request, every member of support derives the member role on org1, and through it the roles assigned to org1. The assignment works in that direction only: the members of org1 derive nothing from support. To undo it, send the same request with DELETE.
When the members of support become members of org1, Permit creates:
- A role derivation from the
memberrole ongroupto thememberrole ongroup. - A relation from the
groupresource type to itself. - A relationship between the
group:supportinstance and thegroup:org1instance.
3. Verify the group hierarchy
List the child groups of a group:
curl -X GET \
'https://api.permit.io/v2/schema/{proj_id}/{env_id}/groups/group:support/children' \
-H 'authorization: Bearer API_SECRET_KEY'
List the parent groups of a group:
curl -X GET \
'https://api.permit.io/v2/schema/{proj_id}/{env_id}/groups/group:org1/parents' \
-H 'authorization: Bearer API_SECRET_KEY'
The child list of one group includes the other group, and the parent list of the other group includes the first group.
List and get groups
List all groups
List the groups in an environment as a flat, paginated list, without their hierarchy:
curl -X GET \
'https://api.permit.io/v2/schema/{proj_id}/{env_id}/groups/direct' \
-H 'authorization: Bearer API_SECRET_KEY'
To filter the list, add the tenant, resource, or search query parameter. Use page and per_page (up to 100) to page through results.
Get a specific group
Get one group by its group instance key, for example teams:marketing:
curl -X GET \
'https://api.permit.io/v2/schema/{proj_id}/{env_id}/groups/direct/teams:marketing' \
-H 'authorization: Bearer API_SECRET_KEY'
The response holds the group resource type key, the group instance key, the tenant, and the group ID.
Manage groups with the Python SDK
In the Python SDK, permit.api.groups sends the Groups API requests on this page. It needs permit 3.1 or later, and an environment API key, or a project or organization API key with the SDK's API context set to the environment.
This example sets up the marketing team example from Python. Create the social_media resource and the editor role first, as in steps 2 and 3. Replace <YOUR_API_KEY> with your environment API key.
Create the client and the group. In create(), group_instance_key is the instance key alone, marketing, not teams:marketing:
import asyncio
from permit import Permit
permit = Permit(token="<YOUR_API_KEY>", pdp="http://localhost:7766")
async def create_marketing_group():
await permit.api.groups.create(
{
"group_instance_key": "marketing",
"group_resource_type_key": "teams",
"group_tenant": "default",
}
)
asyncio.run(create_marketing_group())
Assign the editor role on social_media:training_video to the group, and add a user to the group. Replace <USER_KEY> with the key of an existing user. Like every call of the asyncio client, these calls run inside an async function:
await permit.api.groups.assign_role(
"teams:marketing",
{
"role": "editor",
"resource": "social_media",
"resource_instance": "training_video",
"tenant": "default",
},
)
await permit.api.groups.assign_user("teams:marketing", "<USER_KEY>", tenant="default")
Once the PDP has the change, a check for that user, the edit action, and the training_video instance returns True:
allowed = await permit.check(
"<USER_KEY>", "edit", {"type": "social_media", "key": "training_video", "tenant": "default"}
)
The role is a resource role on the training_video instance, which the group's members derive through ReBAC role derivation over the group instance. It isn't a tenant-wide role: a check must name the resource instance, and a check that names only the resource type, such as permit.check("<USER_KEY>", "edit", "social_media"), doesn't use it.
Python groups methods
| Method | Endpoint |
|---|---|
list(page=1, per_page=100) | GET /groups/direct |
get(group_instance_key) | GET /groups/direct/{group_instance_key} |
create(group_data) | POST /groups |
delete(group_instance_key) | DELETE /groups/{group_instance_key} |
assign_role(group_instance_key, role_data) | POST /groups/{group_instance_key}/roles |
remove_role(group_instance_key, role_data) | DELETE /groups/{group_instance_key}/roles |
assign_user(group_instance_key, user_key, tenant) | PUT /groups/{group_instance_key}/users/{user_id} |
remove_user(group_instance_key, user_key, tenant) | DELETE /groups/{group_instance_key}/users/{user_id} |
assign_group(group_instance_key, {"group_instance_key": ...}) | PUT /groups/{group_instance_key}/assign_group |
remove_group(group_instance_key, {"group_instance_key": ...}) | DELETE /groups/{group_instance_key}/assign_group |
- Group identifiers. The
group_instance_keyargument of every method butlist()andcreate()takes the group's ID,"<type>:<key>"such as"teams:marketing", or the instance key alone, such as"marketing", which finds only groups of the defaultgroupresource type. - Group-to-group assignment.
assign_group("group:support", {"group_instance_key": "org1"})makes the members ofsupportmembers oforg1, so they derive the roles assigned toorg1. The members oforg1derive nothing fromsupport.remove_group()with the same arguments undoes it. Both groups must be of the same resource type, and the second argument names its group by instance key alone or by ID, never"<type>:<key>". - Listing.
list()returns one page of groups with the total count, and doesn't take thetenant,resource, orsearchfilters. It lists the instances of every resource type that has amemberrole. - Early-access endpoints. The SDK has no methods for the early-access endpoints that list a group's roles, users, child groups, or parent groups.
The blocking client, permit.sync.Permit, has the same methods. Call them without await.
API reference
For request and response schemas of every Groups endpoint, see the Groups section of the Permit API reference.
Related pages
- Groups UI: manage groups in the Permit dashboard
- What is ReBAC?
- ReBAC API calls