> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bigdata.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Streaming Responses

> How the Bigdata.com Research Agent and Workflows stream results, what each message type means, and how to build a robust streaming handler.

Both the Research Agent (`/v1/research-agent`) and the Workflows execution endpoint
(`/v1/workflow/execute`) return a single, long-lived HTTP response that streams a
sequence of typed events using Server-Sent Events (SSE). This page is the canonical
reference for that stream: the event format, every public message type, the typical
order of events in a request, and a complete handler you can copy into your codebase.

## The SSE format

Each event is a single line beginning with `data: `, followed by a JSON document
terminated by a newline:

```
data: {"chat_id": "abc123", "message": {"type": "THINKING", "content": "Analyzing..."}}\n
data: {"chat_id": "abc123", "message": {"type": "ANSWER", "content": "NVIDIA's"}}\n
data: {"chat_id": "abc123", "message": {"type": "ANSWER", "content": " gross margin"}}\n
...
data: {"chat_id": "abc123", "message": {"type": "COMPLETE", "consumption": [...]}}\n
```

Lines that do not start with `data: ` are heartbeats or comments and should be ignored.
The stream ends after a `COMPLETE` (or `ERROR`) event.

<Note>
  The Research Agent and Workflows use slightly different envelopes:

  * **Research Agent** wraps the event in a `message` field:
    `{"chat_id": "...", "message": {"type": "...", ...}}`
  * **Workflows** wraps it in a `delta` field:
    `{"request_id": "...", "execution_id": "...", "delta": {"type": "...", ...}}`

  Examples in this guide use the Research Agent envelope. For Workflows, read the
  typed event from `event["delta"]` instead of `event["message"]`.
</Note>

## Message types

Eleven public message types may appear in the stream. The `type` field is the only
field guaranteed to be present on every message and is the discriminator your handler
should dispatch on.

| Type                | Purpose                                          | Key fields                                          |
| ------------------- | ------------------------------------------------ | --------------------------------------------------- |
| `PLANNING`          | Research plan progress                           | `plan.title`, `plan.steps[]` with per-step `status` |
| `THINKING`          | Internal reasoning text                          | `content`                                           |
| `ACTION`            | The agent is invoking a tool                     | `tool_name`, `tool_arguments`                       |
| `AUDIT`             | Detailed trace of a tool's execution             | `audit_traces[]`                                    |
| `GROUNDING`         | Citations linking spans of the answer to sources | `references[]`                                      |
| `ANSWER`            | A chunk of the final answer text                 | `content`                                           |
| `STRUCTURED_OUTPUT` | Extracted structured data                        | `content`, `json_schema`                            |
| `LLM_RETRY`         | The agent is retrying a transient LLM failure    | `message`                                           |
| `TOOL_ERROR`        | A specific tool failed (often recoverable)       | `error`, `tool_name`                                |
| `ERROR`             | Unrecoverable error; the stream terminates       | `error`                                             |
| `COMPLETE`          | End of stream                                    | `consumption[]`, `checkpoint_id`                    |

Every message also carries an optional `message_id` (groups related chunks; see below)
and a `role` (defaults to `"assistant"`; sub-agents may use other role names).

## Typical lifecycle

A request that runs to completion emits events in roughly this order. Some types are
optional and depend on the request configuration.

```
PLANNING            (if a research plan is used or generated)
  -> THINKING       (reasoning between actions)
  -> ACTION         (tool call begins)
  -> AUDIT          (tool result detail)
  -> (loop: more THINKING / ACTION / AUDIT cycles as needed)
GROUNDING           (one or more, referencing spans of the upcoming ANSWER)
ANSWER              (streamed in chunks; concatenate them in order)
STRUCTURED_OUTPUT   (only if the request included structured_output_schema)
COMPLETE            (carries final consumption and checkpoint_id)
```

A request that hits a recoverable problem may also emit `LLM_RETRY` (informational,
agent retries automatically) or `TOOL_ERROR` (per-tool failure; the agent may try a
different approach). An unrecoverable failure emits `ERROR` and terminates the stream
without a `COMPLETE` event.

## Per-type reference

### PLANNING

Emitted when the agent's research plan changes. The full plan is sent each time;
clients should replace any previously displayed plan with the latest version rather
than diffing.

```json theme={null}
{
  "type": "PLANNING",
  "plan": {
    "title": "Credit Analysis Framework",
    "steps": [
      {"description": "Analyze business model", "status": "COMPLETED"},
      {"description": "Review financial statements", "status": "IN_PROGRESS"},
      {"description": "Assess liquidity", "status": "NOT_STARTED"}
    ]
  }
}
```

