stream/ask

Asks a question and streams SQL generation, execution, and answer events using server-sent events

Recent Requests
Log in to see full request history
TimeStatusUser Agent
Retrieving recent requests…
LoadingLoading…

The POST /v2/stream/ask endpoint lets you ask a single question and stream the whole answer over Server-Sent Events (SSE) — SQL generation and execution state, the streamed natural-language summary (or an explanation for non-SQL questions), and mid-stream clarification checkpoints you can answer without restarting the turn.

This is the v2 (adhoc) streaming ask — the successor to the v1 /stream/ask. Two things are intentionally different from v1:

  • Charts are not part of this stream. Once you have the generated SQL, render a chart separately with POST /v2/generate_chart (Apache ECharts).
  • Clarification is built in. When the AI needs input, the stream emits a pendingQuestion frame; you answer it with POST /v2/stream/ask/respond and the same open stream resumes.

What It Does

The endpoint returns a Server-Sent Events (SSE) stream that includes:

  1. A leading init frame carrying the threadId (use it for follow-ups).
  2. Processing state for SQL generation (understanding → searching → planning → generating → correcting) and execution.
  3. The generated SQL and, for SQL answers, a streamed natural-language summary_generation content block — or, for non-SQL questions, a streamed explanation content block.
  4. pendingQuestion frames when the AI pauses for clarification at the intent or sql_reasoning checkpoint.

Basic Usage

{
  "projectId": 1,
  "question": "List the top 5 states with the most customers"
}

This simplified stream shows the key phases of a /v2/stream/ask request — from the leading init frame, through understanding and generating SQL, executing it, and finally producing a streamed summary.

// Stream begins
data: { "type": "init", "threadId": "0625991d-1bba-407d-8ad4-dd0210172484", "queryId": null }

data: { "type": "message_start" }

// SQL Generation Stages
data: { "type": "state", "data": { "state": "sql_generation_start" }}
data: { "type": "state", "data": { "state": "sql_generation_understanding" }}
data: { "type": "state", "data": { "state": "sql_generation_searching" }}
data: { "type": "state", "data": { "state": "sql_generation_planning" }}
data: { "type": "state", "data": { "state": "sql_generation_generating" }}
data: { "type": "state", "data": { "state": "sql_generation_success", "sql": "SELECT ... LIMIT 5" }}

// SQL Execution
data: { "type": "state", "data": { "state": "sql_execution_start" }}
data: { "type": "state", "data": { "state": "sql_execution_end" }}

// Summary Generation (streamed content block)
data: { "type": "content_block_start", "content_block": { "type": "text", "name": "summary_generation" }}
data: { "type": "content_block_delta", "delta": { "text": "Here" }}
data: { "type": "content_block_delta", "delta": { "text": " are" }}
data: { "type": "content_block_delta", "delta": { "text": " the top 5 states by customer count." }}
data: { "type": "content_block_stop" }

// Stream ends
data: { "type": "message_stop" }

This example omits several detailed properties (such as question, rephrasedQuestion, intentReasoning, traceId, and timestamp) for clarity. Refer to the full schema for a complete breakdown of all available fields.

Request fields

FieldTypeRequiredDescription
projectIdnumberTarget project.
questionstringThe natural-language question.
threadIdstringContinue an existing thread. Supplying it also enables the single-active-turn guard (see below).
sampleSizenumberRow limit for the SQL execution preview.
languagestringLanguage used for the summary/explanation (e.g. "English"). Defaults to the project language.
returnBothSqlDialectbooleanWhen true, sql_generation_success also carries the native dialectSql. Defaults to false.
customInstructionstringExtra instruction passed to the AI for this turn.

Single-active-turn guard. When you supply a threadId, a second concurrent /v2/stream/ask for the same thread is rejected with 409 (A turn is already in progress for this thread). Omit threadId and the server generates one (returned in the init frame).

Clarification (pendingQuestionrespond)

At the intent or sql_reasoning checkpoint the AI may need you to disambiguate. Instead of failing, the stream emits a pendingQuestion frame and keeps the connection open:

data: {
  "type": "pendingQuestion",
  "data": {
    "questionId": "b8f0c1e2-...",
    "checkpoint": "intent",
    "question": "Which \"customers\" did you mean?",
    "options": [
      { "value": "all", "label": "All registered customers" },
      { "value": "active", "label": "Only active customers" }
    ],
    "selectionType": "single",
    "rationale": "The question is ambiguous about customer status.",
    "queryId": "1f0a3c7d-...",
    "threadId": "0625991d-1bba-407d-8ad4-dd0210172484"
  },
  "timestamp": 1751014955000
}

