Skip to main content
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.
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"].

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.
TypePurposeKey fields
PLANNINGResearch plan progressplan.title, plan.steps[] with per-step status
THINKINGInternal reasoning textcontent
ACTIONThe agent is invoking a tooltool_name, tool_arguments
AUDITDetailed trace of a tool’s executionaudit_traces[]
GROUNDINGCitations linking spans of the answer to sourcesreferences[]
ANSWERA chunk of the final answer textcontent
STRUCTURED_OUTPUTExtracted structured datacontent, json_schema
LLM_RETRYThe agent is retrying a transient LLM failuremessage
TOOL_ERRORA specific tool failed (often recoverable)error, tool_name
ERRORUnrecoverable error; the stream terminateserror
COMPLETEEnd of streamconsumption[], 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.
{
  "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.
{
  "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…”).
{
  "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.
{
  "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 guide for the full citation-rendering pattern, including the buffering requirement.
{
  "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.
{
  "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).
{
  "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.
{
  "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 for guidance on what to surface to users.
{
  "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.
{
  "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.
{
  "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.
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

Grounding and citations

How GROUNDING references map to answer text spans, and how to render inline citations.

Error handling

The difference between ERROR, TOOL_ERROR, and LLM_RETRY, plus HTTP-level failure modes.

Conversation continuity

Resuming Research Agent chats with checkpoints and Workflows runs with execution IDs.

Research Agent quickstart

Build your first Research Agent request end-to-end.