
# API Reference

Sovrium exposes a complete REST API for managing tables, records, views, activity logs, analytics, authentication, and admin schema management. All endpoints accept and return JSON, enforce RBAC with field-level permissions, and emit a single canonical error envelope.

:::callout
**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/api
```

## Authentication & access control

The API uses session-based authentication (Better Auth). Requests that touch protected resources must carry a valid session cookie or `Authorization: Bearer <token>` header. Access is governed by **RBAC** (admin, member, viewer) with **field-level permissions** layered on top.

Per the anti-enumeration policy, an authenticated request that lacks read access to a resource returns **404** (not 403), so the caller cannot distinguish "not found" from "no access". A genuine **403** is reserved for surfaces where the resource is known to exist but the action is denied (e.g. a non-admin hitting `GET /api/admin/schema/versions`).

## Error response contract

All 4xx/5xx responses use a single canonical JSON envelope, so one decoder covers every error.

**Generic 4xx/5xx envelope:**

```json
{
  "success": false,
  "message": "Authentication required",
  "code": "UNAUTHORIZED"
}
```

The `code` is drawn from a stable enum: `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `VALIDATION_ERROR`, `CONFLICT`, `RATE_LIMITED`, `INTERNAL_ERROR`, `SERVICE_UNAVAILABLE`. Optional `error` (legacy type identifier) and `details` may also be present.

**Validation 400 envelope** — when the failure is field-level, an `errors[]` array names each offending field:

```json
{
  "success": false,
  "message": "One or more fields failed validation",
  "code": "VALIDATION_ERROR",
  "errors": [{ "field": "id", "message": "Cannot write to readonly field 'id'" }]
}
```

| Status | `code`              | Meaning                                             |
| ------ | ------------------- | --------------------------------------------------- |
| `400`  | `VALIDATION_ERROR`  | Field-level validation failure (carries `errors[]`) |
| `401`  | `UNAUTHORIZED`      | No / expired session                                |
| `403`  | `FORBIDDEN`         | Authenticated, action denied (resource known)       |
| `404`  | `NOT_FOUND`         | Not found, or anti-enumeration access denial        |
| `413`  | `PAYLOAD_TOO_LARGE` | Batch payload exceeds the limit                     |
| `429`  | `RATE_LIMITED`      | Rate limit exceeded                                 |
| `500`  | `INTERNAL_ERROR`    | Unexpected server error (details redacted)          |

## Health

Server health check endpoint (unauthenticated).

| Method | Path          | Description         |
| ------ | ------------- | ------------------- |
| `GET`  | `/api/health` | Check server status |

## Tables

Read table definitions, including field schemas and permission rules.

| Method | Path                                | Description           |
| ------ | ----------------------------------- | --------------------- |
| `GET`  | `/api/tables`                       | List all tables       |
| `GET`  | `/api/tables/{tableId}`             | Get table by ID       |
| `GET`  | `/api/tables/{tableId}/permissions` | Get table permissions |

## Records

Full CRUD, batch operations, soft-delete lifecycle, revision history, and record comments. See [Records Overview](/en/docs/records-overview) for the data model.

### CRUD

| Method   | Path                                       | Description          |
| -------- | ------------------------------------------ | -------------------- |
| `GET`    | `/api/tables/{tableId}/records`            | List records         |
| `POST`   | `/api/tables/{tableId}/records`            | Create a record      |
| `GET`    | `/api/tables/{tableId}/records/{recordId}` | Get record by ID     |
| `PATCH`  | `/api/tables/{tableId}/records/{recordId}` | Update a record      |
| `DELETE` | `/api/tables/{tableId}/records/{recordId}` | Soft-delete a record |

### Batch operations

| Method   | Path                                   | Description                  |
| -------- | -------------------------------------- | ---------------------------- |
| `POST`   | `/api/tables/{tableId}/records/batch`  | Create multiple records      |
| `PATCH`  | `/api/tables/{tableId}/records/batch`  | Update multiple records      |
| `DELETE` | `/api/tables/{tableId}/records/batch`  | Soft-delete multiple records |
| `POST`   | `/api/tables/{tableId}/records/upsert` | Create or update a record    |

### Trash & history

| Method | Path                                               | Description                 |
| ------ | -------------------------------------------------- | --------------------------- |
| `GET`  | `/api/tables/{tableId}/trash`                      | List trashed records        |
| `POST` | `/api/tables/{tableId}/records/{recordId}/restore` | Restore a deleted record    |
| `POST` | `/api/tables/{tableId}/records/batch/restore`      | Restore multiple records    |
| `GET`  | `/api/tables/{tableId}/records/{recordId}/history` | Get record revision history |

