API REFERENCE
Streaming responses
Receive a chat completion as server-sent events, ending with a usage frame and `data: [DONE]`.
Set stream to true on POST /v1/chat/completions. The response is text/event-stream: one data: frame per chunk, then a final frame, then data: [DONE].
STREAM
data: {"id":"85fef9589fba4d84af7a76e0a4581c6b","object":"chat.completion.chunk","created":1793282257,"model":"z1","choices":[{"index":0,"delta":{"content":"o"},"finish_reason":null}]} data: {"id":"85fef9589fba4d84af7a76e0a4581c6b","object":"chat.completion.chunk","created":1793282257,"model":"z1","choices":[{"index":0,"delta":{"content":"k"},"finish_reason":"stop"}]} data: {"id": "85fef9589fba4d84af7a76e0a4581c6b", "object": "chat.completion.chunk", "created": 1793282257, "model": "z1", "choices": [], "usage": {"prompt_tokens": 11, "completion_tokens": 2, "total_tokens": 13, "cost_usd": 2.85e-05, "cost_provenance": "family_price", "rate_id": "commercial-v1-2026-10-25"}} data: [DONE]| Frame | Shape |
|---|---|
| Content chunk | choices[0].delta.content carries text. finish_reason is null until the last chunk, then stop. |
| Usage frame | choices: [] and a usage object with prompt_tokens, completion_tokens, total_tokens. On Tessen's families it also carries cost_usd (the settled cost), cost_provenance and rate_id (the price version). It is sent whether or not you ask for it. |
| Terminator | data: [DONE] |
Note
Reasoning text is not streamed: a reasoning model's stream carries only the answer in delta.content.
Refusals arrive inside the stream
A streamed response commits to HTTP 200 before the request is routed, so a refusal arrives as an error frame, not as an HTTP status. Its type is the code a non-streaming request would return as error.code.
ERROR FRAMES
data: {"error": {"message": "This commercial family is not available for inference", "type": "model_not_found"}} data: {"error": {"message": "Insufficient credit balance", "type": "insufficient_credits", "required_usd": 0.0032835, "available_usd": 0.0, "allowed_actions": ["top_up"]}}Always check each frame for an error key before reading choices, as the example does.
Example
STREAMING COMPLETION
curl -N https://api.tessen.ai/v1/chat/completions \ -H "Authorization: Bearer $TESSEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "z1", "stream": true, "max_tokens": 256, "messages": [{"role": "user", "content": "Say hello in five words."}] }'import jsonimport osimport requests with requests.post( "https://api.tessen.ai/v1/chat/completions", headers={"Authorization": f"Bearer {os.environ['TESSEN_API_KEY']}"}, json={ "model": "z1", "stream": True, "max_tokens": 256, "messages": [{"role": "user", "content": "Say hello in five words."}], }, stream=True, timeout=120,) as response: response.raise_for_status() for line in response.iter_lines(decode_unicode=True): if not line.startswith("data: "): continue data = line[len("data: "):] if data == "[DONE]": break frame = json.loads(data) if "error" in frame: # a refusal arrives as a frame inside a 200 stream raise RuntimeError(frame["error"]["message"]) if frame["choices"]: print(frame["choices"][0]["delta"].get("content", ""), end="", flush=True) else: print() print(frame["usage"])const response = await fetch("https://api.tessen.ai/v1/chat/completions", { method: "POST", headers: { Authorization: `Bearer ${process.env.TESSEN_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ model: "z1", stream: true, max_tokens: 256, messages: [{ role: "user", content: "Say hello in five words." }], }),});if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`); const decoder = new TextDecoder();let buffer = "";for await (const chunk of response.body) { buffer += decoder.decode(chunk, { stream: true }); const lines = buffer.split("\n"); buffer = lines.pop(); for (const line of lines) { if (!line.startsWith("data: ")) continue; const data = line.slice("data: ".length); if (data === "[DONE]") continue; const frame = JSON.parse(data); if (frame.error) throw new Error(frame.error.message); // a refusal arrives as a frame inside a 200 stream if (frame.choices.length) process.stdout.write(frame.choices[0].delta.content ?? ""); else console.log("\n", frame.usage); }}