Managing users
Add accounts, reset passwords, and deactivate accounts — from the admin portal or four administrator-only endpoints.
Updated Aug 04, 2026
On this page
Before you start
Sign in at to the admin portal with an administrator account and open Users.
Editors and readers can open this screen and read the list; the New user button and the row actions appear only for administrators.
In the API examples, $PLYM_TOKEN is the access_token from POST /api/auth/login.
The first account
The API creates it at startup from PLYM_SUPERUSER_EMAIL and PLYM_SUPERUSER_PASSWORD in .env, with the administrator role and the display name Administrator. The installer generates that password; read it back with grep PLYM_SUPERUSER_PASSWORD .env. Startup creates the account only when no account with that email exists — later restarts never reset its password or reactivate it.
plym rebuild, plym update, plym template install, and plym set url sign in with those exact credentials to re-render posts. If you change that account's password in the portal or deactivate it, update .env to match, or those four commands fail at the re-render step.
Add a user
- Open Users and click New user.
- Fill in Display name, Email, and Temporary password — 8 characters minimum.
- Pick a Role. The form defaults to Editor; Administrator and Reader are the other two options.
- Click Add user. The account appears in the list and can sign in at once.
curl -X POST http://localhost:9173/api/users \
-H "Authorization: Bearer $PLYM_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"email": "sam@acme.com",
"password": "temp-pass-8-plus",
"display_name": "Sam Rivera",
"role": "editor"
}'
{
"id": 3,
"email": "sam@acme.com",
"role": "editor",
"is_active": true,
"display_name": "Sam Rivera",
"bio": null,
"avatar_url": null,
"links": [],
"created_at": "2026-07-29T23:40:21.587336Z",
"updated_at": "2026-07-29T23:40:21.587336Z"
}
The password you type is the password they use. plym does not force a change at first sign-in, so send it over a channel you trust and ask them to change it.
| Status | Code | Cause |
|---|---|---|
| 201 | — | Account created. The response is the full user object. |
| 409 | users.email_exists |
The email already belongs to an account, active or deactivated. |
| 422 | — | Password under 8 characters, empty display name, or malformed email. |
| 403 | auth.insufficient_role |
The calling token is not an administrator. |
Change your own password
The admin portal has no screen for this. Any signed-in account changes its own password with one call, and the call needs the current password:
curl -X POST http://localhost:9173/api/auth/change-password \
-H "Authorization: Bearer $PLYM_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"old_password":"temp-pass-8-plus","new_password":"a-longer-password"}'
{ "ok": true }
A wrong old_password returns 401 auth.invalid_credentials. A successful change deletes every refresh token for the account, so other sessions stop renewing.
Reset someone else's password
- Open Users and hover the person's row.
- Click the Reset password action and confirm.
- plym generates a 12-character password, saves it, and copies it to your clipboard. Paste it somewhere before you copy anything else — it appears nowhere else.
The API takes a password you choose instead of generating one, 8 characters minimum:
curl -X POST http://localhost:9173/api/users/3/reset-password \
-H "Authorization: Bearer $PLYM_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"new_password":"a-new-password"}'
{ "ok": true }
A reset replaces the password and deletes every refresh token for that account. The person's current access token keeps working until it expires, up to 900 seconds. Reset also works on a deactivated account, and does not reactivate it.
Deactivate a user
- Open Users and hover the person's row.
- Click Deactivate and confirm.
- The row moves to the Deactivated tab, which shows a count beside its label.
curl -X DELETE http://localhost:9173/api/users/3/deactivate \
-H "Authorization: Bearer $PLYM_TOKEN"
204 No Content
| Effect | Detail |
|---|---|
| Sign-in blocked | POST /api/auth/login returns 403 auth.inactive_user, even with the right password. |
| Sessions cut | Every refresh token is deleted, so POST /api/auth/refresh returns 401 auth.token_invalid. |
| Last access token still valid | It keeps full privileges until it expires — up to 900 seconds — and can still create or delete posts. |
| Record kept | The account stays in GET /api/users with is_active: false, under Deactivated. |
| Content untouched | Nothing is unpublished, re-rendered, or reassigned. |
An access token cannot be revoked before it expires. If you need someone out immediately, deactivate the account and treat the next 900 seconds (PLYM_JWT_ACCESS_TTL_SECONDS) as a window in which their last token still works.
| Status | Code | Cause |
|---|---|---|
| 204 | — | Deactivated. Repeating the call returns 204 again. |
| 403 | users.cannot_delete_self |
An administrator cannot deactivate their own account. |
| 403 | auth.insufficient_role |
The calling token is not an administrator. |
| 404 | users.not_found |
No account with that id. |
What happens to their posts
Deactivation changes nothing about content.
- Published posts stay published, at the same URLs, with no re-render.
- The byline keeps their display name.
authorin the API response is unchanged, and the rendered page keeps whatever byline the template shows. - Drafts stay drafts and stay visible to every editor.
- Any editor or administrator can edit, publish, or delete those posts. The byline still names the deactivated author afterwards.
- Authorship cannot be moved.
PATCH /api/posts/{id}has no author field. - Their profile — display name, bio, avatar, links — is frozen.
PATCH /api/users/meis the only endpoint that writes a profile, and they can no longer sign in to call it.
This is also why deletion does not exist: a post row references its author with ON DELETE RESTRICT, so removing an account would mean deleting or reassigning everything the person wrote.
Reactivate a user
- Open Users and switch to the Deactivated tab.
- Hover the row and click the reactivate action.
curl -X POST http://localhost:9173/api/users/3/reactivate \
-H "Authorization: Bearer $PLYM_TOKEN"
The response is the user object with is_active: true. The account returns with the same id, role, and password it had before. Reactivating an already active account returns 200 and changes nothing.