LexeyLexey
HomePricingDocs
Sign in
Streaming guide
Basics
  • Getting started
Features
  • Manage tab
  • Knowledge base
  • Conversations
  • Customer chat
  • Skills & automations
  • Quality assurance
  • AI safety
  • Billing & usage
Deployment
  • Embedding your chat
API
  • Customer Chat API
  • Management Chat API
  • Webhook events
  • API key management
  • Streaming guide
  • Agent integration
Help
  • FAQ

Streaming guide

Both the Customer Chat API and Management Chat API return Server-Sent Events (SSE) streams when you send a message. This guide covers the stream format and provides consumption examples in multiple languages.

Event format

Each SSE event consists of an id: line and a data: line, separated by a blank line:

1id: 1
2data: {"type":"delta","text":"Our"}
3 
4id: 2
5data: {"type":"delta","text":" business hours are 9am–5pm."}
6 
7id: 3
8data: [DONE]

Event types

Eventdata: payloadDescription
Delta{"type":"delta","text":"..."}Incremental text chunk — append to the response
Status{"type":"status","status":"escalated"}Conversation state change (e.g. escalated to human)
Error{"type":"error","message":"..."}Mid-stream error (rare)
Done[DONE]Stream complete — no more events will follow

Event IDs

Every event includes a monotonically incrementing id: field (starting at 1). Use this for completeness checking (verify no events were lost) and debugging.

Pre-stream errors

If the request fails before streaming begins (auth, billing, validation), the response is a standard JSON error with an appropriate HTTP status code — not an SSE stream. Always check the response status code before attempting to read the stream.

Code examples

All examples follow the same pattern: POST a message, consume the SSE stream, and collect the full response text.

1const BASE_URL = "https://lexey.ai/api/v1/customer";
2 
3async function sendMessage(conversationId: string, message: string): Promise<string> {
4 const res = await fetch(
5 `${BASE_URL}/conversations/${conversationId}/messages`,
6 {
7 method: "POST",
8 headers: {
9 Authorization: `Bearer ${API_KEY}`,
10 "Content-Type": "application/json",
11 },
12 body: JSON.stringify({ message }),
13 },
14 );
15 
16 if (!res.ok) {
17 const error = await res.json();
18 throw new Error(error.error);
19 }
20 
21 const reader = res.body!.getReader();
22 const decoder = new TextDecoder();
23 let fullText = "";
24 let buffer = "";
25 
26 while (true) {
27 const { done, value } = await reader.read();
28 if (done) break;
29 
30 buffer += decoder.decode(value, { stream: true });
31 const parts = buffer.split("\n\n");
32 buffer = parts.pop()!;
33 
34 for (const part of parts) {
35 const dataLine = part.split("\n").find((l) => l.startsWith("data: "));
36 if (!dataLine) continue;
37 const data = dataLine.slice(6);
38 if (data === "[DONE]") return fullText;
39 
40 try {
41 const event = JSON.parse(data);
42 if (event.type === "delta") fullText += event.text;
43 else if (event.type === "error") throw new Error(event.message);
44 } catch (e) {
45 if (e instanceof SyntaxError) continue; // skip malformed lines
46 throw e;
47 }
48 }
49 }
50 return fullText;
51}

Edge cases

  • Chunked reads may split events — TCP chunks don't align with SSE event boundaries. Always buffer incoming data and split on \n\n to extract complete events.
  • Error mid-stream — If the server encounters an error after streaming has begun, it emits an error event followed by [DONE].
  • Connection timeout — Streaming requests have a 120-second timeout. If the connection drops before [DONE], treat the collected text as partial.
  • Empty response — If the agent's response is blocked by a safety filter, the stream may contain a single delta with the refusal message, then [DONE]. This is not an error.

Product

  • Features
  • Pricing
  • Use Cases

Documentation

  • Getting Started
  • Customer API
  • Management API

Company

  • Sign Up
  • Sign In
  • Terms of Service
  • Privacy Policy
© 2026 Lexey. All rights reserved.