Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/rommapp/romm/llms.txt

Use this file to discover all available pages before exploring further.

The Users API provides control over user accounts in RomM. Most endpoints require admin-level access — creating users, changing roles, and generating invite links are restricted to users with the users.write scope. Every user can read and update their own profile via the /api/users/me and /api/users/{id} endpoints.

Roles

RomM uses a two-tier role model:
RoleDescription
adminFull access. Bypasses all permission group checks. Can create, edit, and delete any user.
userAccess is governed by their assigned permission group. Can only edit their own profile.
The legacy viewer and editor roles from older RomM versions are automatically coerced to user at login. There is no longer a separate editor tier — granular permissions are managed through permission groups.

GET /api/users

Retrieve all user accounts. Admin-only. Required scope: users.read
curl http://localhost:8080/api/users \
  -H "Authorization: Bearer <token>"
Response: Array of UserSchema objects.
id
integer
Internal user ID.
username
string
Username (lowercase).
email
string | null
Email address (lowercase).
enabled
boolean
Whether the account is active.
role
string
Role value: admin or user.
permission_group_id
integer | null
ID of the assigned permission group. null uses the server default.
oauth_scopes
string[]
Computed list of OAuth2 scopes the user can grant.
avatar_path
string
Path to the user’s avatar image.
last_login
string | null
ISO 8601 last login timestamp (UTC).
last_active
string | null
ISO 8601 last activity timestamp (UTC).
ra_username
string | null
Linked RetroAchievements username.
ra_progression
object | null
RetroAchievements progression data.
ui_settings
object | null
User UI preference settings.
created_at
string
ISO 8601 creation timestamp.
updated_at
string
ISO 8601 last-updated timestamp.

GET /api/users/identifiers

Retrieve only the IDs of all user accounts. Required scope: users.read
curl http://localhost:8080/api/users/identifiers \
  -H "Authorization: Bearer <token>"
Response: [1, 2, 3, ...]

GET /api/users/me

Retrieve the currently authenticated user’s profile. Also returns the current device_id if a device session is active. Required scope: me.read
curl http://localhost:8080/api/users/me \
  -H "Authorization: Bearer <token>"
Response: UserSchema for the current user, with current_device_id populated.

GET /api/users/

Retrieve a single user by internal ID. Required scope: users.read
id
integer
required
User internal ID.
curl http://localhost:8080/api/users/2 \
  -H "Authorization: Bearer <token>"
Response: UserSchema or 404 if not found.

GET /api/users//avatar

Serve a user’s avatar image. Any authenticated user can fetch any other user’s avatar (avatars are considered public within an authenticated session). Required scope: assets.read
id
integer
required
User internal ID.
curl http://localhost:8080/api/users/2/avatar \
  -H "Authorization: Bearer <token>" \
  -o avatar.jpg

POST /api/users

Create a new user account. Requires admin privileges (the users.write scope) when admin users already exist. During initial setup (no admins exist yet) this endpoint is open. Required scope: users.write (once any admin exists)
username
string
required
Username for the new account (must be unique).
email
string
required
Email address (must be unique).
password
string
required
Initial password.
role
string
required
Role for the new user: admin or user. Only an existing admin can create another admin.
curl -X POST http://localhost:8080/api/users \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"username": "alice", "email": "alice@example.com", "password": "s3cr3t", "role": "user"}'
Response: 201 Created — the new UserSchema.

PUT /api/users/

Update a user account. Accepts multipart/form-data so that a new avatar can be uploaded in the same request. Admins can update any user. Non-admin users can only update themselves. Required scope: me.write (self) or users.write (admin updating another user)
id
integer
required
User internal ID.
curl -X PUT http://localhost:8080/api/users/2 \
  -H "Authorization: Bearer <token>" \
  -F "ra_username=myRAuser" \
  -F "avatar=@/path/to/avatar.png"
If you change your own username or password, your current session will be cleared and you will be logged out.

DELETE /api/users/

Delete a user account. Removes the user’s avatar directory from disk. Required scope: users.write
id
integer
required
User internal ID.
curl -X DELETE http://localhost:8080/api/users/2 \
  -H "Authorization: Bearer <token>"
Response: 204 No Content.
GuardError
Deleting yourself400 You cannot delete yourself
Deleting the last admin400 You cannot delete the last admin user

POST /api/users/invite-link

Generate an invite link token that allows someone to self-register with a pre-assigned role. Only admins can generate admin invite links. Required scope: users.write
role
string
required
Role for the invited user: admin or user.
expiration
integer
Token lifetime in seconds. Defaults to INVITE_TOKEN_EXPIRY_SECONDS. Must be a positive integer.
curl -X POST "http://localhost:8080/api/users/invite-link?role=user&expiration=86400" \
  -H "Authorization: Bearer <token>"
Response:
{ "token": "<invite_jwt>" }
Share the token with the invitee. They register via POST /api/users/register.

POST /api/users/register

Self-register using an invite link token. No authentication required.
username
string
required
New username.
email
string
required
Email address.
password
string
required
Password.
token
string
required
Invite token received from an admin.
curl -X POST http://localhost:8080/api/users/register \
  -H "Content-Type: application/json" \
  -d '{"username": "bob", "email": "bob@example.com", "password": "hunter2", "token": "<invite_jwt>"}'
Response: 201 Created — the new UserSchema.

POST /api/users//ra/refresh

Refresh RetroAchievements progression data for a user. Requires the user to have ra_username set. Required scope: me.write
id
integer
required
User internal ID.
incremental
boolean
default:"false"
When true, only retrieves new progression since the last refresh. When false, fetches the full progression from scratch.
curl -X POST http://localhost:8080/api/users/1/ra/refresh \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"incremental": true}'
Response: 200 OK (no body).

Build docs developers (and LLMs) love