SSE Streaming
Queue a message with POST /message, then read the reply from GET /sessions/{session_id}/stream. Every Server-Sent Event type explained.
Messaging is two steps: queue the message with POST /message, then read the reply from GET /sessions/{session_id}/stream as Server-Sent Events — text, tool calls and results as they happen.
POST /message returns {"success": true, ...} immediately. The reply only arrives on the stream.• How It Works
1
POST /message
Body
{"message": "..."} (1–5000 characters) with your JWT.2
Open the stream
GET
/sessions/{session_id}/stream with the same JWT. Use the session_id from authentication.3
Parse events
Each event is a line
data: {json}. Switch on type.4
Stop on done
Close the connection when you receive a done event, then send the next message.
• Event Types
| type | Fields | Meaning |
|---|---|---|
token | content | A chunk of reply text. Append chunks in order. |
content | content | A complete block of reply text (sent instead of tokens in some flows). |
tool_call | name, call_id, arguments | Mara is calling a tool. |
tool_call_started | tool_name, call_id | A tool started running. |
tool_call_finished | tool_name, call_id | A tool finished running. |
tool_call_complete | name, call_id | The tool call was recorded. |
tool_response | name, call_id, output | The tool result, e.g. a quote or a transaction to sign. |
ui_hint | chart_context | Display hint for the web app. Safe to ignore. |
completion | message, metrics | The reply is complete, with usage metrics. |
done | — | End of stream. Close the connection. |
error | message | Something failed. The message is safe to show. |
session_expired | session_id, message | The previous session timed out and a new one was created. |
• Python Example
Python
import json, requests
H = {"Authorization": f"Bearer {token}"}
requests.post(f"{BASE}/message", headers=H,
json={"message": "swap 10 USDC for ETH on Base"}).raise_for_status()
with requests.get(f"{BASE}/sessions/{session_id}/stream",
headers=H, stream=True) as r:
for line in r.iter_lines(decode_unicode=True):
if not line or not line.startswith("data: "):
continue
event = json.loads(line[6:])
kind = event["type"]
if kind in ("token", "content"):
print(event["content"], end="", flush=True)
elif kind == "tool_response":
handle_tool_output(event["name"], event["output"])
elif kind == "error":
raise RuntimeError(event["message"])
elif kind == "done":
break• Tips
Do not buffer the response — process lines as they arrive
Ignore event types you do not recognise; new ones may be added
Sessions expire after inactivity (410). Authenticate again and retry
Share this page