Skip to content

REST API Chat Integration

Use the schema chat endpoint to add a SyntheticBrew agent experience to a product or service. Streaming responses use Server-Sent Events (SSE); clients that only need a completed answer can request one JSON response.

You need:

  • a schema with Chat Enabled and an entry agent;
  • an API key with the chat scope, or a valid end-user JWT;
  • the schema’s immutable name, such as support-handbook;
  • the origin of your Cloud workspace or Enterprise deployment.

Set the origin once in examples:

Terminal window
export SYNTHETICBREW_URL="https://YOUR_SYNTHETICBREW_ORIGIN"
export SYNTHETICBREW_TOKEN="bb_your_token"

Enterprise operators testing on the host can instead use a local origin such as http://localhost:8443 if their network and TLS configuration allow it.

POST /api/v1/schemas/{name}/chat requires chat scope. {name} is the stable configuration name returned by GET /api/v1/schemas, not the schema UUID.

Terminal window
curl -N "$SYNTHETICBREW_URL/api/v1/schemas/support-handbook/chat" \
-H "Authorization: Bearer $SYNTHETICBREW_TOKEN" \
-H "Content-Type: application/json" \
-d '{"message":"What is the return policy?"}'

The request body is limited to 1 MB.

FieldRequiredMeaning
messageOne of message or resume_interruptA new user message. An empty string is not a message.
resume_interruptOne of message or resume_interruptAn object containing interrupt_id and arbitrary JSON payload. Requires session_id.
session_idOn resumeA UUID returned by the previous turn. Omit it to create a new session.
streamNoDefaults to true. Set to false for a collected JSON response.
user_subUsually noOptional visitor identifier for trusted API-token integrations. It is namespaced under the token; authenticated JWT subjects cannot be overridden.
headersNoHeader values to forward to MCP tools. Only names allowed by each MCP server’s forward_headers configuration are taken from the HTTP request automatically.

message and resume_interrupt are mutually exclusive. Sending both or neither returns 400.

The response content type is text/event-stream. Each frame has an event name and a JSON payload:

EventPayloadClient behavior
thinkingcontentOptionally display model reasoning when available.
message_deltacontentAppend the chunk to the visible assistant response.
messagecontentTreat as the completed assistant message when emitted.
tool_calltool, call_id, optional argumentsShow that a tool started. arguments is the tool argument map.
tool_resulttool, call_id, content, summary, has_errorRecord the tool result and failure flag.
confirmationcontent, call_id, optional toolInformational notification for confirm_before; see the caution below.
interrupt_requestinterrupt_id, contentParse content as JSON, render the requested UI, and resume the same session.
interrupt_resumeinterrupt_id, contentMark the rendered interrupt as answered without adding a duplicate user bubble.
donesession_id and optional token fieldsSave the session UUID and close the turn.
errorcontent and optional code, messageSurface the error. The stream then ends.

The optional fields on done are total_tokens, context_tokens, prompt_tokens, completion_tokens, and cached_prompt_tokens. They appear only when the runtime has those values.

Example:

event: thinking
data: {"content":"I will check the policy."}
event: tool_call
data: {"tool":"knowledge_search","call_id":"call-7","arguments":{"query":"return policy"}}
event: tool_result
data: {"tool":"knowledge_search","call_id":"call-7","content":"Returns are accepted within 30 days.","summary":"1 relevant passage","has_error":false}
event: message_delta
data: {"content":"Returns are accepted "}
event: message_delta
data: {"content":"within 30 days."}
event: done
data: {"session_id":"67b592d5-a76f-4d83-9090-1ff0342ee2c3","total_tokens":156}

Do not rely on a standalone structured_output event. When an agent calls show_structured_output, the stream emits interrupt_request instead of the raw tool events, followed by interrupt_resume after the client answers.

The content string in interrupt_request contains the persisted interrupt payload. Parse it as JSON and render the described form or structured block.

event: interrupt_request
data: {"interrupt_id":"d2219e44-d2c3-41ac-a721-c8dd3f660e68","content":"{\"interrupt_id\":\"d2219e44-d2c3-41ac-a721-c8dd3f660e68\",\"kind\":\"structured_output\",\"schema\":{...}}"}

Post the answer to the same schema chat endpoint:

Terminal window
curl -N "$SYNTHETICBREW_URL/api/v1/schemas/support-handbook/chat" \
-H "Authorization: Bearer $SYNTHETICBREW_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"session_id":"67b592d5-a76f-4d83-9090-1ff0342ee2c3",
"resume_interrupt":{
"interrupt_id":"d2219e44-d2c3-41ac-a721-c8dd3f660e68",
"payload":{"answers":[{"id":"leave_type","value":"vacation"}]}
}
}'

Expected result: the stream emits interrupt_resume, the paused turn continues, and a final done event retains the same session ID.

ConditionHTTP status
Missing session_id on a resume400
Unknown or cross-tenant interrupt404
Interrupt belongs to another session403
Interrupt was already resolved or abandoned409

Save the UUID from done, then include it on the next message:

Terminal window
curl -N "$SYNTHETICBREW_URL/api/v1/schemas/support-handbook/chat" \
-H "Authorization: Bearer $SYNTHETICBREW_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"message":"Does that include sale items?",
"session_id":"67b592d5-a76f-4d83-9090-1ff0342ee2c3"
}'

Session administration uses separate read and write scopes:

