/ask

Ask in plain English.
Get JSON back.

One endpoint that translates natural language into a plan across every shape, runs it, and returns rows. The LLM call uses your key — see BYOK LLM.

123 top 5 customers by spend in March cost router vector.topk graph.walk est 41ms est 87ms sql.aggregate result · 2 rows customer spend c_84 4,204 c_12 3,977 plan + cost attached
the pipeline

How a question becomes a plan.

The model never free-writes a query. It picks from a shortlist the cost walker already ranked — so the expensive plan it might have guessed was filtered out before it could choose it.

01
You ask

POST a plain-English question to /ask. No query language, no shape selection — the question is the whole request.

"q": "Top 5 customers by spend…"
02
Cost walker shortlists

Candidate plans across SQL, vector, graph and full-text are ranked with real cardinality from oc-stats. Cheap plans rise above expensive ones before any model is consulted.

oc-stats → ranked candidates
03
Model picks

The LLM — running on your key — chooses from the cost-tested shortlist. It cannot select a shape that never passed the cost test.

choice ⊆ shortlist
04
Rows, plan attached

The response carries the rows plus the chosen plan and per-node cost. Nothing about how the answer was produced is hidden.

"plan" · "cost" · "explain"
one round trip

The answer arrives with its receipts.

A single POST carries the question. The response carries the rows — and the plan that produced them, with per-node cost. The question on the right routes to sql.aggregate + sql.semi_join: an aggregate for spend, a semi-join for the login check.

If the plan looks wrong, you don't debug a black box — you pin a shape and re-run. How each underlying mode works is covered in the architecture guide.

/ask
> POST /v1/tenants/:t/ask
> { "q": "Top 5 customers by spend in March
>         who haven't logged in this week" }

{
  "plan": "sql.aggregate + sql.semi_join",
  "cost": { "rows_estimate": 5, "io_cost": 8.2 },
  "rows": [ { "id": "c_84", "spend": 4204.10 }, … ],
  "explain": { "nodes": [ … ] }
}
why it doesn't guess

Three properties that make /ask debuggable.

Cost-informed

The model chooses from plans that already passed a cost test built on real cardinality from oc-stats — not from whatever shape its training data favors.

oc-stats cardinalityranked before the pick
Deterministic

Same question → same plan → same rows, modulo data. Replays are stable. If you log a question today, re-running it tomorrow exercises the same plan.

stable replayssame plan, same rows
Overridable

Every response carries the plan it ran with per-node cost. Disagree with the pick? Pin a shape and re-run — the override is a first-class request, not a workaround.

EXPLAIN in every responsepin + re-run
byok llm

Your key. Your audit. Your bill.

The model call inside /ask runs on a key you bring. We keep the planner and the security boundary; your provider keeps the token bill. No token markup is billed — what your provider charges is what LLM usage costs you.

Read BYOK LLM →
Providers

Bring a key from any of the four supported providers. Switch providers without touching your queries.

OpenAIAnthropicGeminiGroq
Keys at rest

Keys are envelope-encrypted at rest. They are decrypted only when a request needs them.

envelope-encrypted
Audit

The audit trail shows which provider and model ran each call, with prompt and completion token counts.

providermodelprompt + completion tokens
Fallback & billing

A platform key exists as a fallback, but it is opt-in only. Token usage is never marked up — there is no LLM line on your bill.

fallback opt-in onlyno token markup
next

Read the /ask docs, then send your first question.