API v2 streaming format
How API v2 streams chat responses: bare data: lines carrying text-delta parts, a terminal finish, and a final data: [DONE]. With a parsing example.
View as MarkdownWhen you call chat with streaming enabled (the default), the response is text/event-stream. Unlike the v1 stream, v2 uses the AI-SDK data-stream convention: each line is a bare data: carrying a JSON object (no event: names), and the stream ends with a literal data: [DONE].
Frame types
| type | Fields | Meaning |
|---|---|---|
| text-delta | text | A chunk of the assistant's reply. Concatenate text across frames to build the full answer. |
| error | errorText | A failure occurred mid-stream. The stream still terminates with [DONE]. |
| finish | finishReason, metadata | Terminal metadata frame: finishReason is stop (or tool-calls when the agent is awaiting a tool result), and metadata carries messageId, conversationId, userId, userMessageId, and usage.credits. |
After the finish frame, the server writes one last line — the literal string [DONE] — and closes the connection.
Raw stream
data: {"type":"text-delta","text":"Your "}
data: {"type":"text-delta","text":"order shipped yesterday."}
data: {"type":"finish","finishReason":"stop","metadata":{"userMessageId":"msg_788","conversationId":"456","userId":"user_8821","messageId":"msg_789","usage":{"credits":1}}}
data: [DONE]Parsing the stream
Split on newlines, strip the data: prefix, stop at [DONE], and JSON-parse everything else:
const res = await fetch(
"https://app.bookbag.ai/api/v2/agents/123/chat",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BOOKBAG_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ message: "Where is my order?", userId: "user_8821" }),
}
);
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let answer = "";
for (;;) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop();
for (const line of lines) {
if (!line.startsWith("data: ")) continue;
const payload = line.slice(6);
if (payload === "[DONE]") continue;
const frame = JSON.parse(payload);
if (frame.type === "text-delta") answer += frame.text;
else if (frame.type === "finish") console.log("done", frame.metadata);
else if (frame.type === "error") throw new Error(frame.errorText);
}
}
console.log(answer);Buffer across reads. A single network chunk may contain a partial line; keep the trailing fragment in buffer and only parse complete lines.
Set "stream": false in the request body to get a single JSON response instead. See Chat with an agent (v2).