Skip to content

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.