API Authentication
All API endpoints under /api/v1/ require a bearer token in the Authorization header.
Creating tokens
Via the UI
- Log in to the web interface
- Go to Apps > [App] > Settings > API Tokens
- Enter a name for the token and click Create
- Copy the displayed token immediately – it is shown only once
Tokens are scoped to a specific app. A token can only access the app it was created for.
Via the CLI
substrukt create-token "My token name" --app my-app
Prints the raw token to stdout. Requires at least one user to exist.
Using tokens
Include the token in the Authorization header:
curl -H "Authorization: Bearer YOUR_TOKEN" \
http://localhost:3000/api/v1/apps/my-app/schemas
Token storage
Tokens are hashed with SHA-256 before storage. The raw token is never stored – only the hash. This means:
- Lost tokens cannot be recovered; create a new one
- Token names are for your reference only
Managing tokens
Tokens are scoped to apps. Admins and editors can create tokens. Each user can:
- View tokens for apps they have access to (name and creation date)
- Delete tokens they created
Token management is available at Apps > [App] > Settings in the UI.
Role-based access
API actions are restricted by the token creator’s role:
| Role | Permissions |
|---|---|
admin | Full access including import/export, backup, and app settings |
editor | Create, update, delete content and uploads; fire deployments |
viewer | Read-only access to schemas, content, and uploads |
Rate limiting
API requests are rate-limited to 100 requests per minute per IP address. When the limit is exceeded, the API returns:
HTTP/1.1 429 Too Many Requests
{
"error": "Rate limit exceeded"
}
The rate limit is configurable via --api-rate-limit. The rate limiter uses a sliding window per IP, determined by the X-Forwarded-For header when --trust-proxy-headers is enabled, or uses a global bucket otherwise.
Error responses
| Status | Meaning |
|---|---|
401 Unauthorized | Missing or invalid bearer token |
404 Not Found | Schema or entry not found |
429 Too Many Requests | Rate limit exceeded |
400 Bad Request | Invalid request body or validation errors |
500 Internal Server Error | Server-side error |