The /v4/interact/socket endpoint is still in beta and potentially subject to minor changes.
The Voiceflow WebSocket runtime uses socket.io to establish persistent, bidirectional connections for real-time conversational AI interactions.
Find the socket.io client in your respective development language. This documentation will use javascript, but the fundamentals remain the same.
Connection configuration
import { io } from 'socket.io-client';
const socket = io('https://general-runtime.voiceflow.com', {
path: '/v4/interact/socket',
timeout: 10000,
transports: ['websocket', 'polling', 'webtransport'], // optional fallbacks
reconnection: true,
tryAllTransports: true,
reconnectionDelay: 1000,
reconnectionAttempts: 5,
reconnectionDelayMax: 5000,
});
Lifecycle
A session is not the same as the socket.io connection. You can disconnect/reconnect multiple times and still continue the same conversation session.
For more information on socket.io lifecycle management, reference documentation.
Initialization sequence
- Wait for
connect. This is a socket.io level connection established.
- Send
client.start. Send voiceflow project metadata/config + optional sessionKey.
- Wait for
client.started. Client handshake, confirms configuration.
- If
client.started specifies newSessionRequired:
- Send
session.create.
- Wait for
session.created. Save returned sessionKey for future use.
- Agent is now ready for interactions
// save sessionKey on client side for future reconnections
let sessionKey: string | undefined;
// socket.io level connection
socket.on('connect', () => {
// voiceflow client handshake
socket.emit('client.start', { sessionKey, ...metadata });
socket.once('client.started', (payload: { newSessionRequired: boolean }) => {
if (payload.newSessionRequired === false) {
return ready();
}
// session create handshake
socket.emit('session.create', {});
socket.once('session.created', (payload: { sessionKey: string }) => {
sessionKey = payload.sessionKey;
ready();
});
});
})
Action Lifecycle
Once the session is ready, you can now send actions to the agent and get responses back.
- send new action with
action.send, i.e. { type: 'text', payload: 'hello' }
- get back a status
action.status either status: 'accepted' | 'rejected'
- if
status: accepted:
- get back agent responses with
action.trace, display to user
- get
action.status with status: 'completed'
This represents the back and forth conversation with the agent.
Try to not send a new action while an existing action is running. While this is supported, it can make it harder to debug user state and lead to certain race conditions.Instead wait for status: 'completed' or status: 'rejected' after sending an action.
Conversation Lifecycle
Start
To start the conversation from the beginning, your first action should be:
socket.emit('action.send', {
action: { type: 'launch', payload: {} }
});
It is also possible to launch from specific points in the conversation with events:
socket.emit('action.send', {
action: { type: 'event', payload: { event: { name: 'event_name' } } }
});
End
The session ending also signals that the conversation has ended. This can also occur because of timeouts.
socket.on('session.ended', () => {
// cleanup code
})
Client events
client.start
Initial Voiceflow metadata and configuration to validate and set up the client. This is aways the first message that the client should send to the server after socket.io successfully connects. Server will respond with client.started.
Payload
?: denotes optional properties.
interface ClientStartPayload {
userID: string; // primary user identifier
projectID: string; // voiceflow projectID
// (versionID) environment to use, [default: "development"]
environmentID?: string;
// session to reconnect to, ignore if new session
sessionKey?: string | null;
// voiceflow project API Key (under settings), do not set for public facing clients
authorization?: string;
config?: {
// break up LLM responses into live chunks [default: false]
completionEvents: boolean;
// timezone in IANA (tz database) for the vf_user_timezone variable
userTimezone?: string;
audioEvents: boolean; // enable TTS audio chunk responses
audioEncoding?: 'audio/x-mulaw' | 'audio/pcm'; // audio response format
};
}
Example Call
socket.emit('client.start', {
userID: 'test@test.com',
projectID: '6939bedf69c7ce5ee7a108ed',
environmentID: 'production',
authorization: 'VF.DM...',
config: {
completionEvents: true
};
});
session.create
Creates a new session for the user. Should be sent after client.started indicates newSessionRequired: true. Server will respond with session.created.
Example Call
socket.emit('session.create', {});
action.send
Sends a user action to the server. A valid session must already exist. Server will respond with action.status and action.trace events.
See possible action types in the action body of interaction requests..
Payload
interface ActionSendPayload {
action: {
// request type: "text" | "intent" | "launch" | "event" | "action" | etc.
type: string;
// action-specific payload
payload?: string | object;
};
}
Example Call
socket.emit('action.send', {
action: {
type: 'text',
payload: 'Hello, how are you?'
}
});
Server events
client.started
Server ack to client.start, informs if a new session needs to be created
Payload
interface ClientStartedPayload {
newSessionRequired: boolean
}
Example
socket.on('client.started', (payload: ClientStartedPayload) => {
if (payload.newSessionRequired) {
socket.emit('session.create', {});
} else {
// session is now ready for interactions
}
});
session.created
Confirms that a new session has been created. Client should store the sessionKey for future reconnections.
Payload
interface SessionCreatedPayload {
sessionKey: string; // JWT session key to store and use for reconnections
}
Example
socket.on('session.created', (payload: SessionCreatedPayload) => {
localStorage.setItem('sessionKey', payload.sessionKey);
// session is now ready for interactions
});
session.ended
Indicates that the conversation session has ended. Client should close the session and optionally display a workflow to create a new one.
Payload
interface SessionEndedPayload {
// reason for session ending (e.g., "end_of_diagram", "inactivity_timeout", etc.)
reason: string;
}
action.trace
Represents agent responses and debugging information for an action. See possible trace types.
Payload
type ActionTracePayload = {
// trace object containing agent response data (speak, text, audio, etc.)
trace: Trace;
// message identifier matching the action.send messageID
messageID: string;
}
Example
socket.on('action.trace', (payload: ActionTracePayload) => {
// Handle trace data (speech, text, audio chunks, etc.)
console.log('Trace received:', payload.trace);
});
action.status
Indicates the status of an action that was sent via action.send. This event is sent when an action is accepted, rejected, or completed.
Payload
interface ActionStatusPayload {
status: 'accepted' | 'rejected' | 'completed'; // action status
messageID: string; // message identifier matching the action.send messageID
reason?: string; // optional reason for rejection or completion
}
server.restart
Server is gracefully shutting down and informing the client to reconnect to a different server instance.
Payload
interface ServerRestartPayload {
action?: {
type: string;
payload?: string | object;
diagramID?: string;
time?: number;
}; // optional action to replay after reconnection
}
error
General error message from the server. May occur at any point during the connection.
Payload
interface ErrorPayload {
code?: string; // optional error code
error: string; // error message
}