API reference
Kaveon exposes two independent HTTP surfaces: the FastAPI platform API that Studio calls, and the standalone Rust Engine API. They have different base paths, different authentication, and different maturity.
Which surface
| Surface | Base path | Use for | Maturity |
|---|---|---|---|
| Studio proxy | /api/kaveon/* | Browser and user-facing clients | Current |
| Platform API | /api/v1/* | Server-side integrations behind the trusted boundary | Current |
| Engine HTTP | /v1/* | SQL, catalog, cluster, and query operations | Alpha |
Authentication
The browser never sends trusted identity to the platform API. Studio resolves the session server-side, then its same-origin proxy stamps X-User-* headers and seals the request with KAVEON_PROXY_SECRET, which must match on Studio and the API. The API rejects identity headers that do not arrive with the matching secret.
# From a browser or user-facing client — go through the proxy, send no identity
curl -s https://your-studio-host/api/kaveon/api/v1/datasets
# Server-side, behind the trust boundary — supply the proxy secret yourself
curl -s http://kaveon-api:8080/api/v1/datasets \
-H "x-proxy-secret: $KAVEON_PROXY_SECRET" \
-H "x-user-email: analyst@example.com" \
-H "x-user-role: Analyst"Roles on the API
Endpoints enforce a minimum role of Viewer → Analyst → Editor → Admin. Query execution is the one with a notable exception: a Viewer may execute only from a dashboard or filter context, and only a single read-only SELECT — no DDL, no DML, no stacked statements. Everything else requires Analyst.
Running SQL
| Method | Path | Purpose |
|---|---|---|
| POST | /api/v1/sql/execute | Execute synchronously. |
| POST | /api/v1/sql/execute-async | Submit a job; returns a job id. |
| GET / DELETE | /api/v1/sql/async/{job_id} | Poll for results, or cancel. |
| POST | /api/v1/sql/generate | Generate SQL from a dataset and chart definition. |
| DELETE | /api/v1/sql/cache | Invalidate cached results. |
| POST | /api/v1/lab/query | SQL Lab execution — cancellable, recorded in history. On a KaveonDB source, "stream": true submits with paged delivery and answers at once with the query id. |
| GET | /api/v1/lab/query/{id} | A streamed statement’s record: state, elapsed time, columns, stage and scan counters, execution placement, error. |
| GET | /api/v1/lab/query/{id}/results/{n} | Page n of its rows: 200 with the page, 202 with Retry-After while it is being written, 404 past the end, 410 once the statement failed or was cancelled. |
| DELETE | /api/v1/lab/query/{id} | Cancel a streamed statement. |
| POST | /api/v1/lab/ctas | Create a table from a query. |
| GET | /api/v1/lab/query-history | Your own query history. |
curl -s $API/api/v1/sql/execute \
-H 'content-type: application/json' \
-d '{
"sql_text": "SELECT region, SUM(total) AS revenue FROM orders GROUP BY region",
"database": "kaveon",
"source": "lab",
"row_limit": 1000,
"use_cache": true,
"cache_ttl": 300
}'sql_text and database are required. row_limit accepts 1–5000 and cache_ttl 30–3600 seconds; caching is off unless you ask for it. Results are keyed by a SHA of the query, so an identical request inside the TTL returns without touching the source.
Asking questions — DLM
| Method | Path | Purpose |
|---|---|---|
| POST | /api/v1/dlm/ask | Resolve a natural-language question deterministically. |
| POST | /api/v1/dlm/serve-chart | Answer directly as chart-ready series. |
| GET | /api/v1/dlm/route | See which dataset a question routes to. |
| GET | /api/v1/dlm/coverage | What the compiled context can answer. |
| GET | /api/v1/dlm/filter-values | Dimension values from context, no source scan. |
| POST | /api/v1/datasets/{id}/dlm/generate | Compile the dataset context. |
| GET / PUT | /api/v1/datasets/{id}/dlm/context | Read or curate the context spec. |
| GET | /api/v1/datasets/{id}/freshness | Staleness of the compiled context. |
curl -s $API/api/v1/dlm/ask \
-H 'content-type: application/json' \
-d '{"question":"revenue by region","limit":50}'The response reports the routed dataset, the assembled SQL, chart hints, and which path answered. ok: false means nothing matched — the DLM declines rather than guessing, which is the point of a deterministic resolver. Asking against a stale dataset also triggers a background rebuild.
Content and configuration
Datasets, charts, dashboards, and data sources follow the same REST shape — GET to list, GET /{id} to read, POST to create, PUT or PATCH to update, DELETE to remove, plus a /summary listing and a /favorite toggle.
| Domain | Base | Notable |
|---|---|---|
| Datasets | /api/v1/datasets | /{id}/columns |
| Charts | /api/v1/charts | — |
| Dashboards | /api/v1/dashboards | string ids, not integers |
| Data sources | /api/v1/data-sources | /{id}/test, /{id}/table-count |
| Health | /api/health | unauthenticated probe |
Errors
Errors carry a stable machine-readable code alongside a human-readable message:
{ "detail": { "code": "forbidden", "message": "Analyst role required to execute queries." } }| Status | Means |
|---|---|
| 401 | No usable identity — the proxy secret is missing or does not match. |
| 403 | Authenticated, but the role or visibility rule denies it. |
| 429 | Rate limited — 120 SQL executions per user per minute. |
Engine HTTP
A separate service with its own base path. The statement body takes query, optionally scoped by catalog and schema:
curl -s localhost:8080/v1/statement \
-H 'content-type: application/json' \
-d '{"query":"SELECT count(*) FROM warehouse.default.orders"}'Responses carry a query id, state, columns, rows, and elapsed time. Results are materialized in process memory and history is process-local, so do not assume durability or retention across restarts. Full endpoint list: Kaveon Engine.
Compatibility
- Pin clients to a deployed version; the API is pre-1.0 and response fields may change.
- Route user-facing traffic through the Studio proxy, never directly at the platform API.
- Keep the Engine API on a trusted network during alpha.