Streaming¶
POST /v1/typed/stream takes the same body as /v1/typed and returns
NDJSON: one JSON object per line, one line per decoding step. Every line
carries the current state of all fields, so you can show them growing
side by side.
curl -N https://api.trusttyped.com/v1/typed/stream \
-H "Content-Type: application/json" \
-d '{"context": "Total 33000. Paid in cash.",
"schema": {"total": {"type": "number", "integer": true}, "paid": "bool",
"note": {"type": "string", "depends_on": ["total"]}}}'
Real events from that request:
{"step": 1, "stage": 1, "round": 1, "fields": {"total": "3", "paid": true, "note": ""}, "finished": ["paid"], "mode": "text", "model": "…"}
{"step": 7, "stage": 2, "round": 1, "fields": {"total": 33000, "paid": true, "note": "Paid"}, "finished": ["total", "paid"], "mode": "text", "model": "…"}
{"step": 11, "done": true, "result": {"total": 33000, "paid": true, "note": "Paid in cash."}, "confidence": {…}, "needs_review": [], "trace": [], "provenance": {…}, "seconds": 1.9, …}
Events¶
Progress events (no done key):
| Key | Meaning |
|---|---|
step |
Decoding step, counted across stages. |
stage, round |
Current stage and round (return rules start a new round). |
fields |
Every field: partial text while it is being written, the final value once finished, "" if not started, "⏳ calling API…" while a tool call is running. |
finished |
Fields that are complete. |
mode, model |
text or image, and the model used. |
Final event ("done": true): the same fields as the
response object (result,
confidence, needs_review, trace, provenance, …), plus step and
rounds.
Error event: {"error": "…", "done": true} if something fails after
streaming has started. Errors found before streaming (invalid schema, host
not allowed) are returned as a normal HTTP 400 instead.
Reading the stream¶
import json, requests
with requests.post("https://api.trusttyped.com/v1/typed/stream", json=body, stream=True) as r:
r.raise_for_status()
for line in r.iter_lines():
event = json.loads(line)
if event.get("error"):
raise RuntimeError(event["error"])
if event.get("done"):
print("final:", event["result"], "review:", event["needs_review"])
else:
print(event["step"], event["fields"])
const resp = await fetch("https://api.trusttyped.com/v1/typed/stream", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const reader = resp.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
for (;;) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
let nl;
while ((nl = buffer.indexOf("\n")) >= 0) {
const event = JSON.parse(buffer.slice(0, nl));
buffer = buffer.slice(nl + 1);
if (event.done) console.log("final", event.result);
else console.log(event.step, event.fields);
}
}
Partial values are previews
While a field is being written, its value in fields is raw text (a
number may show as "3" before becoming 33000). Only the final event
has typed values, and a rule can still reject or
change the outcome at the end of a stage.