Skip to main content
Role-Based Access Control (RBAC) is the right choice when access decisions depend on who a user is within an organization — admin, manager, member — rather than on specific resource relationships or request context. If your permission logic maps cleanly to a fixed set of named roles, RBAC is the simplest and most maintainable approach. In Permify, roles are modeled as relations on an entity. You assign users to those relations, then write actions (permissions) that reference them. No special role construct is required — the schema DSL handles it naturally.

When to use RBAC

  • You have a predictable, bounded set of roles (e.g., admin, manager, member).
  • Permissions are uniform across all users with the same role.
  • You don’t need per-resource ownership rules or dynamic attribute checks.
  • You want a model that is easy for non-engineers to reason about.
RBAC and ReBAC can coexist in a single Permify schema. You can start with pure RBAC and layer in relationship-based rules as your product grows.

Example: SaaS organization with file permissions

This example models an organization with four roles — admin, manager, member, and agent — and two categories of resources: organization files and vendor files. Each resource category has its own set of permissions, and some roles have narrower access than others.

Complete schema

entity user {} 

entity organization {

    // roles
    relation admin   @user
    relation member  @user
    relation manager @user
    relation agent   @user

    // organization file permissions
    action view_files        = admin or manager or (member not agent)
    action edit_files        = admin or manager
    action delete_file       = admin

    // vendor file permissions
    action view_vendor_files   = admin or manager or agent
    action edit_vendor_files   = admin or agent
    action delete_vendor_file  = agent

}

How the schema works

The organization entity declares four relations. Each relation maps directly to a role — any user can be assigned to one or more of them. Actions combine roles with boolean operators (or, not) to express nuanced rules:
  • action edit_files = admin or manager — only admins and managers can edit files.
  • action view_files = admin or manager or (member not agent) — admins, managers, and members who are not also agents can view files. Members who hold the agent role are excluded.
  • action delete_vendor_file = agent — only users with the agent role can delete vendor files.
Permify Schema supports and, or, not, and and not operators so you can express any combination of roles in a single action.

End-to-end implementation

1

Write the schema

Push your schema to Permify using the Schema Write API.
curl --location --request POST 'localhost:3476/v1/tenants/t1/schemas/write' \
--header 'Content-Type: application/json' \
--data-raw '{
  "schema": "entity user {} entity organization { relation admin @user relation member @user relation manager @user relation agent @user action view_files = admin or manager or (member not agent) action edit_files = admin or manager action delete_file = admin action view_vendor_files = admin or manager or agent action edit_vendor_files = admin or agent action delete_vendor_file = agent }"
}'
Save the returned schema_version — you can pin requests to a specific version to avoid race conditions during schema updates.
2

Assign users to roles

Write relationship tuples to assign users to roles within an organization. Each tuple takes the form entity#relation@subject.
curl --location --request POST 'localhost:3476/v1/tenants/t1/relationships/write' \
--header 'Content-Type: application/json' \
--data-raw '{
  "metadata": {
    "schema_version": ""
  },
  "tuples": [
    {
      "entity": { "type": "organization", "id": "1" },
      "relation": "admin",
      "subject": { "type": "user", "id": "daniel" }
    },
    {
      "entity": { "type": "organization", "id": "1" },
      "relation": "member",
      "subject": { "type": "user", "id": "ashley" }
    },
    {
      "entity": { "type": "organization", "id": "1" },
      "relation": "manager",
      "subject": { "type": "user", "id": "mert" }
    },
    {
      "entity": { "type": "organization", "id": "1" },
      "relation": "agent",
      "subject": { "type": "user", "id": "ege" }
    }
  ]
}'
3

Check permissions

Call the Check API to enforce access at runtime. Permify evaluates the schema and stored tuples and returns an allow or deny decision.
cURL
curl --location --request POST 'localhost:3476/v1/tenants/t1/permissions/check' \
--header 'Content-Type: application/json' \
--data-raw '{
  "metadata": {
    "snap_token": "",
    "schema_version": "",
    "depth": 20
  },
  "entity": { "type": "organization", "id": "1" },
  "permission": "delete_file",
  "subject": { "type": "user", "id": "daniel" }
}'
Response
{
  "can": "RESULT_ALLOWED"
}

Permission matrix

The following table summarizes which roles can perform each action in this schema.
Actionadminmanagermemberagent
view_files
edit_files
delete_file
view_vendor_files
edit_vendor_files
delete_vendor_file
A member who is also assigned the agent relation is denied view_files because of the member not agent expression. Both relations must be checked when evaluating that action.

Building ReBAC Systems

Add relationship-based rules on top of roles — useful for resource ownership and hierarchies.

Building ABAC Systems

Extend permissions with attribute checks such as IP range, region, or account balance.

Modeling guide

Full reference for the Permify Schema DSL — entities, relations, actions, and rules.

Sync data

Learn how relationship tuples are written and stored.

Build docs developers (and LLMs) love