Menu
On this page

API & MCP

Chat API & streaming

The Chat API drives the same SQL agent the app uses: you send a question with some dataset or project context, and the agent searches the schema, writes and self-corrects SQL, runs it, and streams back a grounded answer. This page focuses on the streaming endpoint — the flagship — and the supporting chat-management routes.

Choose context with headers

Every turn needs to know which data it can query. Unlike the rest of the API, chat context is passed in headers, not the body:

HeaderMeaning
x-project-idscomma-separated project ids — the agent can reach every dataset inside
x-dataset-idscomma-separated dataset ids (or gp_… table names)
x-view-idscomma-separated view ids
x-chat-idan existing chat id, to continue the conversation

At least one of project / dataset / view ids is required. Omit x-chat-id to start a fresh conversation — the first stream event returns the new chat's id.

Stream an answer — POST /chat/stream

This is the endpoint to use. It streams the answer as it's produced, exactly like the app's chat panel.

curl -N -X POST "https://<your-gopie-host>/v1/api/chat/stream" \
  -H "Authorization: Bearer gp_pub_…" \
  -H "Content-Type: application/json" \
  -H "x-dataset-ids: gp_DzuutVJef9rSG" \
  -d '{"messages": [{"role": "user", "content": "What are the top products by revenue?"}]}'
Body fieldMeaning
messagesthe conversation so far — an array of {role, content} (required)
snapshot_versionquery datasets as of a past version

Understand the stream

The response is a stream of newline-delimited JSON — one JSON object per line (the Vercel AI SDK UI-message format, signalled by the x-vercel-ai-ui-message-stream: v1 response header). Each line has a type; you reassemble the answer by handling the types you care about and ignoring the rest.

The ones that matter:

typeCarries
data-chat-createdthe new chat's chatId — the first line of a fresh conversation
text-start / text-delta / text-endthe answer prose, streamed token by token in delta
data-sql-querythe SQL the agent ran (emitted pending then success, with any snapshotVersion)
data-datasets-usedwhich datasets the answer drew on
data-citationssource documents backing the answer, when any
tool-input-available / tool-output-availablethe agent's tool calls, as they happen
data-heartbeata keep-alive; ignore it
finishterminal — carries token counts, model, and latency in messageMetadata

A typical turn:

{"type":"data-chat-created","data":{"chatId":"f47ac10b-…"}}
{"type":"data-sql-query","id":"sql-1","data":{"query":"SELECT product, SUM(revenue) AS total FROM gp_DzuutVJef9rSG GROUP BY 1 ORDER BY 2 DESC LIMIT 10","status":"pending"}}
{"type":"text-start","id":"text-1"}
{"type":"text-delta","id":"text-1","delta":"The top product is "}
{"type":"text-delta","id":"text-1","delta":"the 27-inch Monitor at $2,490."}
{"type":"text-end","id":"text-1"}
{"type":"data-sql-query","id":"sql-1","data":{"query":"SELECT product, SUM(revenue) …","status":"success"}}
{"type":"data-datasets-used","data":{"datasets":["Orders"]}}
{"type":"finish","messageMetadata":{"totalTokens":1420,"inputTokens":380,"outputTokens":1040,"model":"claude-sonnet","latencyMs":2340}}

A turn runs to a wall-clock limit (three minutes by default). If it's exceeded, or if the LLM is unavailable, the stream ends with an error notification instead of a finish. Closing the connection cancels the turn server-side — there's no orphaned work.

Resume an interrupted stream — GET /chat/stream/resume

If a stream drops mid-turn (a flaky network, a reconnect), reattach with the chat id:

curl -N "https://<your-gopie-host>/v1/api/chat/stream/resume?chat_id=f47ac10b-…" \
  -H "Authorization: Bearer gp_pub_…"

You'll receive the buffered part of the turn so far, then the live tail. Resume is best-effort and in-memory — if the turn already finished, the buffer expired, or you're talking to a different server replica, you get 204 No Content (nothing to resume). Persistence happens on the original POST; a resume never re-saves.

A note on POST /chat/completions

There's also an OpenAI-compatible POST /chat/completions that streams chat.completion.chunk deltas ending in data: [DONE]. It exists for tools that speak the OpenAI wire format. For GoPie-native clients, prefer /chat/stream above — it carries the richer typed parts (SQL, citations, datasets used) that /completions flattens away. One caveat: snapshot_version is honoured only on /stream, not /completions.

Manage chats

Method + pathEffect
GET /chatlist your chats (limit, page)
POST /chat/createcreate an empty chat with context attached, without running a turn
GET /chat/{chatID}chat details
GET /chat/{chatID}/messagesthe full message history
PUT /chat/{chatID}/titlerename it
PUT /chat/{chatID}/visibilityset private, public, or organization
DELETE /chat/{chatID}delete it (204)

Reading a chat's details or messages requires that you created it or that its visibility is public — otherwise 403. The visibility model and its caveats are covered in the chat docs.