plym Docs
    plym.io

    Roles

    plym has a built-in IAM system for different kinds of users. This page is a comprehensive guide to roles and access levels.

    Updated Aug 04, 2026

    On this page

    Every plym account holds one of three roles — reader, editor, or administrator — and the role decides which endpoints and admin screens that account can use.

    Each API route names the roles it accepts, so the set of roles is fixed in the build rather than configured. Unlike most CMS permission systems, plym has no groups, no per-post ownership rules, and no way to change an account's role once the account exists. The role is copied into the access token at sign-in and travels with every request as a bearer token.

    Choosing a role

    Grant editor to everyone who writes. Grant administrator only to the one or two people who create accounts, reset passwords, and read leads. Grant reader to an account that must sign in but must never publish.

    Role Covers
    reader Sign in, read published content, read site settings, list accounts, manage own profile.
    editor Everything a reader can do, plus posts, categories, FAQs, media, and the search index.
    administrator Everything an editor can do, plus account management and leads.

    The roles are cumulative. A route that accepts editor accepts administrator too, so an administrator never needs a second account to publish.

    Privileges by action

    Anonymous means a request with no Authorization header.

    Action Endpoint Anonymous reader editor administrator
    Sign in, renew a session, log out POST /api/auth/login, /refresh, /logout
    List published posts GET /api/posts
    Read a published post GET /api/posts/{id}
    List tags, categories, FAQs GET /api/tags, /api/categories, /api/faqs
    Submit a lead POST /api/collect
    Change own password POST /api/auth/change-password
    Read and update own profile GET, PATCH /api/users/me
    Read site settings GET /api/config
    List every account GET /api/users
    List drafts, filter by status, search all posts GET /api/posts?include_drafts=true
    Read a draft GET /api/posts/{id}
    Create, update, delete, re-render, preview posts POST, PATCH, DELETE /api/posts…
    Create, update, delete categories /api/categories
    Create, update, delete FAQs /api/faqs
    Upload, list, delete media /api/media
    Rebuild the search index POST /api/index
    Create an account POST /api/users
    Reset another account's password POST /api/users/{id}/reset-password
    Deactivate or reactivate an account /api/users/{id}/deactivate, /reactivate
    List leads GET /api/submissions

    What every signed-in account can see

    Any signed-in account, reader included, can call GET /api/users and read every account's email address, role, and active state. A reader account is a trusted account with read-only reach, not a public one. No setting narrows this.

    Authorization errors

    Status Code Cause
    401 auth.token_invalid Missing, malformed, or expired bearer token.
    403 auth.insufficient_role Valid token, but the route does not accept that role.
    403 auth.inactive_user Correct password, deactivated account.
    404 posts.not_found A draft requested without an editor token. plym hides drafts rather than reporting 403.

    Every error carries the same body shape:

    {
      "detail": {
        "code": "auth.insufficient_role",
        "message": "Insufficient role for this action"
      }
    }

    The access token

    POST /api/auth/login returns an access token and a refresh token:

    curl -X POST https://flapico.com/api/auth/login \
      -H 'Content-Type: application/json' \
      -d '{"email":"sam@flapico.com","password":"a-real-password"}'
    {
      "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
      "refresh_token": "8f3c1d9b7a2e4f6c...",
      "token_type": "bearer"
    }

    The access token is a JWT holding the account id and its role, and it expires 900 seconds after issue (PLYM_JWT_ACCESS_TTL_SECONDS). The refresh token lasts 2592000 seconds — 30 days — and is consumed each time you exchange it.

    { "sub": "3", "role": "editor", "iat": 1785368410, "exp": 1785369310, "typ": "access" }

    A request is authorized against the role claim inside the token, not against the account row. Deactivating an account therefore does not invalidate a token it already holds; see managing-users.

    Roles in the CLI and the MCP server

    The CLI reads PLYM_SUPERUSER_EMAIL and PLYM_SUPERUSER_PASSWORD from .env, so plym rebuild, plym update, plym template install, and plym set url act as that administrator. The MCP server authenticates with the email and password it was configured with, and that account's role applies unchanged — create_post on a reader account fails with This account cannot create posts (editor role required).

    Limits

    • Three roles. No custom roles, no groups, no per-post or per-category permissions.
    • One role per account.
    • A role cannot be changed after the account is created. No endpoint updates another account's role, and PATCH /api/users/me ignores a role field.
    • Published posts, tags, categories, and FAQs are readable without a token. No setting puts a published site behind sign-in.
    • An access token cannot be revoked before it expires.