Terminal window
# List active sessions for one agent. Default per_page is 20; maximum is 100.
curl "$SYNTHETICBREW_URL/api/v1/sessions?agent_name=support-agent&status=active&page=1&per_page=20" \
-H "Authorization: Bearer $SYNTHETICBREW_TOKEN"
# Read one session and its persisted events.
curl "$SYNTHETICBREW_URL/api/v1/sessions/67b592d5-a76f-4d83-9090-1ff0342ee2c3" \
-H "Authorization: Bearer $SYNTHETICBREW_TOKEN"
curl "$SYNTHETICBREW_URL/api/v1/sessions/67b592d5-a76f-4d83-9090-1ff0342ee2c3/messages" \
-H "Authorization: Bearer $SYNTHETICBREW_TOKEN"

GET /api/v1/sessions accepts agent_name, user_sub, status, from, to, page, and per_page. Dates use RFC 3339 or YYYY-MM-DD. Valid stored statuses are active, completed, expired, and failed. The current session record does not retain an agent name, so agent_name is accepted but does not narrow results. Filter by user, status, or time, then correlate the session ID with events or the Tool Call Log when you need agent attribution.

{
"data": [
{
"id": "67b592d5-a76f-4d83-9090-1ff0342ee2c3",
"title": "Return policy",
"schema_id": "3429747f-c6ba-4e18-a10c-a93b05193295",
"user_sub": "website:visitor-42",
"status": "active",
"metadata": {"order_id": "ord-123"},
"created_at": "2026-08-18T08:00:00Z",
"updated_at": "2026-08-18T08:03:00Z"
}
],
"total": 1,
"page": 1,
"per_page": 20,
"per_page_max": 100,
"total_pages": 1
}

POST /api/v1/sessions accepts optional id, title, schema_id, user_sub, and metadata. PUT /api/v1/sessions/{id} accepts title, status, and metadata; DELETE /api/v1/sessions/{id} removes the session. IDs must be UUIDs and metadata is capped at 16 KB. Use only the stored status values listed above; SyntheticBrew rejects unsupported values.

API tokens with sufficient scope and Admin sessions can manage all sessions in the workspace. A regular end-user token can access only sessions with its own user_sub; attempts to access another user’s session return 404 without revealing whether it exists.

Set stream to false when the client does not need progressive output or human-in-the-loop widgets:

Terminal window
curl "$SYNTHETICBREW_URL/api/v1/schemas/support-handbook/chat" \
-H "Authorization: Bearer $SYNTHETICBREW_TOKEN" \
-H "Content-Type: application/json" \
-d '{"message":"Summarize the return policy.","stream":false}'
{
"session_id": "67b592d5-a76f-4d83-9090-1ff0342ee2c3",
"schema_id": "3429747f-c6ba-4e18-a10c-a93b05193295",
"message": "Returns are accepted within 30 days.",
"tool_calls": [
{
"tool": "knowledge_search",
"input": "{\"query\":\"return policy\"}",
"output": "Returns are accepted within 30 days."
}
]
}

The optional error field contains a turn error. Non-streaming collection does not expose an interrupt payload, so use streaming for show_structured_output workflows.

EventSource cannot send the required POST request. Use fetch and parse the response stream:

const response = await fetch(
`${process.env.SYNTHETICBREW_URL}/api/v1/schemas/support-handbook/chat`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.SYNTHETICBREW_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ message: 'Hello' }),
},
);
if (!response.ok || !response.body) {
throw new Error(`SyntheticBrew returned HTTP ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
let eventName = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() ?? '';
for (const line of lines) {
if (line.startsWith('event: ')) eventName = line.slice(7);
if (!line.startsWith('data: ')) continue;
const data = JSON.parse(line.slice(6));
if (eventName === 'message_delta') process.stdout.write(data.content);
if (eventName === 'interrupt_request') {
renderInterrupt(data.interrupt_id, JSON.parse(data.content));
}
if (eventName === 'done') console.log('\nsession:', data.session_id);
}
}
StatusMeaning
400Invalid JSON, an invalid UUID, an oversized body, or invalid field combination.
401Missing or invalid authentication.
403The token lacks the required scope, or a resumed interrupt belongs to another session.
404The schema is unavailable or chat-disabled, or the requested resource is not visible to this actor.
409The interrupt was already resolved or abandoned.
429A Cloud edge or an Enterprise reverse proxy applied its configured traffic policy.
5xxA service, model, or upstream tool failed.

An in-stream failure arrives as error and closes the stream. Retry only requests that are safe for your workflow. Use exponential backoff for 429, 502, 503, and 504, honoring Retry-After when the edge or proxy supplies it.

SyntheticBrew does not expose a built-in per-key rate-limit configuration or a rate-limit usage endpoint. Cloud traffic controls are applied by the managed service; Enterprise customers configure any request-rate policy in their ingress or reverse proxy.

Authenticated clients can query the tool-call log:

Terminal window
curl "$SYNTHETICBREW_URL/api/v1/audit/tool-calls?agent=support-agent&tool=knowledge_search&page=1&per_page=20" \
-H "Authorization: Bearer $SYNTHETICBREW_TOKEN"

Filters are session_id, agent, tool, status (completed or failed), user_id, from, to, page, and per_page. The default page size is 50 and the maximum is 100. Results contain id, session_id, agent_name, tool_name, input, output, status, duration_ms, user_id, and created_at, plus pagination metadata.

The model registry is anonymous and read-only:

Terminal window
curl "$SYNTHETICBREW_URL/api/v1/models/registry?provider=anthropic"
curl "$SYNTHETICBREW_URL/api/v1/models/registry/providers"

Enterprise deployments can expose Prometheus-format metrics at /metrics. Restrict that endpoint through network policy or your reverse proxy.