stream/interactive_ask

Creates or continues an interactive conversation and streams content using server-sent events

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

Streams interactive mode conversations with real-time AI responses and conversation management.

This endpoint enables natural, conversational interactions with your data. Unlike the standard /v2/stream/ask endpoint which focuses on SQL generation and execution, interactive mode allows the AI to engage in a more flexible conversation flow — it may ask clarifying questions, provide explanations, generate insights, or create visualizations based on the context.

This endpoint combines conversation creation and streaming into a single POST request, making it ideal for building conversational interfaces, chatbots, or interactive data exploration tools.

📌

v2 only. This endpoint is served under /api/v2 only. Charts are returned as ECharts option objects (inline in the stream) and the conversation supports a mid-stream clarification loop — the AI can pause to ask you a question and resume on the same open stream once you answer via POST /v2/stream/interactive_ask/respond. The legacy /api/v1/stream/interactive_ask variant returns Vega-Lite output and has no clarification loop.

What It Does

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

  1. A leading init frame — carries the threadId and queryId you need to drive follow-up turns and answer clarification questions.
  2. Conversation events — real-time AI responses, explanations, and content blocks (including ECharts charts) forwarded from the AI Service as they are generated.
  3. Clarification checkpointspendingQuestion frames emitted when the AI needs input before continuing.
  4. Conversation history management — automatic multi-turn thread management.
  5. A terminal done frame — signals that streaming has finished.

Stream Envelope

The response is a single SSE stream (Content-Type: text/event-stream) whose frames are newline-delimited data: {json}\n\n records. The stream is a mix of frames the API wrapper adds and raw frames forwarded verbatim from the AI Service:

FrameOriginShape
initAPI wrapper (first frame){ "type": "init", "threadId": "…", "queryId": "…" }
content_block_*, message_*, errorForwarded from AI ServiceRaw AI events (see the Event Types section)
pendingQuestionAPI wrapperClarification checkpoint (see the Clarification Loop section)
doneAPI wrapper (last frame){ "done": true }

Discriminate every frame on the JSON type field (the terminal frame instead carries done: true). Forwarded AI frames also include a leading event: <name> line; the init, pendingQuestion, and done frames are data:-only.

graph TD
  init["init { threadId, queryId }"]
  blocks["forwarded content_block_* frames<br/>(ECharts charts inline)"]
  pending["pendingQuestion (optional, repeatable)"]
  respond["POST /v2/stream/interactive_ask/respond"]
  done["done: true"]

  init --> blocks
  blocks --> pending
  pending -->|answer via respond side-channel| respond
  respond -->|conversation resumes on the same stream| blocks
  blocks --> done

Basic Usage

Initial Question

Request

{
  "projectId": 123,
  "question": "What are the top 5 states with the most customers?"
}

Response

The stream begins immediately after validation and authentication. The first frame is always the init frame; subsequent events are forwarded from the AI Service:

// Leading init frame (carries threadId + queryId)
data: {"type":"init","threadId":"0625991d-1bba-407d-8ad4-dd0210172484","queryId":"6771808b-790a-48f3-b60c-5c7e62a694f4"}

// Events forwarded from AI Service
event: message_start
data: {"type":"message_start","message":{"query_id":"6771808b-790a-48f3-b60c-5c7e62a694f4","trace_id":"a87102f7-6e27-4aed-a32d-52b444ceadac"}}

event: content_block_start
data: {"type":"content_block_start","index":0,"message":{"type":"text","content_block_label":"GREETINGS","trace_id":"a87102f7-6e27-4aed-a32d-52b444ceadac","metadata":{"visible_in_ui":true,"ui_components":["MARKDOWN","ASSISTANT"],"show_elapsed_time":false,"elapsed_time":0.0}}}

event: content_block_delta
data: {"type":"content_block_delta","index":0,"message":{"type":"text_delta","content_block_label":"GREETINGS","content":"You're all set. Let me process that for you.","trace_id":"a87102f7-6e27-4aed-a32d-52b444ceadac","metadata":{"visible_in_ui":true,"ui_components":["MARKDOWN","ASSISTANT"],"show_elapsed_time":false,"elapsed_time":0.01}}}