Step statuses: `NOT_STARTED`, `IN_PROGRESS`, `COMPLETED`, `SKIPPED`, `FAILED`.

### THINKING

The agent's intermediate reasoning between tool calls. `content` may be a partial
chunk; concatenate consecutive chunks that share the same `message_id` to assemble a
single reasoning block.

```json theme={null}
{
  "type": "THINKING",
  "message_id": "think-1",
  "content": "I need to retrieve the latest 10-K filing before..."
}
```

THINKING is informational. Many integrations suppress it from the user-facing UI and
show only ANSWER content.

### ACTION

Notification that the agent is calling a tool. Useful for showing per-tool progress
in the UI (e.g., "Searching for earnings transcripts...").

```json theme={null}
{
  "type": "ACTION",
  "tool_name": "search",
  "tool_arguments": {"query": "NVIDIA Q2 FY26 earnings"}
}
```

The corresponding result arrives later as an `AUDIT` message.

### AUDIT

Detailed traces of tool executions. Each entry in `audit_traces` carries an
`audit_type` discriminator and a `tool_id` that matches references in subsequent
`GROUNDING` messages.

```json theme={null}
{
  "type": "AUDIT",
  "audit_traces": [
    {
      "audit_type": "SearchAuditV1",
      "tool_id": "audit-42",
      "query": {"text": "NVIDIA Q2 FY26 revenue"},
      "results": [
        {"type": "CQS", "values": [{"id": "doc-1", "hd": "NVIDIA 10-Q", "...": "..."}]}
      ]
    }
  ]
}
```

Audit trace types include `SearchAuditV1`, `MarkdownAuditV1`, `StructuredReportAuditV1`,
`SubAgentStartedAuditV1`, and `SubAgentCompletedAuditV1`. The full schema for each is
defined in the OpenAPI specs.

### GROUNDING

Citations linking spans of the upcoming `ANSWER` text to their sources. Each
reference carries `start` and `end` character offsets into the cumulative answer
text. See the [Grounding and citations](/how-to-guides/agents/concepts/grounding-and-citations)
guide for the full citation-rendering pattern, including the buffering requirement.

```json theme={null}
{
  "type": "GROUNDING",
  "references": [
    {
      "start": 120,
      "end": 175,
      "tool_name": "search",
      "audit_id": "audit-42",
      "source": {
        "type": "BIGDATA",
        "id": "doc-1",
        "hd": "NVIDIA 10-Q Filing",
        "src_name": "SEC EDGAR",
        "ts": "2026-04-15T00:00:00Z"
      }
    }
  ]
}
```

### ANSWER

A chunk of the final answer text. Multiple `ANSWER` events fire as the model
generates output. Concatenate `content` values in arrival order to reassemble the
full answer. Grounding offsets index into this cumulative concatenated text.

```json theme={null}
{
  "type": "ANSWER",
  "message_id": "ans-1",
  "content": " The Data Center segment contributed $41.1 billion in"
}
```

### STRUCTURED\_OUTPUT

Only emitted when the request included a `structured_output_schema`. Fires once,
after the final `ANSWER` chunk, with the extracted JSON. See the request schema for
constraints on the input schema (top-level must be an object or list-of-objects).

```json theme={null}
{
  "type": "STRUCTURED_OUTPUT",
  "json_schema": {"type": "object", "properties": {"...": "..."}},
  "content": {"company": "NVIDIA", "revenue_q2_fy26": 46.74}
}
```

### LLM\_RETRY

Informational: the agent is retrying a transient LLM failure (rate limit, timeout,
etc.). No client action is required; the agent will continue automatically. Log if
useful for observability.

```json theme={null}
{
  "type": "LLM_RETRY",
  "message": "Retrying after upstream rate limit"
}
```

### TOOL\_ERROR

A specific tool failed. The agent may recover by calling a different tool or
synthesizing without that source. The stream continues. See
[Error handling](/how-to-guides/agents/concepts/error-handling) for guidance on what to
surface to users.

```json theme={null}
{
  "type": "TOOL_ERROR",
  "tool_name": "search",
  "error": "Upstream search service timed out"
}
```

### ERROR

Unrecoverable. The stream terminates without a `COMPLETE` event. Surface this to
the user.

```json theme={null}
{
  "type": "ERROR",
  "error": "Request failed: invalid checkpoint id"
}
```

### COMPLETE

End of stream. Carries the final token consumption breakdown and, for Research Agent
requests, a `checkpoint_id` that can be passed as `from_checkpoint_id` in a
follow-up request. See [Conversation continuity](/how-to-guides/agents/concepts/conversation-continuity).

