REST API Reference
Every action in the Datarelix UI is a REST endpoint, and the same endpoints are available programmatically. Base URL: https://app.datarelix.ai. Most routes live under /api/v1; sign-in routes are at the root (/auth/...). An interactive reference (Swagger / ReDoc) and the OpenAPI schema are served by the app itself.
Authentication
Two credentials are accepted, and either one is enough:
- A browser session cookie — set when you sign in. This is what the app itself uses; no key involved.
- An
X-API-Keyheader — for server-to-server calls.
curl -H "X-API-Key: $DATARELIX_API_KEY" \ https://app.datarelix.ai/api/v1/connectionsAPI keys are issued on request, not self-serve — there is no key-generation screen in the app. Contact us if you need one. Full details, including what happens when both credentials are present, are in Authentication.
Execution
POST /api/v1/run # run a question, returns the finished RunResultPOST /api/v1/run/stream # same, as an SSE stream of progress + artifactsGET /api/v1/runs # ?connection_id= &status= &limit= (max 100) &offset=GET /api/v1/runs/{run_id}Note the path is run (singular) for execution and runs (plural) for history. There is no endpoint to cancel a run — a run is cancelled by closing the SSE stream. (The one cancel endpoint in the API belongs to background schema-discovery jobs, below.)
Request body for both execution routes:
| Field | Type | Notes |
|---|---|---|
question | string | Required, 1–2000 characters. |
connection_id | string | Required. |
conversation_id | string | Optional — continues an existing conversation instead of starting one. |
chart_requested | boolean | Optional, default false. Set when the user asked for a chart. |
selected_tables_override | string[] | Optional — restrict this run’s table scope. |
selected_columns_override | object | Optional — table name → column names, for this run only. |
Artifacts
GET /api/v1/artifacts/{artifact_id}GET /api/v1/artifacts/{artifact_id}/downloadGET /api/v1/artifacts/{artifact_id}/render # ?format=png|svg — server-side chart renderGET /api/v1/runs/{run_id}/artifacts/{artifact_id}/csv # ?rerun=true to bypass the cachePOST /api/v1/runs/{run_id}/artifacts/{artifact_id}/refreshCSV export and refresh are addressed through their run — the run id is part of the path, and it’s what ownership is checked against.
Connections
GET /api/v1/connections # ?tag=POST /api/v1/connectionsGET /api/v1/connections/{id}PATCH /api/v1/connections/{id}DELETE /api/v1/connections/{id}POST /api/v1/connections/{id}/testPOST /api/v1/connections/{id}/duplicatePOST /api/v1/connections/{id}/paused # body: { "paused": true | false }PUT /api/v1/connections/{id}/tagsGET /api/v1/connections/dialects # supported engines + auth modes + availabilityGET /api/v1/connections/auth-modes # ?dialect=postgres (required)GET /api/v1/connections/dialects is the authoritative list of engines and the auth modes each one supports — read it rather than hard-coding a list.
POST /api/v1/connections/{id}/paused is how you free a connection slot without deleting anything; see Billing & plans.
Schema
GET /api/v1/connections/{id}/schemas # schemas / datasets / databases in scopeGET /api/v1/schema/{connection_id}/tablesGET /api/v1/schema/{connection_id}/tables/{table_name}GET /api/v1/connections/{id}/metadata/status # is the analyzed metadata stale?PUT /api/v1/connections/{id}/metadata # curated table/column descriptions, keysPUT /api/v1/connections/{id}/schema-overrides # key/relationship overrides used when planningPUT /api/v1/connections/{id}/schema-selection # persisted table + column selectionPUT /api/v1/connections/{id}/selected-tablesTable listing lives under /api/v1/schema/{connection_id}/..., not under the connection. There is no /connections/{id}/schema endpoint.
Schema discovery
POST /api/v1/connections/{id}/introspect # structure only, no AIPOST /api/v1/connections/{id}/discover # AI schema analysis, blockingPOST /api/v1/connections/{id}/discover/stream # AI schema analysis, SSEPOST /api/v1/connections/{id}/discover/cross-schema # relationships only, reuses metadataPOST /api/v1/connections/{id}/discover/jobs # start (or re-attach to) a background jobGET /api/v1/connections/{id}/discover/jobs/activeGET /api/v1/connections/{id}/discover/jobs/{job_id}GET /api/v1/connections/{id}/discover/jobs/{job_id}/streamPOST /api/v1/connections/{id}/discover/jobs/{job_id}/cancel # 202, best-effortBackground jobs are the path the app uses: a job survives navigating away, and the stream can be re-attached. Starting a fresh AI schema analysis consumes one from your monthly allowance; resuming or retrying a job that reuses existing discovery does not.
Conversations
GET /api/v1/conversations # ?connection_id= &limit= (max 100) &offset=GET /api/v1/conversations/{id}GET /api/v1/conversations/{id}/runsDELETE /api/v1/conversations/{id}DELETE /api/v1/conversations/{id}/runs/from/{from_turn_index}The last one is the truncate operation — it’s a DELETE addressed at a turn index, and it removes that turn and everything after it. This is what “edit an earlier message and re-ask” does under the covers.
Query templates
GET /api/v1/connections/{id}/templatesPOST /api/v1/connections/{id}/templatesPUT /api/v1/templates/{template_id}DELETE /api/v1/templates/{template_id}Dashboards
GET /api/v1/dashboardsPOST /api/v1/dashboardsGET /api/v1/dashboards/{id}PUT /api/v1/dashboards/{id}DELETE /api/v1/dashboards/{id}POST /api/v1/dashboards/{id}/refreshPOST /api/v1/dashboards/{id}/itemsPUT /api/v1/dashboards/{id}/items/reorderPUT /api/v1/dashboards/{id}/items/{item_id}DELETE /api/v1/dashboards/{id}/items/{item_id}POST /api/v1/dashboards/{id}/items/{item_id}/refreshUpdating a dashboard is PUT, not PATCH. Reordering is PUT .../items/reorder — a nested route under items, not a top-level reorder.
Chart styling
GET /api/v1/conversations/{id}/stylePOST /api/v1/conversations/{id}/style # apply a style patch directly, no AIGET /api/v1/conversations/{id}/charts/restyle # ?run_id= &artifact_id=GET /api/v1/style/presetsBilling and workspace
GET /api/v1/billing/summary # plan, status, usage this month, seatsPOST /api/v1/billing/checkout # returns a Stripe Checkout URLPOST /api/v1/billing/portal # returns a Stripe customer portal URLGET /api/v1/workspace # your workspace: roster for owners, summary for membersPOST /api/v1/workspace/invitesDELETE /api/v1/workspace/invites/{invite_id}POST /api/v1/workspace/invites/acceptDELETE /api/v1/workspace/members/{member_user_id}Workspace routes only do anything on a Team plan. Plan behaviour is described in Billing & plans; the error codes these return are in Error codes.
POST /api/v1/billing/webhook also exists — it is Stripe’s callback, authenticated by signature, and is not callable by you.
Account
GET /auth/meGET /auth/me/onboarding-statusGET /auth/providers # which sign-in methods are enabledPOST /auth/email/registerPOST /auth/email/loginPOST /auth/email/forgot-passwordPOST /auth/email/reset-passwordPOST /auth/email/verifyPOST /auth/refreshPOST /auth/logoutThese are at the root, not under /api/v1. See Authentication.
Streaming a run
POST /api/v1/run/stream returns server-sent events. Each event has a name and a JSON payload.
curl -N -X POST https://app.datarelix.ai/api/v1/run/stream \ -H "X-API-Key: $DATARELIX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "connection_id": "conn_abc", "question": "Top 10 customers by revenue last quarter", "chart_requested": true }'Events you’ll see:
| Event | Payload |
|---|---|
status | { phase: "building_context" | "planning" | "executing" } |
step_start | { index, op, ref } |
step_complete | { index, status, elapsed_ms } |
error | { type, message, recoverable } |
complete | The full run result |
The connection is kept alive with periodic heartbeat comments, so don’t treat a quiet stream as a dead one. To continue a conversation, pass conversation_id in the request body.
Failures are described by error codes.
Idempotency
Run creation is not idempotent — each call creates a new run. To replay a question, re-issue the same payload; to replay it in place within a conversation, truncate from the turn index first (DELETE /api/v1/conversations/{id}/runs/from/{from_turn_index}) and then ask again.
Rate limits and quotas
Two different things throttle you, and they return different bodies:
- Rate limit — a fixed per-minute request limit, currently 20 requests per minute, counted per API key (or per signed-in user when you’re using a session). Exceeding it returns
429with a plain-string detail andRetry-After,X-RateLimit-Limit, andX-RateLimit-Remainingheaders. - Plan quotas — monthly questions, monthly AI schema analyses, connection limits. These return a structured body with a
codeyou can branch on. See Error codes.
Branch on the response body’s code, never on the status alone — 429 is shared by the rate limiter and the quota system.