REST API Overview
Sovrium exposes a REST API over tables, records, views, activity, analytics and authentication. Every endpoint accepts and returns JSON, enforces RBAC with field-level permissions, and emits a single canonical error envelope.
The full path list lives in the Endpoint Reference; this page is the contract that governs all of it.
Early Preview. The API surface is evolving. Endpoints may change before v1.0.
Base URL
All endpoints are relative to your Sovrium instance base URL.
http://localhost:3000/apiAuthentication & access control
Authentication is session-based (Better Auth). Requests touching protected resources must carry one of exactly two credential forms:
| Credential | How it travels | When it applies |
|---|---|---|
| Session cookie | better-auth.session_token |
Always, once auth is configured. The browser default. |
x-api-key: <key> |
An x-api-key request header |
When auth.apiKeys is on. See API Keys. |
Authorization: Bearer is not one of them. A Bearer token — including a valid API key sent that way — resolves no session and returns 401. The header is deliberately not a credential path on /api/*, so a long-lived key travels on exactly one audited route rather than two. If a request that should work returns 401, check the header name first.
Access is governed by roles plus field-level permissions. Table permissions accept all, authenticated, or an arbitrary list of role names — so admin, member and viewer are the built-in defaults, not a fixed set. An app with no auth block evaluates requests under a guest role.
Per the anti-enumeration policy, an authenticated request that lacks access to a resource returns 404, never 403, so the caller cannot distinguish "not found" from "no access".
Do not expect a 403 on the records surface. Even "the caller can read this record but may not modify it" answers 404, deliberately, so the write boundary is not discoverable by probing. The few genuine 403s live on CSV export and the form bulk handlers.
Error response contract
All 4xx/5xx responses use one canonical JSON envelope, so a single decoder covers every error.
{
"success": false,
"message": "Authentication required",
"code": "UNAUTHORIZED"
}code is drawn from a stable enum of thirteen values: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_ERROR, BAD_REQUEST, CONFLICT, PAYLOAD_TOO_LARGE, RATE_LIMITED, INTERNAL_ERROR, SERVICE_UNAVAILABLE, STORAGE_ERROR, DATABASE_ERROR, QUOTA_EXCEEDED. An optional error (legacy type identifier) and details (an array of strings) may also be present.
When the failure is field-level, an errors[] array names each offending field:
{
"success": false,
"message": "One or more fields failed validation",
"code": "VALIDATION_ERROR",
"errors": [{ "field": "id", "message": "Cannot write to readonly field 'id'" }]
}A record create refused by the database populates the same array. When a CHECK, foreign-key, or NOT NULL constraint rejects a value on POST /api/tables/:table/records, the response names the column the constraint was declared on:
{
"success": false,
"message": "A submitted value is not allowed by this resource",
"code": "VALIDATION_ERROR",
"field": "status",
"errors": [{ "field": "status", "message": "A submitted value is not allowed by this resource" }]
}Only a field you actually sent is ever named, and the response never lists the values a field accepts. The column is recovered from the constraint that fired and is reported only when it matches a key in your payload, so an error response can never be used to discover a schema you cannot otherwise read. When no submitted key matches, field and errors[] are omitted and the class-level message answers on its own.
Attribution is limited to that single-record create path. A PATCH, or a batch write refused by the same constraint, answers the identical status and code with the class-level message and no field.
| Status | code |
Meaning |
|---|---|---|
400 |
VALIDATION_ERROR |
Field-level validation failure (carries errors[]) |
400 |
BAD_REQUEST |
Malformed request |
401 |
UNAUTHORIZED |
No / expired session |
403 |
FORBIDDEN |
Authenticated, action denied (rare — see above) |
404 |
NOT_FOUND |
Not found, or anti-enumeration access denial |
409 |
CONFLICT |
Unique collision (single and batch) or stale optimistic write |
413 |
PAYLOAD_TOO_LARGE |
Batch payload exceeds the hard limit |
429 |
RATE_LIMITED |
Rate limit exceeded |
500 |
INTERNAL_ERROR |
Unexpected server error (details redacted) |
Batch size failures split across two codes: exceeding the per-operation maximum (1000 create, 100 update, 100 delete ids, 100 upsert) is a 400 VALIDATION_ERROR, while a payload over the hard 1000-id guard is 413.
How a request is framed never changes its code. A unique collision answers 409 whether it arrives as a single write or inside a batch — one handler covers both. The other constraint classes (check, foreign key, NOT NULL) answer 400 VALIDATION_ERROR on every path, since those reject the value that was sent rather than colliding with a row that already exists.
Configuration is code-only
There is no runtime schema-editing API. Sovrium is a configuration-as-code interpreter: you change an app by editing its app.ts / app.yaml and re-deploying — the same file the CLI validates offline with sovrium validate. The admin dashboard reflects runtime data, never configuration (Admin Dashboard); schema changes apply on the next boot (Migrations).
Health
Moved to Endpoint Reference.
Tables
Moved to Endpoint Reference.
Records
Moved to Endpoint Reference.
Views
Moved to Endpoint Reference.
Activity
Moved to Endpoint Reference.
Analytics
Moved to Endpoint Reference.
Authentication endpoints
Moved to Endpoint Reference.
Cross-cutting features
- Pagination — list endpoints page their results (
limit+offset). - Soft deletes —
DELETEtrashes by default;?permanent=trueand?purge=truehard-delete on the same route. - RBAC — role-gated on every protected route, with
guestfor auth-less apps. - Field-level permissions — granular read/write control per field per role.
- Rate limiting — auth and admin endpoints are rate-limited.
- Canonical error envelope — every 4xx/5xx shares the shape above.
Related Pages
- Endpoint Reference — every path, method and description.
- OpenAPI — the machine-readable document and
/api/scalar. - Records Overview — the data model behind the records routes.
- API Keys — the
x-api-keycredential and how a user mints one. - Table Permissions — RBAC and field-level access.
- Security Hardening — deploying this surface safely.
Last updated August 28, 2026
This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.