Authentication
Authentication Methods
Monospace supports three authentication methods:
| Method | Use Case | Transport |
|---|---|---|
| API Key | Server-to-server, scripts, CI/CD | Authorization: Bearer <jwt> header |
| Email/Password Login | User sessions, browser apps | Access token (header or cookie) + refresh token |
| Access Token | Obtained from login, short-lived | Authorization: Bearer <jwt> header |
All tokens are JWTs signed with EdDSA (Ed25519). API keys and access tokens are sent the same way — as a Bearer token in the Authorization header.
Get an API Key
In the Studio, go to Account Settings > Access to create and manage your API keys. Each key is a JWT scoped to a specific user and inherits their permissions.
Manage API Keys
| Endpoint | Method | Description |
|---|---|---|
/api/system/api-keys | POST | Create a new API key |
/api/system/api-keys | GET | List all API keys |
/api/system/api-keys/{key_id} | PATCH | Update a key (name, description, active) |
/api/system/api-keys/{key_id} | DELETE | Delete a key |
API keys can be deactivated without deletion by setting active: false.
Service Accounts
For machine-to-machine access, create a service account and attach API keys to it.
| Endpoint | Method | Description |
|---|---|---|
/api/system/service-accounts | POST | Create a service account |
/api/system/service-accounts | GET | List service accounts |
/api/system/service-accounts/{id} | GET | Read a service account |
/api/system/service-accounts/{id} | PATCH | Update a service account |
/api/system/service-accounts/{id} | DELETE | Delete a service account |
Service accounts are user records with isService: true, owned by a real user via the serviceAccountOwner relation. As an organization admin, you can create service accounts in Organization Settings > Service Accounts.
Authenticate REST Requests
Pass the API key in the Authorization header using the Bearer scheme.
Fetch all articles with an authenticated request:
import { createClient } from './generated/monospace';
const client = createClient({
url: 'https://example.monospace.io',
project: 'blog',
apiKey: 'YOUR_API_KEY',
});
const articles = await client.Articles.readMany();
curl https://example.monospace.io/api/blog/items/articles \
-H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch('https://example.monospace.io/api/blog/items/articles', {
headers: {
Authorization: 'Bearer YOUR_API_KEY',
},
});
const { data } = await response.json();
Create a new article with a POST request:
import { createClient } from './generated/monospace';
const client = createClient({
url: 'https://example.monospace.io',
project: 'blog',
apiKey: 'YOUR_API_KEY',
});
const article = await client.Articles.createOne({
data: {
title: 'Auth Guide',
author: 'alice',
status: 'draft',
},
fields: ['id', 'title', 'status'],
});
curl -X POST https://example.monospace.io/api/blog/items/articles \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '[{"title": "Auth Guide", "author": "alice", "status": "draft"}]'
const response = await fetch('https://example.monospace.io/api/blog/items/articles', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify([
{
title: 'Auth Guide',
author: 'alice',
status: 'draft',
},
]),
});
const { data } = await response.json();
Authorization header automatically when configured with an apiKey. See Client Setup for configuration.Email/Password Login
For user-facing applications, authenticate with email and password. The login endpoint supports two modes.
JSON Mode
Returns access and refresh tokens in the response body:
curl -X POST https://example.monospace.io/api/auth/login \
-H "Content-Type: application/json" \
-d '{"credentials": {"email": "user@example.com", "password": "secret"}, "mode": "json"}'
const response = await fetch('https://example.monospace.io/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
credentials: { email: 'user@example.com', password: 'secret' },
mode: 'json',
}),
});
const { accessToken, refreshToken, expires } = await response.json();
Response:
{
"expires": 900,
"accessToken": "eyJ...",
"refreshToken": "eyJ..."
}
Use the accessToken as a Bearer token for subsequent requests, just like an API key.
Session Mode (default)
Sets httpOnly cookies for the access and refresh tokens. This is the default when mode is omitted:
curl -X POST https://example.monospace.io/api/auth/login \
-H "Content-Type: application/json" \
-d '{"credentials": {"email": "user@example.com", "password": "secret"}}' \
-c cookies.txt
await fetch('https://example.monospace.io/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
credentials: { email: 'user@example.com', password: 'secret' },
}),
});
// Access token cookie is set automatically at path /api
// Refresh token cookie is set at path /api/auth
In session mode, ensure your HTTP client sends cookies with each request (e.g. credentials: 'include' in fetch).
Refresh and Logout
Refresh an Access Token
curl -X POST https://example.monospace.io/api/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refreshToken": "eyJ..."}'
const response = await fetch('https://example.monospace.io/api/auth/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken: 'eyJ...' }),
});
const { accessToken, refreshToken, expires } = await response.json();
DIRECTUS_REFRESH_TOKEN_TTL). Once expired, you must re-authenticate. For production applications:- Store only the latest refresh token — each refresh call may return a new one.
- Handle refresh failures (expired or revoked tokens) by prompting re-login.
- Prefer session mode for browser apps — the server manages token lifecycle via
httpOnlycookies automatically.
Log Out
Invalidates the refresh token:
curl -X POST https://example.monospace.io/api/auth/logout \
-H "Content-Type: application/json" \
-d '{"refreshToken": "eyJ..."}'
await fetch('https://example.monospace.io/api/auth/logout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken: 'eyJ...' }),
});
Auth Endpoints Reference
| Endpoint | Method | Description |
|---|---|---|
/api/auth/login | POST | Email/password login |
/api/auth/refresh | POST | Refresh access token |
/api/auth/logout | POST | Invalidate refresh token |
/api/auth/password-change | POST | Change password (requires auth) |
/api/auth/password-reset/request | POST | Request password reset email |
/api/auth/password-reset/verify | GET | Verify reset token |
/api/auth/password-reset/confirm | POST | Confirm password reset |
Token Extraction Priority
The server checks for authentication in this order:
Authorization: Bearer <token>header- Session cookie (fallback if header is missing)
If the header is present but malformed, the request fails — it does not fall through to the cookie.
Next steps:
- SDK setup — configure the SDK for your project
- API overview — understand URL structure and response format