WebSocket API
Moveo's WebSocket API enables real-time communication between your application and Moveo's services. This is the same WebSocket API that the Moveo Web Client uses internally. You can use this API to build your own custom integration.
With this API you can:
- Create and manage conversation sessions
- Send and receive messages in real-time
- Handle session lifecycle events
- Implement custom message handling logic
Socket.IO protocol
Moveo uses the Socket.IO protocol v4 to establish WebSocket communication between the client and the Moveo channels service.
To initiate a WebSocket connection, send a GET request to:
wss://channels-ws.moveo.ai/v1/web/{INTEGRATION-ID}/ws?EIO=4&transport=websocket
Replace {INTEGRATION-ID} with the ID of the Web integration in Moveo. The server rejects a
connection without the EIO=4 and transport=websocket query parameters. The Socket.IO client adds
them for you.
Example complete URL:
wss://channels-ws.moveo.ai/v1/web/1d093558-bac1-4954-acdd-c8d8dc05fa30/ws/?EIO=4&transport=websocket
Required headers
To upgrade the connection, include the following headers:
- Connection:
upgrade - Upgrade:
websocket
Connection upgrade response
Upon successful upgrade, the server responds with an open packet:
0{"sid":SESSION-ID,"upgrades":["websocket"],"pingInterval":10000,"pingTimeout":5000}
Heartbeat
The server sends a ping packet (2) every pingInterval milliseconds and expects a
pong response before pingTimeout.
Message structure
After connection upgrade, the server only responds to packets prefixed with 4, indicating a
message packet type.
The second character of the packet defines the message type:
0: Connect message (server responds with40{"sid":"SESSION-ID"})1: Disconnect message2: Event message (see supported events below)
Event messages must be an array of exactly two elements:
- The
event type(string), for example,message:send - The
event payload(object), for example:
{
"input": {
"text": "defaultstartmessage"
},
"session_id": "5ebeb74f-b3af-4ec0-960f-cd13ad7154d0",
"channel": "web",
"timestamp": 1737028058711
}
Complete example of a message
42["message:send",{"input":{"text":"defaultstartmessage"},"session_id":"5ebeb74f-b3af-4ec0-960f-cd13ad7154d0","channel":"web","timestamp":1737028058711}]
Depending on the event type, the server may respond after processing.
Code example
This is a comprehensive Node.js example using the socket.io-client library. The example
demonstrates:
- Connection establishment with proper error handling
- Session creation and management
- Message sending and receiving
- Error handling and reconnection logic
- Logging and monitoring
import { io } from "socket.io-client";
// Configuration
const INTEGRATION_ID = "c3725769-d21e-4391-9f15-63f47eefe211"; // replace with your real one
const URL = `wss://channels-ws.moveo.ai/`;
const MAX_RECONNECT_ATTEMPTS = 5;
const RECONNECT_DELAY = 1000; // 1 second
// Connection options
const socketOptions = {
path: `/v1/web/${INTEGRATION_ID}/ws`,
transports: ["websocket"],
autoConnect: true,
upgrade: false,
extraHeaders: {
Connection: "upgrade",
Upgrade: "websocket",
},
// Reconnection settings
reconnection: true,
reconnectionAttempts: MAX_RECONNECT_ATTEMPTS,
reconnectionDelay: RECONNECT_DELAY,
reconnectionDelayMax: 5000,
};
// Initialize socket connection
const socket = io(URL, socketOptions);
// Connection event handlers
socket.on("connect", () => {
console.log("✅ Connected", socket.id);
// Create a new session
const sessionCreatePayload = {
context: {
user: {
// Add any user-specific context here
email: "user123@example.com",
name: "John Doe",
},
},
timestamp: Date.now(),
is_test: true, // Set to false in production
};
socket.emit("session:create", sessionCreatePayload);
});
// Session management
socket.on("session:created", (data) => {
console.log("🆕 Session created:", data);
// Store session ID for future messages
const sessionId = data.session_id;
// Send initial message
const eventPayload = {
input: {
text: "defaultstartmessage",
},
session_id: sessionId,
channel: "web",
timestamp: Date.now(),
};
socket.emit("message:send", eventPayload);
});
// Message handling
socket.on("message:receive", (data) => {
console.log("📥 Message received:", data);
});
// Error handling
socket.on("connect_error", (err) => {
console.error("❌ Connection error:", err);
});
socket.on("disconnect", (reason) => {
console.log("🔌 Disconnected:", reason);
// Handle specific disconnect reasons
if (reason === "io server disconnect") {
// Server initiated disconnect, try to reconnect
socket.connect();
}
});
// Errors that are not tied to one request
socket.on("app:error", (error) => {
console.error("Server error:", error.error_code, error.description);
});
socket.on("message:send:error", (error) => {
console.error("Message not sent:", error);
});
// Cleanup on process exit
process.on("SIGINT", () => {
console.log("Closing connection...");
socket.close();
process.exit();
});
Limits and error handling
Limits
| Limit | Value | What happens when a client exceeds it |
|---|---|---|
| Size of one WebSocket message | 100,000 bytes | The server closes the connection with close code 1009. |
Text of one message (input.text) | 1,500 characters | Moveo keeps the first 1,500 characters and drops the rest. The server does not send an error. |
| User messages in a session | 200 while the AI agent handles the session | The server drops each new message and sends no error. The AI agent does not reply. The limit does not apply after a handover. |
| Open connections | A fixed number for each server | The upgrade request fails with HTTP 503. Connect again with a backoff. |
| Open connections to one session | 75 | session:reconnect, session:continue, and session:history:sync return session:max_concurrent with the session_id, and the connection does not join the session. |
session:continue events | 60 in 60 seconds from one client IP address | Moveo counts these events but does not enforce the limit. When Moveo enforces the limit, the server sends session:continue:error with error_code rate_limited. |
The WebSocket API has no other per-connection message rate limit.
Error events
The server reports an error as an event on the topic of the request, plus :error. For example, a
failed session:create returns session:create:error, and a failed file:upload_url:create
returns file:upload_url:create:error. A payload that is not valid JSON, or that fails validation,
returns app:error:invalid_payload. A server error that is not tied to one request returns
app:error. The payload has one of two shapes:
- An error from the WebSocket server has
error_codeanddescription. - An error that the Moveo API returns has
code,message, andstatus_code.
error_code | Cause |
|---|---|
bad-request | The event or its payload is not valid. |
existing-session | The request conflicts with a session that already exists. |
identity-token-error | The user authentication token is missing, expired, or not valid. |
internal-server-error | The server could not process the request. Retry with a backoff. |
rate_limited | Too many session:continue events from one client IP address, when Moveo enforces that limit. |
Handshake errors
Before the upgrade, the HTTP handshake can fail with one of these responses. The body is JSON with
error_code, error_message, and status_code.
| HTTP status | error_code | Cause |
|---|---|---|
400 | bad-request | The Upgrade header is not websocket, or the EIO or transport query parameter is missing. |
400 | invalid-eio-version | EIO is not 4. |
400 | invalid-transport | transport is not websocket. |
403 | integration-inactive | The Web integration is not active. |
403 | validation-failed | The integration has a list of domains, and the Origin header is empty or not in the list. |
404 | integration-not-found | No Web integration has this ID. |
503 | — | The server has no free connection. Connect again with a backoff. |
Close codes
The server closes the WebSocket connection with one of these codes:
| Code | Reason text | Cause |
|---|---|---|
1000 | connection close requested from client | The client sent a Socket.IO disconnect packet (41). |
1001 | server going away | The server shuts down. Connect again and send session:reconnect. |
1009 | — | A WebSocket message is larger than 100,000 bytes. |
If the server gets no packet, not even a pong, for longer than pingInterval plus pingTimeout, it
closes the connection without a close frame.
Error handling best practices
- Implement exponential backoff for retries
- Log errors with appropriate context
- Monitor error rates and patterns
- Set up alerts for critical errors
- Implement circuit breakers for persistent failures
Troubleshooting
Common issues and their solutions:
-
Connection failures:
- Verify the integration ID is correct
- Check network connectivity
- Ensure proper headers are set
- Verify SSL/TLS configuration
-
Message delivery issues:
- Check message format and structure
- Verify session ID is valid
- Ensure proper event type is used
-
Performance issues:
- Monitor connection health
- Implement reconnection logic
- Handle backpressure appropriately
Rate limits and best practices
-
Connection management:
- Implement exponential backoff for reconnection attempts
- Monitor connection health
- Handle disconnections gracefully
-
Message handling:
- Process messages in order
- Implement message queuing if needed
- Handle message acknowledgments
-
Resource management:
- Close connections when not needed
- Monitor memory usage
- Implement proper cleanup procedures