1. Docs
  2. Assign Roles

Assign Roles

Give identities the access a role bundles: one identity at a time, or many at once.

Overview

An assignment is the link between a role and an identity. Until a role is assigned to someone, nothing it bundles applies to anyone. Each assignment is a three-part record: identity_id, role_id, and node_id: the identity getting access, the role being granted, and the node in your Environment's hierarchy where the role takes effect. In flat RBAC the node_id is always your Environment's root, so the visible decision is just who and which role. Before you can assign a role, the identity has to exist at the Account and have active access to the Environment. Inside an organization there is one more: a role beneath the organization node needs an active membership there first, otherwise the grant answers 409 organization.membership_required; a role at the organization node itself admits the holder as a member. See the Identities overview for the three-layer model. A machine client holds roles the same way, through its own routes (/api/v1/oauth-clients/:id/assignments); see Machine Clients.

Scope

Role assignments are scoped to an Environment

When you assign a role to an identity at a node, the assignment lives in the active Environment, the one selected in the Console's Application and Environment switcher, or by selecting its card on Tenant → Applications. An identity is a single record in your Account directory, not duplicated per Environment, but it reaches an Environment only once you grant it access there, and the rows that say "this identity holds this role at this node" are per-Environment on top of that. Adding an identity to your Account does not place it in any Environment automatically.

A role assignment made in Development doesn't follow the identity to ProductionEach new Environment starts with zero assignments: Promote copies permissions, roles, and hierarchy across, but never assignmentsTime bounds (effective_from / effective_to) apply per-env: an assignment can be active in one env and expired in another

This is what makes safe iteration possible: you can hand out roles freely in Development to test invite flows or evaluator behavior, with zero risk of granting access in Production by accident.

View the Environments reference →

What an assignment grants

When you assign a role, the identity gains every permission the role bundles, at the target node and at every descendant via cascade. There's no separate publish step:

Permissions are computed at evaluate-time: the assignment row is the source of truth, so changes are live on the server the moment they're saved. Apps that evaluate locally with the SDK see them once their cached grants expire (60 seconds by default) or are cleared.Cascade is automatic: an assignment at a parent node grants the role at every descendant node. In flat RBAC this is the entire Environment by definition; in hierarchy mode it's the subtree below the chosen node.Changes are live for evaluate-time API checks immediately. If your application uses the permissions scope and embeds permissions in JWTs (capped at 200 per token), those refresh on the next token issuance.

If you ever need to revoke, removing the assignment row stops the cascade just as immediately.

Under the hood, Canopy evaluates access by walking up the node's lineage, collecting every active assignment along the way, and unioning the permissions of each role that is itself active; a deactivated role grants nothing. Inside an organization, an assignment at or beneath the organization node counts only while the holder's membership there is active; a deactivated member's grants are skipped and the decision reports ORGANIZATION_MEMBERSHIP_INACTIVE. The evaluate endpoint is the canonical source of truth at any moment.

Examples

Flat RBAC

Assign the role Manager to Alice. In flat RBAC every assignment lands at your Environment's single root node:

Acme Production

Alice now has Manager permissions across your entire Environment, because there's no narrower scope to choose.

Hierarchy

Assign the role Store Manager to Alice at the node Store #42:

Store #42
├── Electronics
├── Clothing
└── Warehouse

Alice now has Store Manager permissions at every node in that subtree:

Store #42ElectronicsClothingWarehouse

Direct vs Inherited

When evaluating a permission for an identity at a node, Canopy walks the hierarchy from that node up to the root, gathering every assignment that applies. The result is a union of:

Direct assignments: made at the node itself.Inherited assignments: made at any ancestor node (root, region, department, etc.).

An identity needs only one assignment at the highest applicable level, with no per-leaf duplication. Assign a Regional Manager role at the North America node and it covers every office and team beneath it.

Multiple roles per identity

An identity can hold many roles at the same node. The unique constraint is the three-tuple (identity_id, role_id, node_id), not the two-tuple (identity_id, node_id), so you can stack a Manager role on top of a Member role for the same person at the same place. Permissions union across roles, so the identity's effective access is the combination of every role they hold. There is no implicit role hierarchy: assigning Admin doesn't auto-replace Member; both rows live independently and both contribute to the union.

Assignments are additive: removing one role doesn't affect any other roles the same identity holds.

Time-bound access

Every assignment carries an effective window. Both ends are optional: left empty, effective_from means the assignment is in force from the moment it is created, and effective_to means it never ends. Together they place the assignment in one of three states:

Scheduled: effective_from is in the future. The assignment exists but doesn't grant access yet.Active: the current time falls between effective_from and effective_to. Access is granted.Expired: effective_to has passed. The assignment no longer grants access but stays in your audit trail.

The evaluator factors the lifecycle in automatically: a scheduled assignment doesn't grant access until its start date, and an expired one stops granting on its end date. No cron jobs, no manual cleanup. In the Console, Effective from and Effective to are days in your own time zone: access starts at the beginning of the Effective from day and ends at the close of the Effective to day, so a grant through 31 December includes 31 December. Over the API, send exact ISO 8601 timestamps; a bare date is read as midnight UTC.

Who may grant a role

Your API key and Console users may assign any active role anywhere in the Environment, except an organization's own role, which is held only at that organization's node or beneath it (anywhere else answers 400 rbac.role_outside_organization, see Roles). An identity acting with its own token, a customer's administrator inside an organization, is held to a ceiling, whether it calls the organization's own routes (/api/v1/organizations/{id}/assignments, members and invites) or uses the Access page of the organization admin portal:

The subset rule: an identity may grant a role at a node only when it holds, at that node or above, every permission the role carries, Canopy's reserved keys included.The refusal: a grant outside the ceiling answers 403 assignment.grant_ceiling, whether it is a new assignment, a change of role, the role given with a new member, or an invitation that carries one. Removing an assignment is never bounded by the ceiling. The one grant nobody can take away is the organization's last administrator's: removing it, changing its role, or doing either in a bulk request that would leave the organization with no administrator answers 409 organization.last_admin.Who is exempt: Console users, who already have developer authority; API keys; and the organization's own administrator, holding canopy:organization.manage at the organization node, inside that organization. The Workspace holds its administrators to an allowlist of its own.

The rule needs no configuration and cannot be misconfigured: whoever can delegate can only delegate what they have. Delegate Administration walks through it. Every assignment carries a version. Whoever changes one, through PATCH /api/v1/assignments/{id} or PATCH /api/v1/organizations/{id}/assignments/{assignmentId}, sends it back in If-Match, and a stale copy answers 409 assignment.version_mismatch instead of overwriting a colleague's change.

Next Step

Assignments connect roles to identities, and identities themselves have their own lifecycle, types, and management surface worth understanding.

Environment
API version
v1.0
On this page Was this page helpful?

Tell us how we can improve this guide.