Skip to main content
Tools surface structured results as durable tool_result frames. Richer views can also emit a panel_open frame, while large or persisted payloads are retrieved through the artifacts API.
Each tool_result and any following panel_open arrives before the terminal message frame, wrapped in the durable WebSocket envelope.

Read the tool-result envelope

Normal tool results use this shape:
The tool payload is frame.data.output, not frame.data itself. tool_call_id, depth, and skill are correlation metadata and can be absent. Saved-query turns are the compatibility exception: their synthetic tool_result.data is the bare legacy payload {rows, row_count, truncated, note?} and has no output wrapper. Normalize both forms before rendering:

SQL tables

Successful SQL results have output.kind: "table":
columns and rows are parallel: each row is a value array in column order. type_hint is a renderer-friendly coarse type; sql_type can be absent. row_count is the number of returned rows, and truncated: true means more rows matched upstream. Values are unformatted and can include null. Recoverable failures use kind: "error"; access-control refusals use kind: "denied". Inspect kind before assuming table fields exist, and display the returned error or reason without retrying denied queries from the client.

Saved-query result

The first result for a saved-query turn uses rows as objects for backward compatibility:
When truncated is true, an explanatory note can also be present.

Charts

The chart tool returns a renderer-neutral chart spec capped at 2,000 rows inline and persists the fuller spec produced from the scoped query as an artifact:
Supported chart_type values are bar, line, pie, scatter, area, and histogram. chart_spec.data is columnar (columns plus 2D rows), not an array of row objects. The inline chart_spec.data.rows array contains at most 2,000 rows. When more rows were available, it contains the first 2,000 and chart_spec.data.truncated is true. That flag can also be true when the scoped query itself reached an upstream result limit. The chart_spec artifact is persisted before the inline row cap is applied, so its payload can contain more than 2,000 rows. Inspect the artifact payload’s data.truncated value to distinguish upstream query truncation: true there means the persisted query result was also truncated, while false means the artifact contains the full result returned for that chart. The artifact is fuller than the inline response, not an unbounded export. The current artifact_url value is empty; construct /v1/artifacts/{artifact_id} from artifact_id. Render the inline spec immediately or retrieve the fuller spec later with Get Artifact and Get Artifact Payload. Generic analytics agents can instead produce a rendered HTML artifact:
This payload is also fetched through /v1/artifacts/{artifact_id} and /payload. The external API has no /v1/plots route.

Full table exports

Full table exports and the row_count / truncated artifact-status fields are in a forthcoming API deployment. The currently deployed API revision does not expose this additive contract yet.
When the agent exports a complete SQL result set, it returns an XLSX artifact reference rather than putting the rows on the stream:
The export has its own 250,000-row ceiling. Its truncated flag describes that export ceiling, not the much smaller inline SQL preview cap. Poll Get Artifact if needed, then download /v1/artifacts/{artifact_id}/payload.

Typed panels

Schedules, editable tables, media grids, decks, and exports can be persisted as panels and announced with panel_open after the producing tool result:
Fetch full state with Get Panel using panel_id. entity_id, entity_version, tool_call_id, and chat_message_id can be null.

Handling example

All artifact, panel, chat, and job reads on the external API are scoped to the authenticated member and return 404 rather than disclose another member’s resources.