Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.hopsule.com/llms.txt

Use this file to discover all available pages before exploring further.

Custom roles let Enterprise organizations define their own role names and permission sets. Built-in Owner and Member roles ship with every org; on Enterprise you can add as many as you need — Auditor for SOC 2 separation of duties, Reviewer for read + decision approval access, External integrator for read-only API consumers, anything that fits how your team works.
Custom roles require the Enterprise plan. Non-Enterprise orgs see the two built-in roles and a paywalled UI under Settings → Roles.

How it works

Each organization carries a per-org role catalog in OrganizationRole. Owner is sealed at the database level (one immutable row per org) so no permission edit can lock an org out. Member is system-flagged but editable — owners can rename it (“Contributor”) or narrow its permissions. Every member’s UserOrganization row carries a role_id foreign key to the catalog. On each authenticated request, the auth middleware resolves that role into a permission slice and attaches it to the request context — so every handler downstream sees the same effective set regardless of which role label the user actually holds.

Walkthrough

1

Open Settings → Roles

From the organization dropdown, switch to the org. Open Settings and click Roles in the sidebar. You’ll see the two seeded rows (Owner, Member) plus any custom roles you’ve already created.
2

Create a custom role

Click Create role. A sheet opens with:
  • Name — must be unique within your org (case-insensitive).
  • Description — what this role is for.
  • Copy permissions from — optional. Picking Member pre-fills the 13 default permissions; you tweak from there.
  • Permission matrix — 47 keys across 16 domains (Decisions / Memories / Capsules / Tasks / Team / Settings / Billing / …). Each domain has Select all / Deselect all. Sensitive permissions are marked with a warning icon — be deliberate.
3

Assign at invite time

From Team → Invite user, the role dropdown lists every assignable role in your org. Pick Auditor, send the invite, and the invitee lands with that role’s exact permissions.
4

Reassign existing members

Open Team, click the role pill on any member row, and pick a new role. Owners can’t be demoted past the last-Owner guard — the server rejects any operation that would leave the org without an Owner.

Built-in roles

Owner

PermissionsAll 47 keys (always full access)
Editable?No — name and permissions are sealed
Deletable?No — immutability is enforced at the database layer
Count per orgExactly one
The Owner row never appears in invite or change-role dropdowns. Owner identity changes through ownership transfer (a separate flow), never through role edits.

Member

Permissions38 standard keys (no *:delete, *:manage)
Editable?Yes — name and permissions can be changed
Deletable?No — every org keeps a built-in fallback row
Count per orgExactly one
If you want to rename Member to “Contributor”, or narrow its perms to remove org:decisions:reject, do it in the role editor. The change applies to every existing Member-role membership immediately.

Permission reference

Each permission key follows the pattern org:<domain>:<verb>:
DomainPermissions
decisionscreate, read, update, delete, accept, reject, deprecate
memoriescreate, read, update, delete
capsulescreate, read, freeze, supersede
taskscreate, read, update, delete
commentscreate, read
sharingcreate, read, revoke
tagscreate, read, delete, attach
chatcreate, read, update, delete
conflictsread, resolve
importsstart, read, cancel
graphread, manage
notificationsread, manage
teamread, invite, manage
settingsread, manage
billingread, manage
projectscreate, read, update, delete
The full descriptive labels live in the role editor — hover any checkbox for the long description.

Common recipes

Use this for SOC 2 audit teams, security reviewers, or external consultants who need visibility without write access.Copy from Member, then Deselect all, then enable:
  • all org:*:read keys
  • org:sharing:create (so they can generate share links to capsules for downstream review)
For senior engineers who approve decisions but don’t write code.Enable:
  • org:decisions:read, org:decisions:accept, org:decisions:reject, org:decisions:deprecate
  • org:memories:read, org:capsules:read, org:tasks:read
  • org:comments:create, org:comments:read
  • org:conflicts:read, org:conflicts:resolve
For partner systems that consume your decision history via the API.Enable only:
  • org:decisions:read, org:memories:read, org:capsules:read
  • org:tasks:read, org:graph:read
  • org:projects:read
Pair with an API token scoped to this role for the partner integration.
For knowledge-management staff who curate the taxonomy without touching content.Enable:
  • all org:*:read keys
  • org:tags:create, org:tags:delete, org:tags:attach

Tier downgrade

If your org downgrades from Enterprise back to Pro:
  • Existing custom roles stay — members keep their assigned permissions.
  • Settings → Roles becomes read-only. A banner explains that Custom roles require Enterprise; existing assignments still apply but you can’t create, edit, or delete roles until you re-upgrade.
  • Invite and change-role dropdowns still list every existing role — you can put a teammate into the existing Auditor role even on Pro.
  • Re-upgrading restores write access immediately. No data migration in either direction.

API reference

All endpoints live under /organizations/{orgId}/roles. List and Get are readable by any org member; mutations require org:settings:manage AND Enterprise tier.
EndpointStatusNotes
GET /organizations/{orgId}/roles200Returns all rows for the org. Members can read.
GET /organizations/{orgId}/roles/{roleId}200, 404Single role.
POST /organizations/{orgId}/roles201, 402 LICENSE_REQUIRED, 409 NAME_CONFLICTBody: {name, description, permissions}.
PATCH /organizations/{orgId}/roles/{roleId}200, 403 (Owner is immutable)Partial: any subset of name/description/permissions.
DELETE /organizations/{orgId}/roles/{roleId}204, 422 (still has members)System rows can’t be deleted.
Every request must include an Authorization: Bearer <token> header (NextAuth session token in dashboard, API token for service-to-service).

Limitations

  1. Owner is immutable. You can’t change Owner’s permissions or rename it. If you need a second “super admin”, create a custom role with the same permission set — but only one row per org has is_immutable=true.
  2. One Owner per org. Demoting the last Owner returns 400 MUST_HAVE_OWNER. Transfer ownership first if you need to leave.
  3. Member can’t be deleted. It’s the fallback when a custom role is removed and the operator picks “reassign to Member” (Phase I.4).
  4. Cross-org role isolation. Roles are scoped by organization_id; you can’t reference an Org A role when inviting into Org B.

FAQ

The delete endpoint returns 422 if the role still has members. You must reassign them first (Phase I.4 ships the in-modal reassignment flow; for now use the change-role dropdown on each member row).
Yes. Open the role editor on the Member row, change the name to “Contributor”, save. The change applies to every existing membership immediately — there’s no migration step.
No. Each role belongs to exactly one organization. If you run multiple orgs and want the same role definition in each, create them separately — the permission catalog is global so the keys match.
Backend. The frontend asks usePermissions().can("...") for UI gating (hiding buttons, disabling fields), but every actual write is checked server-side from the request’s resolved permission slice. A user with a stale browser session can’t escalate by tinkering with React state.