Answer it with a separate call to POST /v2/stream/ask/respond, carrying the queryId and questionId from the frame. This call returns immediately ({ "status": "accepted" }); the ask resumes on the same open SSE stream — you do not reopen /v2/stream/ask.

{
  "projectId": 1,
  "queryId": "1f0a3c7d-...",
  "questionId": "b8f0c1e2-...",
  "action": "SUBMIT",
  "answers": ["active"],
  "threadId": "0625991d-1bba-407d-8ad4-dd0210172484"
}
actionMeaning
SUBMITAnswer with answers (option values) and/or freeText.
SKIPSkip the question and let the AI proceed with its best guess.
RETRYAsk the AI to regenerate the clarification question.
CLOSECancel the clarification and end the turn.

action is case-insensitive. See the stream/ask/respond reference for the full request/response contract and error codes.

State Lifecycle

graph TD
  init["init"]
  message_start["message_start"]
  sql_start["sql_generation_start"]
  sql_understanding["sql_generation_understanding"]
  sql_searching["sql_generation_searching"]
  sql_planning["sql_generation_planning"]
  sql_generating["sql_generation_generating"]
  sql_correcting["sql_generation_correcting"]
  sql_success["sql_generation_success"]
  sql_failed["sql_generation_failed"]
  sql_stopped["sql_generation_stopped"]

  sql_exec_start["sql_execution_start"]
  sql_exec_end["sql_execution_end"]

  pending["pendingQuestion (intent / sql_reasoning)"]
  respond["POST /v2/stream/ask/respond"]

  summary_start["content_block_start: summary_generation"]
  summary_streaming["content_block_delta (text)"]
  summary_stop["content_block_stop"]

  explanation_block["content_block_*: explanation (non-SQL)"]

  error_event["type: error"]
  message_stop["message_stop"]

  %% Normal flow
  init --> message_start -->|"Trigger SQL generation"| sql_start
  sql_start --> sql_understanding --> sql_searching --> sql_planning --> sql_generating

  sql_generating -->|"Auto-correct (if needed)"| sql_correcting
  sql_correcting --> sql_success
  sql_generating --> sql_success

  sql_success -->|"Run SQL"| sql_exec_start --> sql_exec_end

  sql_exec_end -->|"Start LLM summary"| summary_start
  summary_start --> summary_streaming --> summary_stop --> message_stop

  %% Non-SQL branch
  sql_understanding -->|"Non-SQL question"| explanation_block --> message_stop

  %% Clarification checkpoints (resume on the same open stream)
  sql_understanding -.->|"Intent checkpoint"| pending
  sql_generating -.->|"SQL-reasoning checkpoint"| pending
  pending -.->|"Answer via respond"| respond
  respond -.->|"Turn resumes"| sql_generating

  %% Error flows
  sql_generating -->|"Too many corrections failed"| sql_failed -->|"Emit error event"| error_event --> message_stop
  sql_generating -->|"User canceled"| sql_stopped --> message_stop
  sql_exec_start -->|"SQL execution error"| error_event --> message_stop
  summary_start -->|"LLM failed to summarize"| error_event --> message_stop

Event Types

During a /v2/stream/ask request, the API streams a sequence of events using Server-Sent Events (SSE). Each event provides insight into the system’s current state or output.

init

  • Purpose: Leading frame of every stream. Delivers the threadId to use for follow-ups.
  • Payload: queryId is always null here — the per-turn queryId used to answer clarifications is delivered with each pendingQuestion frame instead.
{
  "type": "init",
  "threadId": "0625991d-1bba-407d-8ad4-dd0210172484",
  "queryId": null
}

message_start

  • Purpose: Indicates the start of a new streaming response.
  • Payload: Contains the timestamp when the process began.
{
  "type": "message_start",
  "timestamp": 1751014954139
}

state

  • Purpose: Describes what stage the system is in during processing.

State Lifecycle Overview

stateDescription
sql_generation_startThe system has begun processing the user’s question.
sql_generation_understandingThe AI is interpreting the question and trying to identify its intent.
sql_generation_searchingThe AI is searching relevant tables and metadata to answer the question.
sql_generation_planningA plan for how to generate SQL is being formed, including table joins or filters.
sql_generation_generatingThe SQL is being generated.
sql_generation_correctingA generated SQL failed to execute and is being corrected. (Auto-retry phase)
sql_generation_successSQL was successfully generated. The sql field will be included.
sql_generation_failedSQL generation failed. Followed by an error event.
sql_generation_stoppedSQL generation was manually canceled or interrupted (e.g. client disconnected).
sql_generation_finishedFinal state for SQL generation (used internally; usually followed by success or failed).
sql_execution_startSQL execution has started.
sql_execution_endSQL execution has completed (successfully or not).