```json theme={null}
{
  "type": "COMPLETE",
  "checkpoint_id": "ckpt-789",
  "consumption": [
    {"type": "base", "input_tokens": 12450, "output_tokens": 3120, "cached_tokens": 800},
    {"type": "pro", "input_tokens": 0, "output_tokens": 0, "cached_tokens": 0}
  ]
}
```

## `message_id` and chunk grouping

The optional `message_id` field groups chunks that belong to a single logical unit.
The most common case is `ANSWER`: a long answer arrives as many small `ANSWER`
events, all sharing the same `message_id`. Clients can use the id to detect a new
answer turn versus a continuation of the current one. For most use cases, simply
concatenating consecutive `ANSWER` chunks in arrival order works.

## Canonical Python streaming handler

The handler below dispatches on `type`, reassembles the answer text, collects
grounding references, and prints a useful per-event log. It is the reference
implementation the rest of the docs build on.

```python theme={null}
import json
import requests


def stream_research_agent(api_key: str, message: str, research_effort: str = "standard"):
    """Stream a Research Agent request and return (answer_text, references, consumption)."""
    endpoint = "https://agents.bigdata.com/v1/research-agent"
    headers = {"X-API-KEY": api_key, "Content-Type": "application/json"}
    payload = {"message": message, "research_effort": research_effort}

    answer_text = ""
    references = []
    consumption = []

    with requests.post(endpoint, headers=headers, json=payload, stream=True, timeout=300) as r:
        r.raise_for_status()
        for raw_line in r.iter_lines(decode_unicode=True):
            if not raw_line or not raw_line.startswith("data: "):
                continue

            try:
                event = json.loads(raw_line[6:])
            except json.JSONDecodeError:
                # Malformed line; skip rather than abort the stream.
                continue

            msg = event.get("message", {})
            msg_type = msg.get("type")

            if msg_type == "PLANNING":
                plan = msg.get("plan", {})
                print(f"[plan] {plan.get('title')}: {len(plan.get('steps', []))} steps")

            elif msg_type == "THINKING":
                # Usually suppressed from end-user UIs.
                pass

            elif msg_type == "ACTION":
                print(f"[action] {msg.get('tool_name')}")

            elif msg_type == "AUDIT":
                for trace in msg.get("audit_traces", []):
                    print(f"[audit] {trace.get('audit_type')} {trace.get('tool_id')}")

            elif msg_type == "GROUNDING":
                references.extend(msg.get("references", []))

            elif msg_type == "ANSWER":
                chunk = msg.get("content", "")
                answer_text += chunk
                print(chunk, end="", flush=True)

            elif msg_type == "STRUCTURED_OUTPUT":
                print(f"\n[structured] {json.dumps(msg.get('content'))}")

            elif msg_type == "LLM_RETRY":
                # Informational; agent retries automatically.
                print(f"[retry] {msg.get('message')}")

            elif msg_type == "TOOL_ERROR":
                # Recoverable; log but continue consuming the stream.
                print(f"[tool-error] {msg.get('tool_name')}: {msg.get('error')}")

            elif msg_type == "ERROR":
                # Unrecoverable; the stream terminates after this event.
                raise RuntimeError(f"Research agent error: {msg.get('error')}")

            elif msg_type == "COMPLETE":
                consumption = msg.get("consumption", [])
                print("\n[complete]")

            else:
                # Forward-compatible: ignore unknown message types.
                pass

    return answer_text, references, consumption
```

For Workflows, the same handler works after two changes: post to
`https://agents.bigdata.com/v1/workflow/execute` with a workflow payload, and read
the typed event from `event["delta"]` instead of `event["message"]`.

## Forward compatibility

New message types may be added in future API versions. Clients should treat unknown
`type` values as informational and continue processing the stream rather than
raising. The `else` branch in the handler above demonstrates this pattern.

## Next steps

<CardGroup cols={2}>
  <Card title="Grounding and citations" icon="quote-right" href="/how-to-guides/agents/concepts/grounding-and-citations">
    How GROUNDING references map to answer text spans, and how to render inline citations.
  </Card>

  <Card title="Error handling" icon="triangle-exclamation" href="/how-to-guides/agents/concepts/error-handling">
    The difference between ERROR, TOOL\_ERROR, and LLM\_RETRY, plus HTTP-level failure modes.
  </Card>

  <Card title="Conversation continuity" icon="comments" href="/how-to-guides/agents/concepts/conversation-continuity">
    Resuming Research Agent chats with checkpoints and Workflows runs with execution IDs.
  </Card>

  <Card title="Research Agent quickstart" icon="rocket" href="/getting-started/quickstart_guide_research_agent">
    Build your first Research Agent request end-to-end.
  </Card>
</CardGroup>
