Skip to main content

Endpoint

POST /api/sdk/chat/stream
Same as the Chat endpoint, but returns a streaming text response for progressive rendering. The widget uses this endpoint by default.

Authentication

Requires your publishable widget key in the Authorization header:
Authorization: Bearer ab_live_xxxxxxxxxxxxxxxx

Request Body

The request body is identical to the Chat endpoint. All the same fields are accepted: message, user_id, user_token, session_id, company_id, context, custom_fields, conversation_history, behavior_context, and mode.

Response

The response is a chunked text/plain; charset=utf-8 stream. Read it like a regular ReadableStream and append chunks as they arrive. The full text accumulates over the stream. Action payloads (highlights, ticket creation, etc.) are interleaved in the stream as records framed by the ASCII record-separator character (\x1E):
\x1E__ACTION__<json>\x1E       (per-action, emitted as it resolves)
\x1E__ACTIONS__<json-array>\x1E (final summary at end of stream)
If you only need the assistant’s text, strip any substring matching \x1E__ACTION(S)?__...\x1E from each chunk before rendering. The widget SDK does this for you.

Response Headers

The stream response includes metadata in custom headers:
HeaderDescription
X-Session-IdThe session ID for this conversation. Use this to continue the conversation in subsequent requests.
X-Agent-IdThe ID of the agent handling the request.
Transcripts are stored asynchronously after the stream completes.

Example

const response = await fetch("https://api.haloagents.ai/api/sdk/chat/stream", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer ab_live_xxxxxxxxxxxxxxxx",
  },
  body: JSON.stringify({
    user_id: "user_123",
    message: "How do I set up event tracking?",
  }),
});

// Read metadata from response headers
const sessionId = response.headers.get("X-Session-Id");
const agentId = response.headers.get("X-Agent-Id");

const reader = response.body?.getReader();
const decoder = new TextDecoder();
let fullText = "";

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  const chunk = decoder.decode(value, { stream: true });
  fullText += chunk;
  // Update your UI with the partial response
  updateChatBubble(fullText);
}

When to Use Streaming vs Non-Streaming

Use CaseEndpoint
Chat widget (real-time UX)/chat/stream
Server-side integrations/chat
Webhooks / async processing/chat
Mobile apps with progressive display/chat/stream