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. When you cannot set a header, pass the token in the access_token query parameter instead.
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',
workspace: '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',
workspace: '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.Authenticate with a Query Parameter
For clients that cannot set request headers — MCP over HTTP, EventSource/SSE connections, or a URL opened directly in the browser — pass the token in the access_token query parameter. It accepts any API key or access token and authenticates the request exactly like the Authorization header.
Fetch all articles with the token in the query string:
curl "https://example.monospace.io/api/blog/items/articles?access_token=YOUR_API_KEY"
const response = await fetch(
'https://example.monospace.io/api/blog/items/articles?access_token=YOUR_API_KEY',
);
const { data } = await response.json();
Authorization header, so there is no query-parameter option to configure. See Client Setup.Referer headers. Monospace redacts the access_token value from its own request logs, but cannot control intermediaries. Prefer the Authorization header, and use the query parameter only when you cannot set one.Provide the token through exactly one source. Combining the query parameter with an Authorization header or a session cookie fails, as does repeating the parameter:
| Request | Result |
|---|---|
?access_token=<token> alone | Authenticated |
?access_token= (empty value) | 401 Unauthorized — Invalid token |
?access_token=<token> with a Bearer header or session cookie | 400 Bad Request — Invalid request |
?access_token=<a>&access_token=<b> (repeated) | 400 Bad Request — Invalid request |
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();
MONOSPACE_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 |
Credential Sources
The server reads credentials from three sources:
Authorization: Bearer <token>headeraccess_tokenquery parameter- Session cookie (set by session-mode login)
Provide exactly one. A request that supplies two or more sources — for example a Bearer header alongside a session cookie — fails with 400 Bad Request. A malformed Authorization header counts as a source, so pairing it with any other credential also returns 400. A malformed header on its own returns 401 Unauthorized.
See Also
- Errors — status codes and the error response shape
- MCP — connect an MCP client, a common consumer of the
access_tokenquery parameter - SDK setup — configure the SDK for your project
- API overview — understand URL structure and response format