data Field Reference ("type": "state")

🔹 sql_generation_start

FieldTypeDescription
statestring"sql_generation_start"
questionstringThe original user input.
threadIdstringUnique thread identifier.
languagestringLanguage used for summarization (e.g., "English").

🔹 SQL Generation In-Progress States
(e.g., sql_generation_understanding, searching, planning, generating, correcting)

FieldTypeDescription
statestringCurrent generation state, such as "sql_generation_searching", "sql_generation_planning", etc.
pollCountnumberNumber of polling attempts so far.
rephrasedQuestionstring | nullReformulated version of the user query.
intentReasoningstring | nullAI’s interpretation of what the user is asking.
sqlGenerationReasoningstring | nullStep-by-step reasoning of SQL generation. Appears during generating.
retrievedTablesstring[] | nullList of tables determined to be relevant.
invalidSqlstring | nullSQL that failed during correction attempts (optional).
traceIdstringFor backend debugging.

🔹 sql_generation_success

FieldTypeDescription
state"sql_generation_success"Marks the completion of SQL generation.
sqlstringThe generated SQL query.
dialectSqlstring | undefinedNative dialect SQL, included when returnBothSqlDialect is true.

🔹 sql_execution_start

FieldTypeDescription
state"sql_execution_start"SQL execution has begun.
sqlstringThe SQL query being executed.

🔹 sql_execution_end

FieldTypeDescription
state"sql_execution_end"SQL execution completed. No additional fields.

Example

data: {
  "type": "state",
  "data": {
    "state": "sql_generation_start",
    "question": "list 5 customers",
    "threadId": "0625991d-1bba-407d-8ad4-dd0210172484",
    "language": "English"
  },
  "timestamp": 1751014954142
}

data: {
  "type": "state",
  "data": {
    "state": "sql_generation_understanding",
    "pollCount": 1,
    "rephrasedQuestion": null,
    "intentReasoning": null,
    "sqlGenerationReasoning": null,
    "retrievedTables": null,
    "invalidSql": null,
    "traceId": "f218b1f7-4623-4a56-8b66-18d544797b20"
  },
  "timestamp": 1751014954165
}

data: {
  "type": "state",
  "data": {
    "state": "sql_generation_searching",
    "pollCount": 4,
    "rephrasedQuestion": "List 5 customers from the olist_customers_dataset table.",
    "intentReasoning": "User wants to retrieve specific customer data, likely using SQL query.",
    "sqlGenerationReasoning": null,
    "retrievedTables": null,
    "invalidSql": null,
    "traceId": "f218b1f7-4623-4a56-8b66-18d544797b20"
  },
  "timestamp": 1751014957183
}

data: {
  "type": "state",
  "data": {
    "state": "sql_generation_planning",
    "pollCount": 6,
    "rephrasedQuestion": "List 5 customers from the olist_customers_dataset table.",
    "intentReasoning": "User wants to retrieve specific customer data, likely using SQL query.",
    "sqlGenerationReasoning": null,
    "retrievedTables": ["olist_customers_dataset"],
    "invalidSql": null,
    "traceId": "f218b1f7-4623-4a56-8b66-18d544797b20"
  },
  "timestamp": 1751014959232
}

data: {
  "type": "state",
  "data": {
    "state": "sql_generation_generating",
    "pollCount": 9,
    "rephrasedQuestion": "List 5 customers from the olist_customers_dataset table.",
    "intentReasoning": "User wants to retrieve specific customer data, likely using SQL query.",
    "sqlGenerationReasoning": "1. **Identify the table involved**: The question asks for customer data, so the relevant table is `olist_customers_dataset`.\n\n2. **Determine the number of records needed**: The user requests 5 customers, so we need to select 5 entries from the table.",
    "retrievedTables": ["olist_customers_dataset"],
    "invalidSql": null,
    "traceId": "f218b1f7-4623-4a56-8b66-18d544797b20"
  },
  "timestamp": 1751014962254
}

data: {
  "type": "state",
  "data": {
    "state": "sql_generation_success",
    "sql": "SELECT \"o\".\"customer_id\", \"o\".\"customer_zip_code_prefix\", \"o\".\"customer_city\", \"o\".\"customer_state\" FROM \"olist_customers_dataset\" AS \"o\" LIMIT 5"
  },
  "timestamp": 1751014963263
}

