REST API Reference¶
SynthOrg exposes a REST + WebSocket API built on Litestar. The API is the primary integration surface for the web dashboard, the Go CLI, and any external clients that want to drive a synthetic organization programmatically.
Open Interactive Reference Download OpenAPI Schema
Base URL and Versioning¶
All endpoints live under a version-prefixed path:
The prefix is configurable via the api_prefix field of ApiConfig (default /api/v1). Breaking changes bump the path version; additive changes (new fields, new endpoints, relaxed constraints) ship under the existing version.
When running the server locally you also get two kinds of side paths -- documentation paths (mounted by Litestar at a fixed prefix independent of api_prefix) and API paths (relative to api_prefix):
Documentation paths (fixed at /docs/*):
| Path | Content |
|---|---|
/docs/api |
Scalar UI live against your running server |
/docs/openapi.json |
Live OpenAPI schema for the running server |
API paths (move with api_prefix, shown with the default /api/v1):
| Path | Content |
|---|---|
/api/v1/health |
Liveness + readiness endpoint |
/api/v1/ws |
WebSocket endpoint for server-sent events (approvals, meetings, task lifecycle) |
The static snapshot on this page is produced by scripts/export_openapi.py, which takes the live Litestar schema and runs it through inject_rfc9457_responses to attach RFC 9457 error response shapes to every operation. The result is a superset of what /docs/openapi.json returns at runtime.
Authentication¶
SynthOrg uses JWT session tokens issued by the auth controller. The typical flow:
- First-run setup. On a fresh install,
POST /api/v1/auth/setupcreates the initial CEO account. After setup completes, this endpoint returns a conflict error. - Login.
POST /api/v1/auth/loginwith a username and password returns aTokenResponsecarrying a signed JWT, itsexpires_in(seconds), and amust_change_passwordflag. Include the token on subsequent requests asAuthorization: Bearer <token>. A server-side session record is created as a side effect and is retrievable viaGET /api/v1/auth/sessions. - Password change. New users are forced through
POST /api/v1/auth/change-passwordbefore any other endpoint accepts their token -- therequire_password_changedguard blocks everything else until the temporary password is rotated. - Current identity.
GET /api/v1/auth/mereturns the caller'sid,username,role, andmust_change_passwordflag (no session metadata). - WebSocket tickets. Browsers can't set
Authorizationheaders on WebSocket connections, soPOST /api/v1/auth/ws-ticketmints a short-lived single-use ticket. The preferred way to present it is as the first WebSocket message ({"action": "auth", "ticket": "<ticket>"}) so the ticket never lands in URLs, access logs, or browser history. A legacy/api/v1/ws?ticket=<ticket>query-param form is also accepted and is validated before the WebSocket upgrade. - Session management.
GET /api/v1/auth/sessionslists the caller's active sessions by default; CEOs can pass?scope=allto list every user's sessions across the organization.DELETE /api/v1/auth/sessions/{session_id}revokes a specific session.POST /api/v1/auth/logoutrevokes the session backing the current token (the normal "log out of this browser" action). There is no bulk "revoke all" endpoint.
Passwords are hashed with Argon2id. The server performs a constant-time dummy verification on unknown usernames to prevent timing-based user enumeration.
Endpoint Groups¶
The API is organised into resource controllers. Every controller is mounted under the /api/v1 prefix.
Identity and users¶
| Resource | Path | Purpose |
|---|---|---|
| Auth | /auth |
Setup, login, password, sessions, WebSocket tickets |
| Users | /users |
Human user CRUD (CEO-only), role assignment |
Organization and agents¶
| Resource | Path | Purpose |
|---|---|---|
| Company | /company |
Top-level company identity and config |
| Departments | /departments |
Department CRUD, membership, policy overrides |
| Agents | /agents |
Agent CRUD, hiring/firing, personality assignment |
| Agent Autonomy | /agents/{id}/autonomy |
Per-agent autonomy level and trust policy |
| Agent Collaboration | /agents/{id}/collaboration |
Peer collaboration rules |
| Agent Quality | /agents/{id}/quality |
Quality score overrides (L3 human layer) |
| Activities | /activities |
Activity timeline (lifecycle events, cost events, promotions) |
| Personalities | /personalities |
Personality preset CRUD |
Work and coordination¶
| Resource | Path | Purpose |
|---|---|---|
| Projects | /projects |
Project CRUD, status, artifacts |
| Tasks | /tasks |
Task CRUD, assignment, lifecycle transitions |
| Task Coordination | /tasks/{id}/coordinate |
Multi-agent coordination actions |
| Messages | /messages |
Inter-agent message bus access |
| Meetings | /meetings |
Meeting scheduling, participation, minutes |
| Approvals | /approvals |
Approval gate queue and decisions |
| Artifacts | /artifacts |
Artifact content storage and retrieval |
Workflows¶
| Resource | Path | Purpose |
|---|---|---|
| Workflows | /workflows |
Visual workflow definition CRUD, validation, YAML export |
| Workflow Versions | /workflows/{id}/versions |
Version history, diff, rollback |
| Workflow Executions | /workflow-executions |
Activate, list, get, cancel executions |
| Template Packs | /template-packs |
Additive team pack listing and live application |
| Setup | /setup |
First-run wizard endpoints (template selection, personality seeding) |
Operations and platform¶
| Resource | Path | Purpose |
|---|---|---|
| Health | /health |
Liveness + readiness |
| Providers | /providers |
LLM provider runtime CRUD, model auto-discovery, health |
| Budget | /budget |
Cost tracking, spend reports, budget enforcement, risk budget |
| Analytics | /analytics |
Aggregated metrics across agents, tasks, and providers |
| Reports | /reports |
On-demand report generation (POST /generate) and period listing (GET /periods) |
| Memory Admin | /admin/memory |
Fine-tuning pipeline, checkpoint management, embedder queries |
| Backups | /admin/backups |
Backup orchestration, scheduling, retention |
| Settings | /settings |
Runtime-editable settings (DB > env > YAML > code) |
| Ceremony Policy | /ceremony-policy |
Project and per-department ceremony policy resolution |
Full request/response schemas for every endpoint are in the interactive reference.
Request Patterns¶
Pagination¶
List endpoints accept limit and offset query parameters and return a PaginatedResponse[T] envelope:
{
"data": [...],
"pagination": {"total": 142, "offset": 0, "limit": 50},
"degraded_sources": [],
"error": null,
"error_detail": null,
"success": true
}
data holds the page of items. pagination carries the offset/limit/total triple. degraded_sources is empty on a normal response and lists data sources that failed gracefully when the endpoint returned partial data. error and error_detail are null on success; success is derived from error.
Optimistic concurrency¶
Runtime-editable settings emit an ETag header on reads and honor If-Match on writes. To update a setting without trampling a concurrent write, pass the previously-received ETag back via If-Match; a mismatch produces a 409 Conflict with error_code VERSION_CONFLICT (4002).
Workflow definitions, workflow versions, workflow executions, and tasks use a different optimistic-concurrency mechanism: an expected_version: int field in the request body (not an HTTP header). The server rejects the update with the same VERSION_CONFLICT code when the stored version differs from the value supplied. Both mechanisms produce identical error shapes on conflict; only the input channel differs.
WebSocket events¶
Real-time updates (approval requests, meeting state, task transitions, routing decisions) are pushed over /api/v1/ws. After authenticating with a ws-ticket, clients send JSON messages to subscribe or unsubscribe from named channels (with optional payload filters), and the server pushes WsEvent JSON payloads on subscribed channels. Event types are tagged via a type field on each payload.
Error Format¶
Errors use RFC 9457 Problem Details for HTTP APIs. The server supports two response shapes, selected via content negotiation:
Bare application/problem+json -- returned when the client sends Accept: application/problem+json:
{
"type": "https://synthorg.io/docs/errors#validation",
"title": "Validation Error",
"status": 422,
"detail": "Field 'name' is required",
"instance": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"error_code": 2001,
"error_category": "validation",
"retryable": false,
"retry_after": null
}
Envelope form (ApiResponse[T]) -- the default, returned for application/json or no explicit Accept header:
{
"data": null,
"error": "Field 'name' is required",
"error_detail": {
"detail": "Field 'name' is required",
"error_code": 2001,
"error_category": "validation",
"retryable": false,
"retry_after": null,
"instance": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "Validation Error",
"type": "https://synthorg.io/docs/errors#validation"
},
"success": false
}
In both shapes, instance is the request correlation ID used for log tracing -- not the request URL path. The error_code field is a 4-digit machine-readable code grouped by category, and error_category is the lowercase category identifier:
| Range | error_category |
Examples (error_code name) |
|---|---|---|
| 1xxx | auth |
UNAUTHORIZED, FORBIDDEN, SESSION_REVOKED |
| 2xxx | validation |
VALIDATION_ERROR, REQUEST_VALIDATION_ERROR |
| 3xxx | not_found |
RESOURCE_NOT_FOUND, RECORD_NOT_FOUND, ROUTE_NOT_FOUND |
| 4xxx | conflict |
RESOURCE_CONFLICT, DUPLICATE_RECORD, VERSION_CONFLICT |
| 5xxx | rate_limit |
RATE_LIMITED |
| 6xxx | budget_exhausted |
BUDGET_EXHAUSTED |
| 7xxx | provider_error |
Upstream LLM provider failures |
| 8xxx | internal |
Unhandled server errors |
The type URI points to the category section of the Error Reference, using the pattern https://synthorg.io/docs/errors#<category>. The full error taxonomy, including retryable semantics and retry_after behavior, lives there.
Rate Limiting¶
The API applies per-IP rate limiting via Litestar's built-in RateLimitConfig. Limits are configurable per deployment. Clients that exceed the limit receive 429 Too Many Requests carrying error_code 5000 (RATE_LIMITED) and a Retry-After header. In the envelope form the code lives at error_detail.error_code.
CORS¶
CORS is disabled by default for non-local origins. Add trusted dashboard origins via ApiConfig.cors.allowed_origins. Wildcard origins (*) cannot be combined with allow_credentials=true.
Further Reading¶
- Interactive API Reference -- every endpoint, request body, and response schema
- OpenAPI Schema -- raw schema for codegen and tooling
- Error Reference -- full error taxonomy and codes
- Security -- authn/authz design, trust levels, audit log
- Architecture -- where the API sits in the overall system