Roles and permissions
Who may write, who may publish, and who may change the settings of a brand.
3 min read
Three roles per brand
Every member of a brand has exactly one role in that brand. The same person can be an editor in one brand and an admin in another. The API checks the role before it looks at the request body, so a call you may not make fails with 403 whatever you send.
| Role | Can |
|---|---|
| Editor | Write drafts, request a review, upload media, create terms, and move pages that are not live. |
| Publisher | Everything an editor can, plus publish, schedule, roll back and delete entries, change or move live pages, manage redirects, menus, fragments and strings, delete media, retry webhook deliveries, and read and manage leads. |
| Admin | Everything a publisher can, plus settings, theme, modules, block definitions, webhooks, members, API tokens, integrations, secrets, custom scripts, imports and the audit log. |
A brand always keeps at least one active admin. You cannot remove or suspend the last one.
Platform admin is different
A platform admin manages brands of the whole installation (create, rename, suspend). It is not a brand role, it is set only on the server command line, and it gives no access to the content of a brand. To read or edit a brand, a platform admin still needs a membership there.
Minimum role per content type
An admin can give a custom content type a min_role of editor, publisher or admin. Without one, every member can use the type.
A member who ranks below min_role gets 403 on that type's entries: list filters, show, create, save a draft, publish, revisions, AI actions, reviews and exports. Unfiltered lists simply leave those entries out. Public delivery is not affected.
Review workflow
An editor cannot publish, so they ask for a review. A review pins one revision, so approving it publishes exactly what was reviewed.
| Step | Call | Who |
|---|---|---|
| Request | POST /v1/manage/entries/{entry}/review with an optional note | Any member |
| List | GET /v1/manage/reviews | Publishers and admins see all, an editor sees their own |
| Approve | POST /v1/manage/reviews/{review}/approve, with publish: true to go live | Publisher, admin |
| Send back | POST /v1/manage/reviews/{review}/changes with a required note | Publisher, admin |
| Cancel | POST /v1/manage/reviews/{review}/cancel | The requester, publisher, admin |
| Comment | POST /v1/manage/reviews/{review}/comments with a body | The requester, publisher, admin |
If the page changed after the review was requested, approving with publish: true answers 409 revision_changed and nothing goes live. A new request for a newer version cancels the older open one. Every step is audited and sends a realtime update.
Members and invitations
Only admins manage members. All calls need the X-Brand-Id header.
| Call | What it does |
|---|---|
GET /v1/manage/members | Lists members with role and status (active or suspended). |
POST /v1/manage/members | Body {email, role}. Adds an existing account (201) or, for an unknown email, creates an invitation and answers 202. |
PATCH /v1/manage/members/{user} | Changes a member's role. |
POST /v1/manage/members/{user}/suspend | Blocks the member in this brand. Other brands are unaffected. |
POST /v1/manage/members/{user}/unsuspend | Restores the member. |
DELETE /v1/manage/members/{user} | Removes the member from the brand. |
GET /v1/manage/invitations | Lists pending invitations. |
POST /v1/manage/invitations/{id}/resend | Sends a fresh link and extends the invitation to 7 days. |
DELETE /v1/manage/invitations/{id} | Revokes an invitation. |
{ "email": "[email protected]", "role": "editor" }The invitation link is sent only by email and is never part of an API answer, so whoever holds the link must own the inbox. If the brand has no working mail connection, the answer says email_sent: false: fix the mail settings and press Resend. A suspended member keeps the membership and role but gets 403 on every call of that brand.
Token kinds and who creates them
POST /v1/manage/api-tokens creates a token. Admins can create every kind. The one exception for other members is a short preview token.
| Kind | Who may create it | Expiry |
|---|---|---|
read | Admin | Optional, 1 to 43200 minutes. Without one it never expires. |
preview | Admin, or any member | Required. Up to 7 days for an admin, 1 to 120 minutes for a member. |
agent | Admin, when the brand setting agent.enabled is on | 15 minutes to 90 days, 30 days by default. |
developer | Admin | 15 minutes to 90 days, 30 days by default. |
A member who asks for any other kind, or a longer preview, gets 403. Only admins can list and revoke tokens. See tokens and security for how to send them and MCP and agent access for agent tokens, which write drafts only and always act with the editor role.
Other brands and the audit log
- A record of another brand is a 404, not a 403. Every query is limited to the active brand, so a foreign id looks exactly like one that does not exist, and ids do not leak.
- A suspended brand answers 403 on public and dashboard calls alike.
- Authorization runs first. The role and content type checks happen before validation, so a forbidden call never reveals whether the body was valid.
- Every live change is audited. Publishing, rollbacks, deletes, setting changes, member and token changes write an audit row with ids, never content bodies or secrets.
GET /v1/manage/audit-logs lists the audit rows to admins, newest first, 50 a page. Add ?action=member.suspended to filter by one action.
For the full list of rules, see tokens and security.
Was this page helpful?