data: {
  "type": "state",
  "data": {
    "state": "sql_execution_start",
    "sql": "SELECT \"o\".\"customer_id\", \"o\".\"customer_zip_code_prefix\", \"o\".\"customer_city\", \"o\".\"customer_state\" FROM \"olist_customers_dataset\" AS \"o\" LIMIT 5"
  },
  "timestamp": 1751014963263
}

data: {
  "type": "state",
  "data": {
    "state": "sql_execution_end"
  },
  "timestamp": 1751014963339
}

content_block_start

  • Purpose: Signals the beginning of a streamed text block. The name tells you which one:
    • summary_generation — a natural-language summary of the SQL result.
    • explanation — a natural-language answer to a non-SQL question (no SQL is generated in this case).
  • Payload: Describes the block (always type: "text").
{
  "type": "content_block_start",
  "content_block": {
    "type": "text",
    "name": "summary_generation"
  }
}

content_block_delta

  • Purpose: Streams the actual content in parts (e.g., the summary or explanation, chunk by chunk).
  • Payload: Includes a text_delta with the partial content.
{
  "type": "content_block_delta",
  "delta": {
    "type": "text_delta",
    "text": "São Paulo (SP) leads with"
  }
}

content_block_stop

  • Purpose: Indicates the end of a streamed content block.
{
  "type": "content_block_stop"
}

pendingQuestion

  • Purpose: The AI paused at a clarification checkpoint and needs your input before continuing. The stream stays open; answer via POST /v2/stream/ask/respond.
  • Payload:
FieldTypeDescription
questionIdstringIdentifier of this clarification question. Echo it back in respond.
checkpoint"intent" | "sql_reasoning"Where in the pipeline the AI paused.
questionstringThe clarification question to show the user.
optionsArray<{ value: string; label: string }>Selectable options (may be empty for free-text answers).
selectionType"single" | "multi"Whether one or multiple options may be selected.
rationalestring | undefinedWhy the AI is asking.
queryIdstringPer-turn query id. Pass it to respond to route the answer to this turn.
threadIdstringThe thread this turn belongs to.
{
  "type": "pendingQuestion",
  "data": {
    "questionId": "b8f0c1e2-1a2b-4c3d-9e8f-7a6b5c4d3e2f",
    "checkpoint": "intent",
    "question": "Which \"customers\" did you mean?",
    "options": [
      { "value": "all", "label": "All registered customers" },
      { "value": "active", "label": "Only active customers" }
    ],
    "selectionType": "single",
    "rationale": "The question is ambiguous about customer status.",
    "queryId": "1f0a3c7d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
    "threadId": "0625991d-1bba-407d-8ad4-dd0210172484"
  },
  "timestamp": 1751014955000
}

message_stop

  • Purpose: Marks the end of the entire stream. Includes processing duration (ms) and threadId.
{
  "type": "message_stop",
  "data": {
    "threadId": "0625991d-1bba-407d-8ad4-dd0210172484",
    "duration": 11096
  }
}

Error Handling

If an error occurs at any stage of the pipeline, the server emits a type: "error" frame (followed by a terminal message_stop). This lets clients gracefully detect and handle failures — e.g., show an error message in the UI, add retry logic, or cancel streaming.

Error Event Structure

{
  "type": "error",
  "data": {
    "code": "INVALID_SQL_ERROR",
    "error": "Unrecognized token near FROM"
  },
  "timestamp": 1751015028888
}
FieldTypeDescription
codestringMachine-readable error code (e.g. INVALID_SQL_ERROR, INTERNAL_SERVER_ERROR).
errorstringHuman-readable error message.

Additional context fields (such as traceId) may accompany data depending on the error.

Pre-stream errors. Validation failures that happen before the SSE stream opens are returned as a normal JSON error response instead of an error frame — 400 (missing projectId/question), 401 (invalid API key), 404 (project not found), or 409 (a turn is already in progress for this thread).

Body Params
integer
required
string
required
string
integer
string
boolean
Defaults to false
string
Headers
string

Comma-separated key=value pairs applied as row/column-level security session properties (e.g. region=US,tier=pro). Unknown keys are echoed back in the invalidSessionProperties response field.

string
enum
Defaults to application/json

Generated from available response content types

Allowed:
Responses

Language
Credentials
Bearer
LoadingLoading…
Response
Click Try It! to start a request and see the response here! Or choose an example:
text/event-stream
application/json