event: content_block_stop
data: {"type":"content_block_stop","index":0,"message":{"type":"text","trace_id":"a87102f7-6e27-4aed-a32d-52b444ceadac","content_block_label":"GREETINGS","metadata":{"show_elapsed_time":false,"elapsed_time":0.01}}}

...

// Stream ends
data: {"done": true}

Follow-up Question

Request

{
  "projectId": 123,
  "threadId": "0625991d-1bba-407d-8ad4-dd0210172484",
  "question": "What about last month?"
}

When threadId is provided, the API automatically retrieves the conversation history for that thread and includes it in the request to the AI Service, enabling context-aware responses. When threadId is omitted, a new thread is created automatically and returned in the init frame.

Response

Same SSE stream format as the initial question, with AI responses considering the previous conversation context.

Request Parameters

ParameterTypeRequiredDescription
projectIdnumberYesThe ID of the project to query.
questionstringYesThe user's question or message.
threadIdstringNoThread ID for follow-up questions. If not provided, a new thread is created automatically and returned in the init frame.
languagestringNoLanguage for responses (e.g., "English", "Traditional Chinese"). Defaults to the project language.

Headers

HeaderRequiredDescription
AuthorizationYesBearer <api-key>. A project key (sk-) or organization key (osk-).
X-Wren-Session-PropertiesNoComma-separated key=value pairs applied as row/column-level security session properties (e.g. region=US,tier=pro).

Concurrency: one active turn per thread

Only one turn may be in flight per threadId at a time. If you POST a second turn for a threadId that already has an open stream, the API responds with 409 (A turn is already in progress for this thread). Wait for the current stream to reach its done frame (or answer its pendingQuestion) before starting another turn on the same thread. A request that omits threadId always gets a fresh, unique thread and never contends.

Clarification Loop

Interactive mode may pause at a checkpoint and ask you to disambiguate before it continues. When this happens the API emits a pendingQuestion frame on the open stream and the AI Service waits for your answer.

pendingQuestion frame

{
  "type": "pendingQuestion",
  "timestamp": 1751014957183,
  "data": {
    "questionId": "b7c1e0d2-5f3a-4a2b-9c8d-0e1f2a3b4c5d",
    "checkpoint": "intent",
    "question": "Which region are you asking about?",
    "options": [
      { "value": "us", "label": "United States" },
      { "value": "eu", "label": "Europe" }
    ],
    "selectionType": "single",
    "rationale": "The question could refer to more than one region."
  }
}

Fields

