Author and duplicate a policy
Purpose
Build the access you need when none of the five built-in roles is right — by copying one and trimming it, rather than starting from nothing.
Who this is for
Account administrators. If Roles & Policies is missing from your sidebar, you do not hold the permissions this page describes.
The three words, in the right order
- A policy is a named bag of permission tokens. A token is a string like
campaigns.createoraccount_roles.modify; there are 127 of them across 27 namespaces. One of the 27,communications, is shown in the editor as Communications (Extended) because it post-dates the category list the other 26 come from. - A role is a named bundle of policies, plus a colour. A role holds no tokens of its own.
- An assignment binds one person to one role at one scope.
So the power you grant somebody is: the union of the tokens in the policies attached to the role you assign them, at the scope you assign it. Trimming what somebody can do means editing a policy, not a role.
Where it lives
Roles & Policies in the sidebar, under People & settings. It goes to
/<accountId>/dashboard/team?tab=member_roles — the same Team & access page
as everything else about people, on its own tab. The older
/<accountId>/dashboard/settings/roles address redirects here.
Six tabs: Members list, Member invitations, Member assignments, Member roles, Policies, Communications. This page is about the last two but one.
What you start from
Five roles are seeded into every account, each with its own colour:
| Role | What it carries |
|---|---|
| Account Manager | Account Administration + Member Administration policies |
| Campaign Manager | Campaign Management policy |
| Contact Manager | Member Administration policy |
| Campaign Auditor | Campaign Auditor policy |
| Assessment Auditor | Assessment Auditor policy |
Above the table, What the built-in roles grant expands into a per-role summary of how many actions each carries and through which policies. Read it before you invent anything: most "I need a custom role" requests are answered by picking a different built-in one.
It ends with a warning worth repeating here: Account Manager is account
administration, not campaign operations. It does not carry campaigns.clone,
assessments.create, assessments.modify or assessments.delete — those are the
Campaign Manager's. Somebody who has to clone a campaign or add an assessment
needs that role too, or a custom role carrying both policies.
You may also see Campaign Self-Join Participant and Account Self-Join Participant in the table, marked System Role and Locked. Those are minted by the self-join doors; leave them alone.
Duplicate a built-in role
This is the way to author a role. Duplicating a system role is legal and supported, and it is what the harness and the seeding work both do.
- Member roles tab, find the closest built-in role.
- Open the row's ⋮ menu and choose Duplicate role.
- You get a new role named
{original} (Copy)— and(Copy 2),(Copy 3)if that name is taken. It is a Custom Role, owned by your account, unlocked, and it keeps the original's colour and description. - Every system policy the original carried is copied too, into an editable
custom policy of its own named
{policy} (Copy). Policies your account already owns are attached as they are, not re-copied.
The toast tells you what happened: Created "…" with N editable policy copies, or just Created "…" when there were none to copy.
That last step is the point of duplicating rather than starting empty: you now own editable copies of the exact permission sets the built-in role had, instead of having to rebuild them token by token.
Duplicate policy on the Policies tab does the same for a single policy.
Trim the tokens
Tokens live on policies. There are two ways to reduce what a role can do.
Detach a policy from the role. The row's pencil icon opens Edit Custom Role (a system role gets an eye instead, because it is read-only; the ⋮ menu holds only Duplicate role, Lock/Unlock role and Delete role): Role Title, Color, Description, and Attached Policies as a checkbox list with a System badge on the ones you do not own. Untick and Save Role. There is no search in that list; it scrolls.
Edit the tokens on a policy you own. Policies tab → the policy's pencil icon opens Edit Custom Policy. The right column is Policy Permissions in two modes:
- Visual — a counter reading Selected actions: N, then the tokens grouped by namespace with a checkbox and the raw token beside each human label. There is no search box here either; the grouping is the navigation.
- JSON — a raw textarea over
{ "actions": [...] }. The visual toggles are disabled while the JSON is invalid, and it says why.
Below the groups, a collapsed disclosure reads Not enforced by any route (16). Those tokens are part of the vocabulary but no API route gates on them, so granting one confers nothing. They are shown read-only, for policies that already carry them, and can only be removed in JSON mode. They are kept rather than deleted because deleting them would silently drop tokens out of policies real accounts have already saved — and because a granted-but-unenforced token is a grant that buys nothing, not a leak.
Save with Save Policy. If it is refused the dialog stays open with your edits intact.
You cannot edit a system policy or a system role. On a system role the row offers an Inspect eye rather than a pencil; Role Visualizer then shows its Effective Resolved Capabilities — useful for working out what to copy.
Why a save can be refused
Assessorio will not let you hand out access you do not hold yourself. Four different writes check it, and all four refuse the same way — a 403 whose message names one offending token and counts the rest:
Forbidden: cannot give a role permissions you do not hold — 'account_roles.modify' (+2 more)
The four messages differ only in the verb: cannot grant a role carrying permissions you do not hold (assigning), cannot revoke a role carrying permissions you do not hold (revoking), cannot author a policy carrying permissions you do not hold (creating or editing a policy), and cannot give a role permissions you do not hold (creating a role, attaching policies, duplicating, importing).
The refusal appears as an error toast; the dialog stays open with your work in it.
Three ways out of it, in order of likelihood:
- You are not an account administrator. A holder of
account_roles.modifyis exempt from all four checks — and soundly, because they could already author the role and assign it directly, so refusing here would only move the same grant one route to the left. If you keep hitting this, ask for that permission rather than working around it. - You are granting a role that carries nothing. Always allowed.
- You are an auditor manager granting one of the two seeded auditor roles. A narrow, deliberate exception.
The assign dialog also pre-empts the refusal at Global scope: it filters the role list and prints N roles not shown: you cannot grant permissions you do not hold yourself. At the narrower scopes it offers every role and lets the server decide, because a client-side answer computed at account scope would under-estimate and hide a control that would in fact have been allowed.
Assign it, and choose the scope
Assign Roles to Members takes members, roles and a scope:
| Scope | Means |
|---|---|
| Global | Everywhere in this account. |
| Campaign | Restricted to Campaigns. — the ones you pick. |
| Assessment | Restricted to Assessments. — the ones you pick. |
| Participant | Specific to a Participant. — the ones you pick. |
For Assessment and Participant the dialog asks for the parent Campaign first, then lists that campaign's assessments or participants.
The rule that matters: an assessment- or participant-scoped grant resolves only inside its own campaign. It is real, and it confers nothing at account level. If somebody needs a permission everywhere, that is a Global assignment.
The footer says how many combinations you are about to create — This will generate N assignment combinations — before Finalize Assignments.
Revoking is the Member assignments tab.
Role colour
Each role carries a hex colour, set with a colour picker in the role editor. It is tenant data, not a design token, so it is deliberately not restricted to the product palette; only the shape is validated, and a bad value is refused with color must be a hex colour such as #0d9488.
It draws as a dot beside the role name in the Member roles table and beside each role in the Members list, which is what makes a roster readable at a glance. A duplicate keeps its source's colour, so "Campaign Manager (Copy)" comes out blue like the original. An uncoloured role draws a neutral dot that follows the theme.
Import, export, lock, delete
Export downloads account-<id>-roles-<date>.json (or …-policies-…).
Import reports Role import completed. Created: N, Skipped: N, Invalid: N.
(or Policy import completed. …), and
runs the same anti-amplification check over the whole payload before writing
anything.
Lock role / Lock policy freezes a row: a locked row answers Locked roles cannot be modified / cannot be deleted until it is unlocked. That is a separate thing from the System badge.
Deleting a policy that is attached to a role is refused — detach it first.
Limits and gotchas
- The "New role" button does not work. It opens the editor, and saving reports Role not found: the editor mints an id the save path does not recognise as new, so it attempts an update of a role that does not exist. Duplicate the closest built-in role instead — that is the supported path, and it gives you editable policy copies as well. (New policy on the Policies tab does work.)
- Editing the seeded role definitions in code changes nothing by itself. The runtime reads the database. An account already created keeps what it was seeded with until the platform re-runs the seeding.
- "Locked" and "System" are two different things. A System role cannot be edited by a tenant at all. A Locked row is a per-row freeze anyone with the permission can toggle. The Lock role menu item is offered on System rows and silently does nothing there.
- Tabs are not hidden by permission. Every tab renders for anyone who reaches the page; a member who cannot read roles gets a load error rather than a tidy absence.
- The disclosure count moves. Not enforced by any route (16) is derived from which tokens any route actually gates on. If a release starts enforcing one, the count drops — nothing you did.