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:
| Header | Meaning |
|---|---|
x-project-ids | comma-separated project ids — the agent can reach every dataset inside |
x-dataset-ids | comma-separated dataset ids (or gp_… table names) |
x-view-ids | comma-separated view ids |
x-chat-id | an 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 field | Meaning |
|---|---|
messages | the conversation so far — an array of {role, content} (required) |
snapshot_version | query 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:
type | Carries |
|---|---|
data-chat-created | the new chat's chatId — the first line of a fresh conversation |
text-start / text-delta / text-end | the answer prose, streamed token by token in delta |
data-sql-query | the SQL the agent ran (emitted pending then success, with any snapshotVersion) |
data-datasets-used | which datasets the answer drew on |
data-citations | source documents backing the answer, when any |
tool-input-available / tool-output-available | the agent's tool calls, as they happen |
data-heartbeat | a keep-alive; ignore it |
finish | terminal — 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 + path | Effect |
|---|---|
GET /chat | list your chats (limit, page) |
POST /chat/create | create an empty chat with context attached, without running a turn |
GET /chat/{chatID} | chat details |
GET /chat/{chatID}/messages | the full message history |
PUT /chat/{chatID}/title | rename it |
PUT /chat/{chatID}/visibility | set 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.