Skip to content

HTTP API

The auto-generated HTTP API reference (SK-DOCS-003 slice d) is blocked on apps/api emitting an OpenAPI schema. The contract this page will eventually render is canonical in packages/sdk/src/index.ts — every HTTP endpoint has a matching SDK method, by GLOBAL-001.

While slice d is blocked, use the SDK reference: each method (ask, runSql, createDatabase, listKeys, …) documents the exact wire shape that flows over the corresponding POST /v1/<resource> endpoint.

  • POST /v1/ask — the canonical NL→SQL endpoint. See NlqClient.ask.
  • POST /v1/run — raw-SQL escape hatch (GLOBAL-015). See NlqClient.runSql.
  • POST /v1/databases — create a database from a plain-English brief. See NlqClient.createDatabase.
  • GET /v1/databases — list databases. See NlqClient.listDatabases.
  • POST /v1/keys, GET /v1/keys, DELETE /v1/keys/:id — API-key lifecycle.
  • POST /v1/chat/messages — chat-style transcript endpoint.

Curl-shaped examples for /v1/ask (read + write + anonymous mode) live in the curl tutorial.

Every non-2xx response carries a JSON envelope { "error": { "status": <code>, … } } (GLOBAL-012). An agent can read this table before calling to decide which codes are retryable and which need a human. status is the discriminant the SDK exposes as err.code; the same set is the ApiErrorCode union in packages/sdk/src/index.ts, kept in lockstep by a build-time guard (apps/docs/scripts/check-error-codes.ts).

Code HTTP Meaning Retryable? Recovery action
db_not_found 404 Pinned dbId doesn’t exist. No Drop dbId to create from the goal, or fix the id.
schema_unavailable 422 The DB’s schema fingerprint isn’t ready yet. Yes Retry shortly.
db_misconfigured 502 The DB’s stored config is invalid. No Recreate the database; if it persists, contact support.
db_unreachable 502 The backing engine is unreachable. Yes Retry with backoff.
sql_rejected 400 The SQL failed the allow-list or the engine (body.reason). No Rephrase the goal, or hand-write valid SQL via /v1/run.
llm_failed 502 The model chain couldn’t produce SQL. Yes Retry; if persistent, simplify the goal.
rate_limited 429 Too many requests (body.limit, body.count). Yes Wait, honor Retry-After, then retry.
unauthorized 401 Missing, invalid, or expired credential. No Sign in again, or mint a new key.
forbidden 403 A read-only key tried to write via /v1/run (SK-APIKEYS-003). No Use a write-capable key.
ambiguous_db 409 2+ databases match; confidence below the floor. No Pick from body.candidate_dbs, resend with dbId.
clarify_required 409 Pinned dbId but the goal looks like a create (body.pinned_db). No Resend without dbId to create, or cancel.
invalid_engine 400 engine isn’t in the allowed set. No Use postgres or clickhouse.
invalid_model 400 model isn’t in the preset set (body.allowed). No Use auto, fast, or best.
model_unavailable 409 model: "best" but the account has no frontier lane (no BYOLLM key, no paid plan). No Add a provider key at body.link, or drop model.
goal_required 400 goal missing or empty. No Supply a non-empty goal.
dbId_required 400 dbId required for this call. No Supply dbId.
sql_required 400 /v1/run called without sql. No Supply sql.
sql_too_long 400 sql exceeds the size cap. No Shorten the query.
db_required 400 /v1/run called without db. No Supply db.
invalid_json 400 Request body isn’t valid JSON. No Fix the JSON.
invalid_body 400 Body failed validation (body.message). No Fix the offending field.
invalid_email 400 Email is malformed. No Fix the email.
invalid_byollm_key 400 The BYOLLM credential is mis-shaped. No Fix provider / model / key.
byollm_unavailable 503 The server can’t seal keys (KEK unset). Yes Retry; if persistent it’s an operator action.
secret_unconfigured 503 The server is missing a required secret. No Operator action — not caller-fixable.
wrong_preset 409 remember() target DB isn’t an agent_memory_v1 preset. No Point at an agent-memory database, or create one.
connect_requires_account 403 /v1/db/connect called without a signed-in session. No Sign in, then reconnect.
invalid_request 400 Malformed body, or a bad / non-HTTPS / egress-blocked connection URL (body.message). No Fix the field named in body.message.
introspection_failed 502 The connected database couldn’t be reached or read. Yes Check the host and credential, then retry.
sealing_unconfigured 503 The server can’t seal secrets (KEK unset). No Operator action — not caller-fixable.
unknown_error 5xx A 5xx with no parseable envelope. Yes Retry; if persistent, report it.
non_json_response A proxy/CDN returned non-JSON (SDK sentinel). Maybe Retry; check the network path.
network_error Transport failure, no response (SDK sentinel, httpStatus === 0). Yes Retry with backoff.
aborted The caller’s AbortSignal fired (SDK sentinel, httpStatus === 0). No Re-issue the request if still wanted.

The last four are SDK-only sentinels — the API never sends them; the SDK raises them for transport states. Recoverable failures (transport errors and transient 5xx) are retried automatically by @nlqdb/sdk before they ever surface (SK-SDK-008).

Some codes carry extra fields an agent should branch on:

  • candidate_dbs — on ambiguous_db: [{ id, slug }] to render a picker.
  • pinned_db — on clarify_required: the DB that was pinned when the goal looked like a create.
  • requires_confirm + diff — on a write preview (dryRun / destructive op): apply only after the human approves the diff (GLOBAL-023).
  • limit + count — on rate_limited: the window cap and how many you’ve used.
  • link — on model_unavailable: deep-link to add a BYOLLM key or plan.