/invoke endpoint returns a stream of newline-delimited JSON objects (NDJSON) rather than a single JSON response. Each line is a complete, self-contained JSON event. This lets your application begin processing the agentβs reply progressively rather than waiting for the entire response to arrive.
Why response.json() fails
If you try to parse the raw response body as a single JSON document it will throw an error:
response.json() method cannot handle that format.
The correct approach
Enable streaming on the request and iterate over the response line by line, parsing each non-empty line as its own JSON object:- Pass
stream=Trueso the response body is not buffered all at once. - Use
response.iter_lines()to read one line at a time. - Skip empty lines with
if raw_line:β these are keep-alive bytes with no data. - Decode each line from bytes to UTF-8, then parse with
json.loads().
Parsing approaches compared
| Approach | Works with /invoke? | Notes |
|---|---|---|
response.json() | β | Throws JSONDecodeError. |
response.iter_lines() + json.loads() | β | Correct β parse each line individually. |
response.text.split("\n") | β οΈ | Works but error-prone β prefer iter_lines(). |
/invoke_sync + response.json() | β | No streaming; waits for the full response. |
What each line contains
Each parsed line is a JSON event object from the agent. The structure varies by event type β amessageEvent carries the agentβs text reply, a recommendationEvent carries product suggestions, an agentHandoverEvent signals a human handoff. See Invoke (stream) for the full response event schema.