FieldTypeDescription
questionIdstringIdentifier for this pending question. Pass it back to the respond endpoint.
checkpointstringWhere the AI paused: "intent" (disambiguating what you're asking) or "sql_reasoning" (confirming how the SQL should be built).
questionstringThe clarification prompt to show the user.
optionsarray of { value, label }Selectable answers. May be empty when a free-text answer is expected.
selectionType"single" | "multi"Whether one or multiple options may be selected.
rationalestring (optional)Why the AI is asking.

Answering a pending question

Answer with a side-channel call to POST /v2/stream/interactive_ask/respond — the conversation then resumes on the same still-open interactive_ask SSE stream (do not open a new stream). Use the queryId from the init frame and the questionId from the pendingQuestion frame:

{
  "projectId": 123,
  "queryId": "6771808b-790a-48f3-b60c-5c7e62a694f4",
  "questionId": "b7c1e0d2-5f3a-4a2b-9c8d-0e1f2a3b4c5d",
  "action": "SUBMIT",
  "answers": ["us"],
  "threadId": "0625991d-1bba-407d-8ad4-dd0210172484"
}

Actions

ActionDescription
SUBMITSubmit the user's answer. Requires a non-empty answers array and/or freeText. The AI records it and continues (it may ask another question or resolve).
SKIPSkip this clarification. The AI stops asking and proceeds best-effort (e.g. an ambiguous intent is treated as a SQL question).
RETRYRe-ask the question. After the server's retry cap is reached, further retries are treated as an implicit SKIP.
CLOSEAbandon the clarification and cancel the turn.

The respond call returns immediately with { "status": "accepted" }; the resumed content continues to arrive on the original interactive_ask stream. See the stream/interactive_ask/respond page for its full request/response and error semantics.

Response Format

The endpoint returns a Server-Sent Events (SSE) stream with Content-Type: text/event-stream.

Stream Events

Beyond the wrapper init / pendingQuestion / done frames, events are forwarded directly from the AI Service's conversation API. The following event types are supported:

  • message_start — Indicates the start of a new conversation response.
  • content_block_start — Beginning of a content block.
  • content_block_delta — Streaming content updates (text or JSON deltas).
  • content_block_stop — End of a content block.
  • content_block_failed — Indicates a content block failed to generate.
  • message_stop — End of the entire message stream.
  • error — Error events.

Completion Event

When the stream completes, the API sends a final event:

{
  "done": true
}

This frame is sent by the API wrapper (not the AI Service) to indicate that streaming has finished.

Event Types

init

The leading frame, added by the API wrapper. Carries the identifiers you need to answer clarifications (queryId) and to continue the conversation (threadId).

Example

{
  "type": "init",
  "threadId": "0625991d-1bba-407d-8ad4-dd0210172484",
  "queryId": "6771808b-790a-48f3-b60c-5c7e62a694f4"
}
FieldTypeDescription
threadIdstringThread identifier. Reuse it to send follow-up turns.
queryIdstringPer-turn query identifier. Pass it to the respond endpoint.

message_start

Indicates the start of a new conversation response.

Example

{
  "type": "message_start",
  "message": {
    "query_id": "6771808b-790a-48f3-b60c-5c7e62a694f4",
    "trace_id": "a87102f7-6e27-4aed-a32d-52b444ceadac"
  }
}

Fields

FieldTypeDescription
query_idstringUnique identifier for this conversation query
trace_idstringTrace ID for backend debugging

content_block_start

Signals the beginning of a content block.

Example

{
  "type": "content_block_start",
  "index": 0,
  "message": {
    "type": "text",
    "content_block_label": "GREETINGS",
    "trace_id": "a87102f7-6e27-4aed-a32d-52b444ceadac",
    "metadata": {
      "visible_in_ui": true,
      "ui_components": ["MARKDOWN", "ASSISTANT"],
      "show_elapsed_time": false,
      "elapsed_time": 0.0
    }
  }
}

Fields

FieldTypeDescription
indexnumberIndex of the content block in the message.
message.typestringContent type: "text", "tool_use", "think", or "question" (clarification).
message.content_block_labelstringLabel identifying the content block type (see the Content Block Labels section).
message.trace_idstringTrace ID for debugging.
message.metadataobjectMetadata including UI components, visibility, and timing.

content_block_delta

Streams incremental content updates from the AI. Can contain either text deltas or JSON deltas.

Example (Text Delta)

{
  "type": "content_block_delta",
  "index": 0,
  "message": {
    "type": "text_delta",
    "content_block_label": "GREETINGS",
    "content": "You're all set. Let me process that for you.",
    "trace_id": "a87102f7-6e27-4aed-a32d-52b444ceadac",
    "metadata": {
      "visible_in_ui": true,
      "ui_components": ["MARKDOWN", "ASSISTANT"],
      "show_elapsed_time": false,
      "elapsed_time": 0.01
    }
  }
}

Example (JSON Delta)

{
  "type": "content_block_delta",
  "index": 1,
  "message": {
    "type": "json_delta",
    "content_block_label": "SQL_GENERATION",
    "content": {
      "success": true,
      "sql": "SELECT ..."
    },
    "trace_id": "a87102f7-6e27-4aed-a32d-52b444ceadac",
    "metadata": {
      "visible_in_ui": true,
      "ui_components": ["PREPARATION"],
      "show_elapsed_time": true,
      "elapsed_time": 2.5
    }
  }
}

content_block_stop

Indicates the end of a content block.

Example

{
  "type": "content_block_stop",
  "index": 0,
  "message": {
    "type": "text",
    "trace_id": "a87102f7-6e27-4aed-a32d-52b444ceadac",
    "content_block_label": "GREETINGS",
    "metadata": {
      "show_elapsed_time": false,
      "elapsed_time": 0.01
    }
  }
}

content_block_failed

Indicates that a content block failed to generate.

Example

{
  "type": "content_block_failed",
  "index": 1,
  "message": {
    "type": "json_failed",
    "content_block_label": "SQL_GENERATION",
    "content": "Error message",
    "trace_id": "a87102f7-6e27-4aed-a32d-52b444ceadac",
    "metadata": {
      "visible_in_ui": true,
      "ui_components": ["PREPARATION"],
      "show_elapsed_time": true,
      "elapsed_time": 5.0
    }
  }
}

message_stop

Marks the end of the entire message stream.

Example

{
  "type": "message_stop",
  "message": {
    "query_id": "6771808b-790a-48f3-b60c-5c7e62a694f4",
    "trace_id": "a87102f7-6e27-4aed-a32d-52b444ceadac"
  }
}

error

Error events indicate failures during processing.

Example

{
  "type": "error",
  "message": {
    "query_id": "6771808b-790a-48f3-b60c-5c7e62a694f4",
    "trace_id": "a87102f7-6e27-4aed-a32d-52b444ceadac",
    "code": "SQL_GENERATION_FAILED",
    "message": "Failed to generate SQL",
    "invalid_sql": "SELECT * FROM invalid_table",
    "metadata": {
      "ui_components": ["ERROR"],
      "visible_in_ui": true
    }
  }
}

Content Block Labels

The content_block_label field identifies the type of content being streamed. The following labels are supported.

Text-Based Content Blocks

These blocks stream text content incrementally:

  • GREETINGS — Initial greeting messages.
  • MISLEADING_QUERY_ASSISTANCE — Assistance for misleading queries.
  • GENERAL_ASSISTANCE — General help and guidance.
  • USER_GUIDE_ASSISTANCE — User guide and documentation assistance.
  • DATA_EXPLORATION_ASSISTANCE — Data exploration guidance.
  • USER_CLARIFICATION_ASSISTANCE — Free-text requests for user clarification.
  • QUESTION_RECOMMENDATION_ASSISTANCE — Assistance accompanying recommended follow-up questions.
  • SQL_GENERATION_REASONING — Reasoning behind SQL generation.
  • SQL_ANSWER — Final natural-language answer over the SQL results.

Structured Content Blocks

These blocks contain structured JSON data (json_delta).

INTENT_CLASSIFICATION

Classifies user intent and provides reasoning.

{
  "intent": "TEXT_TO_SQL",
  "rephrased_question": "Show me sales data",
  "reasoning": "User wants to query sales data using SQL",
  "chart_requested": false
}

Intent Types:

  • TEXT_TO_SQL
  • CHART
  • MISLEADING_QUERY
  • GENERAL
  • USER_GUIDE
  • DATA_EXPLORATION
  • USER_CLARIFICATION
  • QUESTION_RECOMMENDATION

The classification also carries a boolean chart_requested flag alongside the intent. When chart_requested is true, the AI auto-generates an ECharts chart after answering a TEXT_TO_SQL question; the dedicated CHART intent is used when the request is purely to (re)visualize the most recent result.

INTENT_CLARIFICATION_QUESTION

A clarification checkpoint. Emitted as a question-type block; the API re-emits its content to you as a standalone pendingQuestion frame (see the Clarification Loop section).

{
  "question_id": "b7c1e0d2-5f3a-4a2b-9c8d-0e1f2a3b4c5d",
  "checkpoint": "intent",
  "question": "Which region are you asking about?",
  "options": [
    { "value": "us", "label": "United States" },
    { "value": "eu", "label": "Europe" }
  ],
  "selection_type": "single",
  "rationale": "The question could refer to more than one region."
}

HISTORICAL_QUESTION_RETRIEVAL

Contains SQL from historical questions.

{
  "sql": "SELECT ...",
  "type": "view",
  "viewId": "optional-view-id"
}

SQL_PAIRS_RETRIEVAL

Contains retrieved SQL pairs from the knowledge base.

{
  "sql_pairs": [
    {
      "question": "What are the top products?",
      "sql": "SELECT ..."
    }
  ]
}

INSTRUCTIONS_RETRIEVAL

Contains retrieved instructions from the knowledge base. The AI also emits scope-specific variants — INSTRUCTIONS_RETRIEVAL_FOR_ANSWER (instructions applied when writing the answer) and INSTRUCTIONS_RETRIEVAL_FOR_CHART (instructions applied when generating a chart) — which share this shape.

{
  "instructions": [
    {
      "instruction": "Always use UTC timezone",
      "question": "What time is it?",
      "instruction_id": "inst-123"
    }
  ]
}

DB_SCHEMA_RETRIEVAL

Contains retrieved database schema information.

Success:

{
  "success": true,
  "retrieved_tables": ["customers", "orders", "products"]
}

Failure:

{
  "success": false,
  "error": {
    "code": "NO_RELEVANT_DATA",
    "message": "No relevant tables found"
  }
}

SQL_GENERATION

Contains the generated SQL query.

Success:

{
  "success": true,
  "sql": "SELECT customer_id, SUM(amount) FROM orders GROUP BY customer_id"
}

Failure:

{
  "success": false,
  "error": {
    "code": "GENERATION_FAILED",
    "message": "Unable to generate SQL"
  }
}

SQL_DIAGNOSIS

Contains diagnosis reasoning for SQL issues.

{
  "reasoning": "The SQL query failed because table 'invalid_table' does not exist"
}

SQL_CORRECTION

Contains corrected SQL after diagnosis.

Success:

{
  "success": true,
  "sql": "SELECT * FROM valid_table"
}

Failure:

{
  "success": false,
  "error": {
    "code": "CORRECTION_FAILED",
    "message": "Unable to correct SQL"
  }
}

SQL_EXECUTOR

Indicates SQL execution status.

Success:

{
  "success": true
}

Failure:

{
  "success": false,
  "error": {
    "code": "QUERY_FAILED",
    "message": "SQL execution failed"
  }
}

FIX_SQL

Contains SQL fix information.

{
  "data": {
    "code": "NO_RELEVANT_SQL",
    "message": "No relevant SQL found",
    "invalid_sql": "SELECT * FROM invalid_table"
  }
}

CHART_GENERATION

Contains the generated chart schema. In interactive mode this is an ECharts option object. Bindings are declared via dataset.dimensions + series.encode; the backend injects the full dataset.source rows.

{
  "chart_schema": {
    "title": { "text": "Revenue by Region", "left": "center" },
    "tooltip": { "trigger": "axis", "axisPointer": { "type": "shadow" } },
    "dataset": { "dimensions": ["region", "revenue"] },
    "xAxis": { "type": "category" },
    "yAxis": { "type": "value", "name": "Revenue (USD)" },
    "series": [
      { "name": "Revenue", "type": "bar", "encode": { "x": "region", "y": "revenue" } }
    ]
  }
}

CHART_ADJUSTMENT

Contains an adjusted ECharts chart schema (same shape as CHART_GENERATION), produced when the user asks to modify an existing chart.

{
  "chart_schema": {
    "title": { "text": "Daily Active Users", "left": "center" },
    "tooltip": { "trigger": "axis" },
    "dataset": { "dimensions": ["day", "dau"] },
    "xAxis": { "type": "time" },
    "yAxis": { "type": "value" },
    "series": [
      { "name": "DAU", "type": "line", "smooth": false, "encode": { "x": "day", "y": "dau" } }
    ]
  }
}

DATA_PREVIEW

Contains a preview of the result to render.

Chart Preview:

{
  "type": "CHART",
  "payload": {
    "title": "Sales Chart",
    "sql": "SELECT ...",
    "chart_schema": { "series": [ { "type": "bar", "encode": { "x": "region", "y": "revenue" } } ] }
  }
}

Table Preview:

{
  "type": "TABLE",
  "payload": {
    "title": "Sales Data",
    "sql": "SELECT ..."
  }
}

SQL Preview:

{
  "type": "SQL",
  "payload": {
    "title": "Generated SQL",
    "sql": "SELECT ..."
  }
}

INSTRUCTION_RECOMMENDATION

Contains recommended instructions.

{
  "instruction_recommendation_info": [
    {
      "uuid": "rec-123",
      "instruction_type": "SQL_PAIR",
      "instruction": "Use customer_id for joins",
      "human_readable_explanation": "This will improve query performance"
    }
  ]
}

QUESTION_RECOMMENDATION

Contains recommended questions the user can explore next, each with a category.

{
  "recommend_instruction": [
    { "question": "What was the total revenue generated by each region last year?", "category": "Descriptive Questions" },
    { "question": "How do customer preferences differ between age groups?", "category": "Segmentation Questions" }
  ]
}

RECOMMENDED_ACTIONS

Contains recommended follow-up actions. Each action carries a label and a templates array of suggested prompts.

{
  "actions": [
    {
      "label": "Draw a chart",
      "templates": ["Draw a chart for this result"]
    }
  ]
}

AI_EVENT_COST

⚠️ Note: This event is for internal usage within the WrenAI system only and is used for monitoring and analysis purposes. It does not directly correspond to the actual usage or charges billed to you, and should not be treated as a representation of your final cost.

Non-Chargeable Events

The following event names do not result in credit charges:

  • NO_DB_SCHEMAS
  • SQL_TIMEOUT
  • SQL_PERMISSION_DENIED
  • SQL_CORRECTION_FAILED
  • SQL_QUERY_ALL_FAILED
  • CHART_INTENT_FAILED
  • CHART_TYPE_FAILED
  • CHART_FAILED
  • OTHERS

CONVERSATION_HISTORY_PAYLOAD

Contains the conversation history payload for persistence. This is used internally to maintain conversation context and is included in API history.

Structure

The structure varies depending on the conversation type and content generated. The request object always contains the user's query, while response contains different fields based on what was generated.

Example 1: SQL Query Response

{
  "type": "content_block_delta",
  "message": {
    "type": "json_delta",
    "content_block_label": "CONVERSATION_HISTORY_PAYLOAD",
    "content": {
      "request": {
        "query": "What are the top 5 products?"
      },
      "response": {
        "intent": "TEXT_TO_SQL",
        "sql": "SELECT product_id, SUM(quantity) FROM orders GROUP BY product_id LIMIT 5",
        "sql_reasoning": "Aggregating order quantities by product",
        "text": "Here are the top 5 products by quantity sold...",
        "chart_schema": null
      },
      "metadata": {
        "failed_sql": null
      }
    }
  }
}

Example 2: Chart Response (ECharts)

{
  "type": "content_block_delta",
  "message": {
    "type": "json_delta",
    "content_block_label": "CONVERSATION_HISTORY_PAYLOAD",
    "content": {
      "request": {
        "query": "how many rows do I have in events, please draw me a chart"
      },
      "response": {
        "intent": "TEXT_TO_SQL",
        "sql": "SELECT COUNT(*) AS \"row_count\" FROM \"events\"",
        "sql_reasoning": null,
        "text": null,
        "chart_schema": {
          "title": { "text": "Row count in events table", "left": "center" },
          "dataset": { "dimensions": ["row_count"] },
          "series": [ { "type": "bar", "encode": { "y": "row_count" } } ]
        }
      },
      "metadata": {
        "failed_sql": null
      }
    }
  }
}

Fields

FieldTypeDescription
request.querystringThe user's question.
response.intentstring (optional)Intent classification (e.g., "TEXT_TO_SQL").
response.sqlstring (optional)Generated SQL query, if applicable.
response.sql_reasoningstring (optional)Reasoning behind SQL generation.
response.textstring (optional)Text explanation or answer.
response.chart_schemaobject (optional)Chart schema (ECharts option object) if a chart was generated.
metadata.failed_sqlstring (optional)The SQL that failed, when applicable.

Note: All fields in response are optional. The structure varies based on the conversation type and what content was generated; only relevant fields are populated.

Body Params
integer
required
string
required
string

Provide to continue a conversation / follow up.

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