### Comments

| Method   | Path                                                | Description               |
| -------- | --------------------------------------------------- | ------------------------- |
| `GET`    | `/api/tables/{tableId}/records/{recordId}/comments` | List comments on a record |
| `POST`   | `/api/tables/{tableId}/records/{recordId}/comments` | Add a comment             |
| `GET`    | `.../{recordId}/comments/{commentId}`               | Get comment by ID         |
| `PATCH`  | `.../{recordId}/comments/{commentId}`               | Update a comment          |
| `DELETE` | `.../{recordId}/comments/{commentId}`               | Delete a comment          |

## Views

Pre-configured views that filter, sort, and group records from a table.

| Method | Path                                           | Description                |
| ------ | ---------------------------------------------- | -------------------------- |
| `GET`  | `/api/tables/{tableId}/views`                  | List views for a table     |
| `GET`  | `/api/tables/{tableId}/views/{viewId}`         | Get view by ID             |
| `GET`  | `/api/tables/{tableId}/views/{viewId}/records` | Get records through a view |

## Activity

Audit log of data changes across all tables.

| Method | Path                         | Description           |
| ------ | ---------------------------- | --------------------- |
| `GET`  | `/api/activity`              | List activity entries |
| `GET`  | `/api/activity/{activityId}` | Get activity detail   |

## Analytics

Privacy-friendly, cookie-free usage analytics.

| Method | Path                       | Description               |
| ------ | -------------------------- | ------------------------- |
| `POST` | `/api/analytics/collect`   | Collect a page view event |
| `GET`  | `/api/analytics/overview`  | Get analytics overview    |
| `GET`  | `/api/analytics/pages`     | Get top pages             |
| `GET`  | `/api/analytics/referrers` | Get top referrers         |
| `GET`  | `/api/analytics/devices`   | Get device breakdown      |
| `GET`  | `/api/analytics/campaigns` | Get campaign stats        |

## Admin: schema management

Live schema editing (draft → validate → publish) for admin users. Registered only when `app.auth` is configured and `SCHEMA_EDIT_API_ENABLED=true`; otherwise every `/api/admin/schema/*` path returns 404. All routes require the `admin` role.

| Method | Path                                   | Description                        |
| ------ | -------------------------------------- | ---------------------------------- |
| `GET`  | `/api/admin/schema/status`             | Drift / draft status               |
| `GET`  | `/api/admin/schema/diff`               | Preview live-vs-file drift         |
| `GET`  | `/api/admin/schema/export`             | Serialize the live version as YAML |
| `GET`  | `/api/admin/schema/draft`              | Get the working draft              |
| `PUT`  | `/api/admin/schema/draft`              | Replace the working draft          |
| `POST` | `/api/admin/schema/draft/discard`      | Discard the draft                  |
| `POST` | `/api/admin/schema/draft/validate`     | Validate the draft                 |
| `POST` | `/api/admin/schema/draft/publish`      | Publish the draft (new version)    |
| `POST` | `/api/admin/schema/draft/rebase`       | Rebase a stale draft on active     |
| `GET`  | `/api/admin/schema/versions`           | List published versions            |
| `GET`  | `/api/admin/schema/versions/{version}` | Get a version                      |
| `POST` | `.../versions/{version}/restore`       | Restore a version                  |

Per-resource draft mutations (`/api/admin/schema/draft/tables`, `/pages`, `/auth/strategies`, `/forms`, `/automations`, `/connections`, `/preview`) and the `/api/admin/schema/prune` operation round out the surface. Each REST endpoint is mirrored as an MCP tool (`{appName}_schema_*`) for AI agents.

## Authentication endpoints

Authentication is handled by Better Auth and mounted at `/api/auth/*`. It includes email/password sign-in and sign-up, social OAuth providers, session management, password reset, email verification, two-factor authentication, magic links, email OTP, organizations, and admin user management. See the [Auth configuration docs](/en/docs/auth-overview) for setup, or explore every auth endpoint in the live API documentation.

Admins can browse the full machine-readable surface at `/api/scalar` and fetch the raw OpenAPI documents — see [OpenAPI](/en/docs/openapi).

## Cross-cutting features

Capabilities that apply across all API endpoints.

- **Pagination** — list endpoints page their results
- **Soft deletes** — `DELETE` trashes (recoverable); a separate purge hard-deletes
- **RBAC** — admin / member / viewer roles gate every protected route
- **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 one shape (see above)
