Authentication
Ragz uses Argon2id-hashed passwords, short-lived JWT access tokens, and rotating refresh tokens for the web app, plus long-lived API keys for programmatic access. This page covers both.
Endpoints
| Method & path | Auth | Purpose |
|---|---|---|
GET /api/v1/auth/bootstrap-status | none | Whether a fresh install still needs its first superadmin |
POST /api/v1/auth/register | none | First-run only — the first registrant becomes superadmin, then this 409s forever after |
POST /api/v1/auth/login | none | Email + password → access token + refresh cookie |
POST /api/v1/auth/refresh | refresh cookie | Rotate the refresh token, issue a new access token |
POST /api/v1/auth/logout | refresh cookie | Revoke the current session |
POST /api/v1/auth/invitations | JWT, admin/superadmin | Invite a user by email into an org |
POST /api/v1/auth/invitations/accept | none (invite token) | Accept an invitation, set a password |
POST /api/v1/auth/forgot-password | none | Request a password-reset email (enumeration-safe: always 202) |
POST /api/v1/auth/reset-password | none (reset token) | Complete a password reset |
POST /api/v1/auth/change-password | JWT | Change your own password |
GET /api/v1/auth/sessions | JWT | List your own live refresh-token sessions |
DELETE /api/v1/auth/sessions/{family_id} | JWT | Revoke one of your own sessions |
POST /api/v1/auth/sessions/revoke-others | JWT | Revoke every session but the current one |
POST /api/v1/admin/api-keys | JWT, superadmin | Issue an API key for a user + workspace |
GET /api/v1/admin/api-keys | JWT, superadmin | List issued API keys (masked — no secret material) |
DELETE /api/v1/admin/api-keys/{key_id} | JWT, superadmin | Revoke an API key |
Session JWTs (web app)
POST /auth/login returns a 15-minute access token in the response body and
sets a rotating refresh token as an httpOnly, Secure, SameSite=Strict
cookie scoped to /api/v1/auth. Send the access token on every subsequent
request as a bearer header; when it expires, call /auth/refresh (the
cookie authenticates that call — no body needed) to get a new one and a
rotated refresh cookie.
# Use it
curl https://ragz.example.com/api/v1/workspaces \
-H "Authorization: Bearer eyJ..."Bootstrap Status
Whether a fresh install still needs its first superadmin.
GET /api/v1/auth/bootstrap-status · Auth: none
Sample response
{"needs_setup": true}Response fields
| Field | Type | Description |
|---|---|---|
needs_setup | boolean | true when no superadmin exists yet and POST /auth/register is open. |
Register
First-run only — the first registrant becomes the platform superadmin, then
this returns 409 Conflict forever after.
POST /api/v1/auth/register · Auth: none
First-run bootstrap
A brand-new install has no users. GET /auth/bootstrap-status reports
{"needs_setup": true}, and POST /auth/register is open — the very
first account created becomes the platform superadmin. Every registration
after that returns 409 Conflict; new users join via invitations from
then on.
Login
Email + password → access token + refresh cookie.
POST /api/v1/auth/login · Auth: none
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Account email. |
password | string | Yes | Account password. |
Sample request
# Log in — access token in the body, refresh token set as a cookie
curl -sX POST https://ragz.example.com/api/v1/auth/login \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{"email": "you@example.com", "password": "correct horse battery staple"}'Sample response
{"access_token": "eyJ...", "token_type": "bearer"}Response fields
| Field | Type | Description |
|---|---|---|
access_token | string | 15-minute JWT access token. |
token_type | string | Always bearer. |
Notes
- Also sets a rotating refresh token as an
httpOnly,Secure,SameSite=Strictcookie scoped to/api/v1/auth. - Send
access_tokenas a bearer header on every subsequent request.
Refresh
Rotate the refresh token, issue a new access token.
POST /api/v1/auth/refresh · Auth: refresh cookie
Sample request
# Refresh when it expires (cookie jar carries the refresh token)
curl -sX POST https://ragz.example.com/api/v1/auth/refresh -b cookies.txt -c cookies.txtResponse fields
Same {access_token, token_type} shape as Login, plus a rotated
refresh cookie.
Notes
- The refresh cookie authenticates the call — no request body needed.
Logout
Revoke the current session.
POST /api/v1/auth/logout · Auth: refresh cookie
Invitations
An admin or superadmin creates an invitation to bring a new user into an
org.
POST /api/v1/auth/invitations · Auth: JWT, admin/superadmin
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Email address to invite. |
role | string | Yes | "admin" or "user". |
org_id | string (uuid) | null | No | Optional and superadmin-only — a superadmin can invite into any org; a plain org admin can only invite into their own (the field is ignored/overridden for them server-side). |
Sample request
curl -sX POST https://ragz.example.com/api/v1/auth/invitations \
-H "Authorization: Bearer eyJ..." \
-H "Content-Type: application/json" \
-d '{ "email": "newperson@example.com", "role": "user", "org_id": null }'Response fields
| Field | Type | Description |
|---|---|---|
invite_token | string | One-time token the invitee redeems via Accept Invitation to set their password and activate the account. |
Accept Invitation
Accept an invitation and set a password.
POST /api/v1/auth/invitations/accept · Auth: none (invite token)
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
token | string | Yes | The invite_token from the invitation. |
password | string | Yes | Password to set for the new account. |
Sample request
curl -sX POST https://ragz.example.com/api/v1/auth/invitations/accept \
-H "Content-Type: application/json" \
-d '{ "token": ..., "password": ... }'Notes
- Sets the account's password and activates it.
Forgot Password
Request a password-reset email. Enumeration-safe: always returns 202,
whether or not the email matches an active account.
POST /api/v1/auth/forgot-password · Auth: none
Reset Password
Complete a password reset.
POST /api/v1/auth/reset-password · Auth: none (reset token)
Change Password
Change your own password.
POST /api/v1/auth/change-password · Auth: JWT
List Sessions
List your own live refresh-token sessions.
GET /api/v1/auth/sessions · Auth: JWT
Revoke Session
Revoke one of your own sessions.
DELETE /api/v1/auth/sessions/{family_id} · Auth: JWT
Revoke Other Sessions
Revoke every session but the current one.
POST /api/v1/auth/sessions/revoke-others · Auth: JWT
API Keys (Programmatic Access)
For scripts, backend services, or the OpenAI-compatible endpoint, a superadmin issues an API key scoped to exactly one user and one workspace.
Create API Key
Issue an API key for a user + workspace.
POST /api/v1/admin/api-keys · Auth: JWT, superadmin
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Human-readable label for the key. |
user_id | string (uuid) | Yes | The user the key acts as. |
workspace_id | string (uuid) | Yes | The workspace the key is scoped to. |
expires_at | string (datetime) | No | Optional expiry. Keys default to expiring after a bounded lifetime if omitted — there are no perpetual keys. |
Sample request
curl -sX POST https://ragz.example.com/api/v1/admin/api-keys \
-H "Authorization: Bearer $SUPERADMIN_JWT" \
-H "Content-Type: application/json" \
-d '{
"name": "reporting-bot",
"user_id": "5b6c...-uuid",
"workspace_id": "a1b2...-uuid"
}'Notes
- The response includes
api_key— the raw key, shown exactly once (the server stores only its hash). - Every other read of the key (
GET /admin/api-keys) returns the maskedApiKeyOutshape: id, name,prefix, org/user/workspace ids, expiry, timestamps — never the secret itself. - Revoke early with
DELETE /admin/api-keys/{key_id}.
List API Keys
List issued API keys (masked — no secret material).
GET /api/v1/admin/api-keys · Auth: JWT, superadmin
Notes
- Returns the masked
ApiKeyOutshape: id, name,prefix, org/user/workspace ids, expiry, timestamps — never the secret itself.
Revoke API Key
Revoke an API key.
DELETE /api/v1/admin/api-keys/{key_id} · Auth: JWT, superadmin
Keys are write-only after creation
The raw key value is never retrievable again after the create response. If it's lost, revoke it and issue a new one — there is no "reveal" endpoint.
Using an API Key
Send the key as a bearer token (or X-API-Key) against the /external/v1
surface — not /api/v1:
curl -sX POST https://ragz.example.com/external/v1/chat \
-H "Authorization: Bearer $RAGZ_API_KEY" \
-H "Content-Type: application/json" \
-d '{"question": "What is our refund policy?"}'Notes
- Every API-key request revalidates that the backing user still has a live membership in the key's workspace and still holds the permission to chat — so disabling a user or removing their workspace membership immediately invalidates any key issued for them, without a separate revocation step.