API Reference
HTTP API endpoints for chat, queries, conversations, actions, and health checks.
Atlas exposes a Hono-based HTTP API. All endpoints are mounted under /api/. Use the SDK for a typed client, or call the API directly.
Base URL
| Setup | Base URL |
|---|---|
| Same-origin (Next.js rewrites) | http://localhost:3000/api |
| Standalone API | http://localhost:3001/api |
| Cross-origin | Set NEXT_PUBLIC_ATLAS_API_URL to the API host |
Authentication
Authentication headers depend on the configured auth mode:
| Auth Mode | Header |
|---|---|
| None | No header required |
| Simple Key | Authorization: Bearer <api-key> or X-API-Key: <key> |
| Managed | Cookie: better-auth.session_token=<token> (browser) or Authorization: Bearer <session-token> |
| BYOT | Authorization: Bearer <jwt> |
POST /api/chat
Streaming chat endpoint. Returns an event stream using the AI SDK Data Stream Protocol.
Request
curl -X POST http://localhost:3001/api/chat \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <key>" \
-d '{
"messages": [
{
"id": "msg-1",
"role": "user",
"parts": [{ "type": "text", "text": "How many users signed up last week?" }]
}
],
"conversationId": "optional-uuid"
}'| Field | Type | Required | Description |
|---|---|---|---|
messages | array | Yes | Chat messages with id, role, and parts |
conversationId | string (UUID) | No | Resume an existing conversation |
Response
Server-sent event stream. The x-conversation-id header contains the conversation ID.
POST /api/v1/query
Synchronous JSON query endpoint. Runs the agent and returns structured results.
Request
curl -X POST http://localhost:3001/api/v1/query \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <key>" \
-d '{"question": "What was last month revenue?"}'| Field | Type | Required | Description |
|---|---|---|---|
question | string | Yes | Natural language question |
conversationId | string (UUID) | No | Resume an existing conversation |
Response
{
"answer": "Last month's revenue was $1.2M, a 15% increase...",
"sql": ["SELECT SUM(total_amount) FROM orders WHERE ..."],
"data": [
{
"columns": ["total_revenue"],
"rows": [{ "total_revenue": 1200000 }]
}
],
"steps": 3,
"usage": { "totalTokens": 1523 },
"conversationId": "uuid",
"pendingActions": []
}| Field | Type | Description |
|---|---|---|
answer | string | Agent's natural language response |
sql | string[] | SQL queries executed |
data | array | Query results with columns and rows |
steps | number | Agent steps taken |
usage.totalTokens | number | Tokens consumed |
conversationId | string | Conversation ID (if persisted) |
pendingActions | array | Actions awaiting approval (if action framework enabled) |
GET /api/v1/conversations
List conversations for the authenticated user.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 20 | Max results (1--100) |
offset | number | 0 | Pagination offset |
starred | boolean | — | Filter by starred status |
Response
{
"conversations": [
{
"id": "uuid",
"userId": "user-id",
"title": "Revenue analysis",
"surface": "web",
"connectionId": null,
"starred": false,
"createdAt": "2026-01-15T10:30:00Z",
"updatedAt": "2026-01-15T10:35:00Z"
}
],
"total": 42
}GET /api/v1/conversations/:id
Get a conversation with all messages.
Response
{
"id": "uuid",
"userId": "user-id",
"title": "Revenue analysis",
"surface": "web",
"starred": false,
"createdAt": "2026-01-15T10:30:00Z",
"updatedAt": "2026-01-15T10:35:00Z",
"messages": [
{
"id": "msg-uuid",
"conversationId": "uuid",
"role": "user",
"content": { "parts": [{ "type": "text", "text": "..." }] },
"createdAt": "2026-01-15T10:30:00Z"
}
]
}PATCH /api/v1/conversations/:id/star
Star or unstar a conversation.
curl -X PATCH http://localhost:3001/api/v1/conversations/<id>/star \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <key>" \
-d '{"starred": true}'DELETE /api/v1/conversations/:id
Delete a conversation and its messages. Returns 204 No Content.
GET /api/health
Health check endpoint. No authentication required.
Response
{
"status": "ok",
"checks": {
"datasource": { "status": "ok", "latencyMs": 12 },
"provider": { "status": "ok", "provider": "anthropic", "model": "(default)" },
"semanticLayer": { "status": "ok", "entityCount": 5 },
"internalDb": { "status": "not_configured" },
"explore": { "backend": "nsjail", "isolated": true },
"auth": { "mode": "managed", "enabled": true },
"slack": { "enabled": false, "mode": "disabled" }
},
"sources": {
"default": { "status": "healthy", "latencyMs": 12, "dbType": "postgres" }
}
}Actions
Manage approval-gated write operations. Requires ATLAS_ACTIONS_ENABLED=true and an internal database.
GET /api/v1/actions
List actions, filtered by status.
| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | pending | Filter by status: pending, approved, denied, executed, failed |
limit | number | 50 | Max results (1--100) |
Response
{
"actions": [
{
"id": "uuid",
"action_type": "export_csv",
"target": "orders",
"summary": "Export orders table to CSV",
"status": "pending",
"requested_by": "user-id",
"created_at": "2026-01-15T10:30:00Z"
}
]
}GET /api/v1/actions/:id
Get a single action by ID. Returns 404 if the action was not requested by the authenticated user.
POST /api/v1/actions/:id/approve
Approve a pending action. The action is executed immediately upon approval.
curl -X POST http://localhost:3001/api/v1/actions/<id>/approve \
-H "Authorization: Bearer <key>"Returns the updated action with execution results. Returns 409 Conflict if the action has already been resolved.
For admin-only approval mode, the requester cannot approve their own action (separation of duties).
POST /api/v1/actions/:id/deny
Deny a pending action. Optionally provide a reason.
curl -X POST http://localhost:3001/api/v1/actions/<id>/deny \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <key>" \
-d '{"reason": "Not approved for production"}'Scheduled Tasks
CRUD endpoints for recurring queries. Requires ATLAS_SCHEDULER_ENABLED=true and an internal database.
GET /api/v1/scheduled-tasks
List scheduled tasks owned by the authenticated user.
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 20 | Max results (1--100) |
offset | number | 0 | Pagination offset |
enabled | boolean | — | Filter by enabled status |
POST /api/v1/scheduled-tasks
Create a scheduled task.
curl -X POST http://localhost:3001/api/v1/scheduled-tasks \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <key>" \
-d '{
"name": "Daily revenue",
"question": "What was yesterday total revenue?",
"cronExpression": "0 9 * * *",
"deliveryChannel": "email",
"recipients": [{"type": "email", "address": "team@example.com"}]
}'| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Task name (1--200 chars) |
question | string | Yes | Natural language question (1--2000 chars) |
cronExpression | string | Yes | Cron schedule (e.g. 0 9 * * *) |
deliveryChannel | string | No | email, slack, or webhook (default: webhook) |
recipients | array | No | Delivery targets (see below) |
connectionId | string | No | Target datasource connection |
approvalMode | string | No | auto, manual, or admin-only (default: auto) |
Recipient types:
{ "type": "email", "address": "user@example.com" }{ "type": "slack", "channel": "#channel", "teamId": "T0..." }{ "type": "webhook", "url": "https://...", "headers": {} }
GET /api/v1/scheduled-tasks/:id
Get a task with its 10 most recent runs.
PUT /api/v1/scheduled-tasks/:id
Update a scheduled task. All fields are optional.
DELETE /api/v1/scheduled-tasks/:id
Delete (disable) a scheduled task. Returns 204 No Content.
POST /api/v1/scheduled-tasks/:id/run
Trigger immediate execution of a task.
GET /api/v1/scheduled-tasks/:id/runs
List past runs for a task.
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 20 | Max results (1--100) |
Admin API
All admin endpoints are mounted at /api/v1/admin and require the admin role. When auth mode is none (local dev), requests are treated as implicit admin.
Overview
GET /api/v1/admin/overview
Dashboard summary with counts of connections, entities, metrics, glossary terms, and plugins.
{
"connections": 2,
"entities": 15,
"metrics": 8,
"glossaryTerms": 12,
"plugins": 1,
"pluginHealth": [
{ "id": "clickhouse", "name": "ClickHouse", "type": "datasource", "status": "healthy" }
]
}Semantic Layer
GET /api/v1/admin/semantic/entities
List all entity YAML files with summary info (table name, description, column/join/measure counts, source).
GET /api/v1/admin/semantic/entities/:name
Get the full parsed YAML for a specific entity. Path-traversal protected.
GET /api/v1/admin/semantic/metrics
List all metric YAML files grouped by source.
GET /api/v1/admin/semantic/glossary
Get all glossary files (default + per-source).
GET /api/v1/admin/semantic/catalog
Get the parsed catalog.yml file. Returns { "catalog": null } if no catalog exists.
GET /api/v1/admin/semantic/stats
Aggregate statistics across the semantic layer.
{
"totalEntities": 15,
"totalColumns": 120,
"totalJoins": 18,
"totalMeasures": 25,
"coverageGaps": {
"noDescription": 2,
"noColumns": 0,
"noJoins": 5
}
}GET /api/v1/admin/semantic/raw/:file
Serve raw YAML content for top-level files (catalog.yml, glossary.yml).
GET /api/v1/admin/semantic/raw/:dir/:file
Serve raw YAML content for subdirectory files (e.g., entities/orders.yml, metrics/revenue.yml).
Connections
GET /api/v1/admin/connections
List all registered database connections with their type and cached health status.
{
"connections": [
{ "id": "default", "dbType": "postgres", "health": { "status": "healthy", "latencyMs": 12 } }
]
}POST /api/v1/admin/connections/:id/test
Trigger a live health check for a specific connection. Returns latency and status.
{
"status": "healthy",
"latencyMs": 15,
"checkedAt": "2026-01-15T10:30:00Z"
}Users
User management endpoints require managed auth mode (Better Auth).
GET /api/v1/admin/users
List users with pagination, search, and role filtering.
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Max results (1--200) |
offset | number | 0 | Pagination offset |
search | string | — | Search by email (contains match) |
role | string | — | Filter by role: viewer, analyst, admin |
GET /api/v1/admin/users/stats
Aggregate user statistics: total count, banned count, and breakdown by role.
{
"total": 42,
"banned": 1,
"byRole": { "admin": 2, "analyst": 15, "viewer": 25 }
}PATCH /api/v1/admin/users/:id/role
Change a user's role. Cannot change your own role or demote the last admin.
curl -X PATCH http://localhost:3001/api/v1/admin/users/<id>/role \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <key>" \
-d '{"role": "analyst"}'POST /api/v1/admin/users/:id/ban
Ban a user. Optionally provide a reason and expiration.
curl -X POST http://localhost:3001/api/v1/admin/users/<id>/ban \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <key>" \
-d '{"reason": "Policy violation", "expiresIn": 86400}'POST /api/v1/admin/users/:id/unban
Remove a ban from a user.
DELETE /api/v1/admin/users/:id
Permanently delete a user. Cannot delete yourself or the last admin.
POST /api/v1/admin/users/:id/revoke
Revoke all sessions for a user (force logout).
Audit
Audit log endpoints require an internal database (DATABASE_URL).
GET /api/v1/admin/audit
Query the audit log with pagination and filters.
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Max results (1--200) |
offset | number | 0 | Pagination offset |
user | string | — | Filter by user ID |
success | boolean | — | Filter by success/failure |
from | string | — | ISO 8601 start date |
to | string | — | ISO 8601 end date |
GET /api/v1/admin/audit/stats
Aggregate audit statistics: total queries, error count, error rate, and queries per day (last 7 days).
Plugins
GET /api/v1/admin/plugins
List all installed plugins with their type, version, and status.
POST /api/v1/admin/plugins/:id/health
Trigger a health check for a specific plugin.
Password Management
These endpoints are available to any authenticated managed-auth user (not admin-only).
GET /api/v1/admin/me/password-status
Check if the current user must change their password.
POST /api/v1/admin/me/password
Change the current user's password.
curl -X POST http://localhost:3001/api/v1/admin/me/password \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <key>" \
-d '{"currentPassword": "old-pass", "newPassword": "new-pass-min-8-chars"}'Error Responses
All error responses follow a consistent format:
{
"error": "error_code",
"message": "Human-readable error description",
"retryAfterSeconds": 12
}Error Codes
| Code | Status | Description |
|---|---|---|
auth_error | 401 | Authentication failed or missing |
rate_limited | 429 | Too many requests. Retry-After header included |
configuration_error | 400 | Atlas is not fully configured (missing provider, etc.) |
no_datasource | 400 | No datasource URL configured |
invalid_request | 400 | Invalid request body or parameters |
validation_error | 422 | Request validation failed (Zod errors in details) |
not_found | 404 | Resource not found |
not_available | 404 | Feature not available (e.g., no internal DB) |
provider_model_not_found | 400 | Configured AI model does not exist |
provider_auth_error | 502 | AI provider authentication failed |
provider_rate_limit | 429 | AI provider rate limited |
provider_timeout | 504 | AI provider timed out |
provider_unreachable | 502 | Could not reach the AI provider |
provider_error | 502 | AI provider returned an error |
internal_error | 500 | Unexpected server error |
Rate Limiting
When ATLAS_RATE_LIMIT_RPM is set, the API enforces per-user rate limits (sliding 60-second window). Rate-limited responses include:
- HTTP status
429 Retry-Afterheader (seconds until the limit resets)retryAfterSecondsin the response body
See Environment Variables for configuration.
CORS
CORS is configured via ATLAS_CORS_ORIGIN (defaults to *). When an explicit origin is set, credentials (cookies) are allowed. The Retry-After and x-conversation-id headers are exposed to the client.
For cross-origin managed auth deployments, see the cross-origin section in the authentication guide.