Skip to documentation

Conduit

Query live data

The knowledge base knows what your documents say; the data layer knows what your systems report right now. eli.ai bridges them through connectors (scoped, read-only external sources) and named queries(governed, parameterized SQL a human authored). This guide lists what's available, runs a query with full provenance, and then crosses the bridge in one call — a grounded answer that cites both documents and live rows.

Prerequisites

  • A key with data:read (list) and data:run (execute).
  • At least one connector and one named query configured in the workspace Data page — see Data connectors & domain components.

Authoring is deliberately not an API job

Creating connectors and named queries — with their credentials, SQL, and read-only validation — is an owner/admin task in the session-authenticated workspace Data UI, not something an API key does. The API consumes the governed surface: it lists and runs, it never writes SQL. That boundary is what keeps the data layer from becoming a SQL prompt-injection target.

Two different things are called “connectors”

/api/v1/data/connectors — used on this page — is the governed live-data surface: SQL databases you query at answer time. Do not reach for /api/v1/connectors to create one; that route manages content-ingestion sources (Slack, Notion, Drive) whose job is to pull documents into the knowledge base. They share a word and nothing else — different scopes, different tables, different lifecycle.

Discover the surface

List the connectors in scope and the named queries you can run. Secrets never appear — connector rows are credential-masked (hasCredential tells you a secret is stored without revealing it), and what you get back is slugs, titles, descriptions, and parameter specs: exactly what you need to make a call. Queries reference their source by connectorId, so correlate the two lists by id — or filter directly with GET /api/v1/data/queries?connectorId=….

GET/api/v1/data/connectorsBearer · data:read

List enabled connectors with health status. Credentials and connection configs are never returned.

Example requestbash
curl -s https://eli.ai/api/v1/data/connectors \
  -H "Authorization: Bearer $ELI_KEY"
Responsejson
{
  "items": [
    {
      "id": "01KY10AAAAAAAAAAAAAAAAAAAA",
      "name": "northwind-ops",
      "kind": "postgres",
      "description": "Order Platform read replica",
      "config": { "host": "replica.internal", "port": 5432, "database": "northwind", "user": "eli_ro" },
      "hasCredential": true,
      "maxRows": 200,
      "timeoutMs": 5000,
      "allowRawSql": false,
      "enabled": true,
      "lastStatus": "ok",
      "lastCheckedAt": "2026-07-21T08:00:00.000Z"
    }
  ],
  "total": 1
}
GET/api/v1/data/queriesBearer · data:read

List named queries (domain components) available to run, with their parameter specs.

Example requestbash
curl -s https://eli.ai/api/v1/data/queries \
  -H "Authorization: Bearer $ELI_KEY"
Responsejson
{
  "items": [
    {
      "id": "01KY10BBBBBBBBBBBBBBBBBBBB",
      "connectorId": "01KY10AAAAAAAAAAAAAAAAAAAA",
      "slug": "stuck-orders-by-age",
      "title": "Stuck orders by age",
      "description": "Orders in STUCK status, grouped by how long they've been stuck. Use for Order Platform health.",
      "params": [
        {
          "name": "min_hours",
          "type": "number",
          "required": false,
          "default": 24,
          "description": "Only orders stuck at least this many hours."
        }
      ],
      "enabled": true
    }
  ],
  "total": 1
}

Run a query

Call a query by its slug and supply validated params. The response is columns, rows (capped at the connector's maxRows, with a truncated flag), timing, and a dataCallId — the anchor into the append-only provenance trail. Every run, success or failure, writes a data_calls row recording the exact SQL, params, and status.

POST/api/v1/data/queries/{slug}/runBearer · data:run

Execute a named query with validated params. Read-only, capped, timed, and provenance-logged. Params are bound, never interpolated.

Request body

paramsobjectName → value map validated against the query's param specs. Types are coerced; required params must be present; defaults fill the rest.
Example requestbash
curl -s -L -X POST https://eli.ai/api/v1/data/queries/stuck-orders-by-age/run \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"params":{"min_hours":24}}'
Responsejson
{
  "dataCallId": "01KY10CCCCCCCCCCCCCCCCCCCC",
  "connectorId": "01KYCONNQUERYLIVEDATA00000",
  "queryId": "01KYQRYQUERYLIVEDATA000000",
  "columns": [
    "age_bucket",
    "order_count"
  ],
  "rows": [
    {
      "age_bucket": "24-48h",
      "order_count": 9
    },
    {
      "age_bucket": ">48h",
      "order_count": 5
    }
  ],
  "rowCount": 2,
  "durationMs": 41,
  "truncated": false
}

A slow or broken source is a status, not a 500

Queries run under a per-connector statement timeout and row cap. A timeout, an error, or a blocked statement returns a structured failure and still writes a data_calls provenance row (status timeout · error · blocked) — never a hung request. Invalid params return 400 with the offending field.

The bridge — one grounded call

The real payoff is not running SQL yourself — it is letting a natural-language question cross into live data automatically. POST /api/v1/query with includeData: true detects the entities in the question, runs their bound queries as governed lookups, and grounds the answer in both documents ([Sn]) and live rows ([Dn]) — the semantic and data layers fused in a single response.

ask across documents and live databash
curl -s -L -X POST https://eli.ai/api/v1/query \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question":"Is anything stuck on the Order Platform right now?","includeData":true}'
dual-cited answer (excerpt)json
{
  "answer": "The Order Platform currently reports 14 stuck orders in the fulfillment queue [D1]. Per the incident runbook, stuck orders older than 24 hours should be requeued from the fulfillment console [S1].",
  "sources":   [ { "id": "S1", "docTitle": "Order Platform incident runbook", "chunkId": "01KY10BB…" } ],
  "dataCalls": [ { "id": "D1", "dataCallId": "01KY10CC…", "connectorName": "northwind-ops", "queryTitle": "Stuck orders by age", "rowCount": 14, "executedAt": "2026-07-21T09:14:03.412Z" } ]
}

The includeData path runs at most three governed lookups and skips any binding whose parameters would need the model to invent a value — it supplies only entity-name and constant params. For the mechanics of which entities map to which queries, see Entity bindings & live data, and for the full response schema, Structured query (v1).

The leverage

Your documents describe the process; your database knows the current state. This endpoint answers questions that need both — "what's our policy, and are we currently violating it?" — with every number traceable to a logged, read-only query and every recommendation traceable to a source chunk. No dashboards to wire, no SQL in your prompts, and a provenance row behind every figure you can hand to an auditor.