Search documentation

Find pages, sections, and snippets across the Clog docs.

Permissions

The complete permission vocabulary, owner-only actions, and the per-endpoint gate matrix.

Authorization in Clog is per-workspace. A member can do something only if their Membership carries the matching permission — or if they're the workspace owner (in which case every permission is implicit).

This page is the full lookup. For the dashboard authoring walkthrough — inviting members, picking permissions — see Members and permissions.

Roles

RoleStored onMeaning
ownerMembership.roleImplicit holder of every permission below. Sole holder of owner-only actions. Exactly one per workspace.
memberMembership.roleHolds only the permissions on its permissions: string[] grant list.

The vocabulary

Ten read scopes and ten write scopes. The list is a closed enum — unknown values are rejected at the wire.

posts:read         posts:write
pages:read         pages:write
categories:read    categories:write
tags:read          tags:write
authors:read       authors:write
media:read         media:write
members:read       members:write
api-keys:read      api-keys:write
settings:read      settings:write
seo:read           seo:write
Scope:read grants:write grants
postsView posts (including drafts).Create, edit, publish, archive, soft-delete posts. Edit schemaType / structuredData (content).
pagesView pages.Create, edit, publish, soft-delete pages.
categoriesView the category tree.Create, rename, reparent, delete categories.
tagsView tags.Create, rename, delete tags.
authorsView author profiles.Create, edit, delete author personas.
mediaView the media library.Upload, edit alt/caption, delete media files.
membersView the Members and Pending invites tabs.Invite, edit permissions, remove members.
api-keysView API keys (prefix only).Create, edit, reveal, revoke API keys.
settingsView workspace settings (General, Branding, SEO defaults).Edit them.
seoView per-entity SEO meta and workspace SEO defaults.Edit them — see the cross-cutting rule.

seo:* is cross-cutting

The two SEO scopes interact with every content entity. The rule:

Field surfaceRequired permission
Per-entity SEO meta (title, description, canonicalUrl, focus keyword, robots, OG, Twitter, breadcrumbs title) on Post / Page / Category / Tag / Author<entity>:write OR seo:write
Post schemaType + structuredData (reader-facing structured content — recipes, how-tos, etc.)posts:write only — it's content, not metadata, so seo:write doesn't unlock it
Workspace SEO defaults + publisher info (read)settings:read OR seo:read
Workspace SEO defaults + publisher info (write)settings:write AND seo:write
Redirects + Link-health (read)settings:read OR seo:read
Redirects + Link-health (write)settings:write OR seo:write

Practical interpretation: seo:write is the "SEO specialist" scope. Hand it to someone who tunes titles, descriptions, robots, and social metadata across every entity without giving them content-edit rights. To let them also touch a recipe's ingredients or a how-to's steps, give them posts:write — but that crosses into content territory.

Owner-only actions

Three actions cannot be granted to a member at any permission level. They use the dedicated requireWorkspaceOwner gate, not requirePermission:

ActionWhy
Delete the workspaceDestructive cascade — removes every post, page, member, key, redirect. Owner only.
Transfer ownershipRe-assigns the single owner role to another existing member. Owner only.
Edit the owner's permissionsThe owner's permission list is always implicitly full — direct edits to it are rejected, even by the owner themselves. To remove an owner's power, transfer ownership first.

Permission flow per request

For dashboard requests (/api/v1/*):

authMiddleware                        → req.auth = { userId, sessionId }
requireWorkspaceMembership            → req.auth.workspaceId, req.auth.membership
requirePermission("posts:write")      → owners pass; members check permissions.includes(...)
requireWorkspaceOwner                 → role === "owner"
handler

For external API requests (/api/v1/external/*):

apiKeyMiddleware                      → req.auth = { userId, apiKeyId, workspaceId }
requireWorkspaceMembership            → loads the creator's current membership
requirePermission("posts:read")       → applies to the creator's current permission set
handler

API keys carry no permissions of their own. At request time the middleware resolves the creator's current Membership on the bound workspace and uses that membership's permission set for authorization.

Two consequences worth re-stating:

  1. Permission changes apply live. Removing posts:write from a member instantly removes write access from every key they created.
  2. Removing a member instantly disables all of their keys. The ApiKey rows still exist but stop authorising. For machine integrations, issue keys from a dedicated service-user account whose membership is stable.

Per-endpoint matrix — external API (/api/v1/external/*)

The full endpoint list — what's available to integrators and which scope each route needs.

Posts

MethodPathPermission
GET/external/postsposts:read
GET/external/posts/:idOrSlugposts:read
POST/external/postsposts:write
PATCH/external/posts/:idOrSlugposts:write
DELETE/external/posts/:idOrSlugposts:write
POST/external/posts/:idOrSlug/tagsposts:write
DELETE/external/posts/:idOrSlug/tagsposts:write

Pages

MethodPathPermission
GET/external/pagespages:read
GET/external/pages/:idOrSlugpages:read
POST/external/pagespages:write
PATCH/external/pages/:idOrSlugpages:write
DELETE/external/pages/:idOrSlugpages:write

Categories, tags, authors

MethodPathPermission
GET/external/categoriescategories:read
GET/external/categories/:idOrSlugcategories:read
POST/PATCH/DELETE/external/categories[/...]categories:write
GET/external/tagstags:read
GET/external/tags/:idOrSlugtags:read
POST/PATCH/DELETE/external/tags[/...]tags:write
GET/external/authorsauthors:read
GET/external/authors/:idOrSlugauthors:read
POST/PATCH/DELETE/external/authors[/...]authors:write

Media

MethodPathPermission
GET/external/mediamedia:read
GET/external/media/:idmedia:read
POST/external/media (multipart)media:write
PATCH/external/media/:idmedia:write
DELETE/external/media/:idmedia:write
MethodPathPermission
GET/external/workspacesettings:read
GET/external/redirectssettings:read OR seo:read
POST/external/link-healthnone beyond workspace membership — intentionally low-friction so consumer sites can fire-and-forget on every 404
GET/external/feeds/sitemap-dataposts:read

Full endpoint shapes live in API → Endpoint reference.

Common grant scenarios

Role you want to createScopes to grant
Content writer who only writes posts and edits imagesposts:read posts:write media:read media:write categories:read tags:read authors:read
Content writer + tag / category managerThe above plus tags:write categories:write
Content writer + their own SEOThe above plus seo:read seo:write
SEO specialist (tune meta everywhere; no content edits)posts:read pages:read categories:read tags:read authors:read seo:read seo:write
Integrator (manages API keys, doesn't write content)api-keys:read api-keys:write — plus the read scopes for whatever the integration is going to fetch
Site admin (everything except ownership)Tick every box. Owner-only actions (delete workspace, transfer ownership, edit owner's permissions) remain forbidden.
Read-only viewerEvery :read scope, no :write scopes.

On this page