WebSocket mode

WebSocket mode is the bi-directional form of the Bot API. Live Hub opens one WebSocket per call, and both sides send messages over it whenever they have something to say. A voice bot depends on that freedom, which is why LLM agents generally use this mode. For the request-response form, see HTTP mode.

Unlike HTTP mode, WebSocket mode can carry audio in both directions, so a bot that performs its own speech recognition or its own synthesis can exchange the audio directly.

The WebSocket connection

Live Hub opens the connection at the start of the call, to the URL configured on the bot connection, using an HTTP Upgrade request. It stays open for the whole conversation and serves exactly one conversational session.

Two proprietary headers on the handshake give you the call's context before any message arrives:

Header What it carries
AC-Caller-Number The caller's phone number, the same value as caller in session.initiate.
AC-Conversation-Id The conversation's unique identifier, the same value as conversationId.

Authentication

Live Hub authenticates on the handshake, in the Authorization header of the Upgrade request, with either a permanent bearer token or an OAuth 2.0 token. Your server checks it there and accepts or rejects the connection. See Security and authentication.

GET /bot/ws HTTP/1.1
Host: bot.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Authorization: Bearer {token}
AC-Caller-Number: +1234567890
AC-Conversation-Id: 4a5b4b9d-dab7-42d0-a977-6740c9349588

Message format

Every message is JSON, with audio base64-encoded inside it rather than sent as binary frames. Each one carries a type naming the message. Messages traveling from Live Hub to the bot also carry conversationId.

Configuration

On the bot connection, set 'Bot connection API type' to WebSocket mode and enter your service's WebSocket address in the 'Bot URL' field, the botURL parameter. Select an 'Authentication method' on the same screen. See AudioCodes Bot API.

To have audio pass directly between the caller and the bot rather than through Live Hub's speech engines, select the 'Enable voice streaming' check box on the bot connection. The underlying parameters are directSTT, which streams the caller's voice to the bot with no speech-to-text in between, and directTTS, which plays the bot's audio to the caller with no text-to-speech.

Messages from Live Hub

Live Hub sends the following messages to the bot.

session.initiate

Live Hub sends session.initiate once the session is established. Answer with session.accepted, or with session.error to decline the conversation.

Parameter Type Description
conversationId string Unique identifier for the conversation.
type string session.initiate.
botName string The configured name of the bot.
caller string The caller's phone number.
expectAudioMessages boolean true when the bot is expected to play audio itself, in which case it must not send message activities carrying text prompts. It follows the directTTS parameter and nothing else.
supportedMediaFormats array The audio coders Live Hub accepts, most preferred first.

The media formats are:

Format Encoding
raw/mulaw Mu-Law, 8-bit 8 kHz, no header
wav/mulaw Mu-Law, 8-bit 8 kHz, WAV header
raw/lpcm16 Linear PCM, 16-bit 16 kHz, no header
wav/lpcm16 Linear PCM, 16-bit 16 kHz, WAV header
raw/lpcm16_8 Linear PCM, 16-bit 8 kHz, no header
wav/lpcm16_8 Linear PCM, 16-bit 8 kHz, WAV header
raw/lpcm16_24 Linear PCM, 16-bit 24 kHz, no header
wav/lpcm16_24 Linear PCM, 16-bit 24 kHz, WAV header
{
  "conversationId": "4a5b4b9d-dab7-42d0-a977-6740c9349588",
  "type": "session.initiate",
  "botName": "my_bot_name",
  "caller": "+1234567890",
  "expectAudioMessages": true,
  "supportedMediaFormats": [
    "raw/lpcm16"
  ]
}

activities

The activities message carries a list of activities. Two of them are shown here.

The start event arrives when the call begins:

{
  "conversationId": "4a5b4b9d-dab7-42d0-a977-6740c9349588",
  "type": "activities",
  "activities": [
    {
      "type": "event",
      "name": "start",
      "id": "582bbc43-0ef7-47e9-97b4-1e6141625b01",
      "timestamp": "2022-07-20T07:15:48.239Z",
      "language": "en-US",
      "parameters": {
        "locale": "en-US",
        "caller": "caller-id",
        "callee": "my_bot_name"
      }
    }
  ]
}

The dtmf event arrives when the caller presses a key:

{
  "conversationId": "4a5b4b9d-dab7-42d0-a977-6740c9349588",
  "type": "activities",
  "activities": [
    {
      "type": "event",
      "name": "dtmf",
      "id": "582bbc43-0ef7-47e9-97b4-1e6141625b01",
      "timestamp": "2022-07-20T07:15:48.239Z",
      "language": "en-US",
      "value": "123"
    }
  ]
}

userStream.start

userStream.start asks to start streaming the caller's audio to the bot. Answer with userStream.started; Live Hub begins sending userStream.chunk messages once it arrives.

{
  "conversationId": "4a5b4b9d-dab7-42d0-a977-6740c9349588",
  "type": "userStream.start"
}

userStream.chunk

userStream.chunk carries one chunk of the caller's audio.

{
  "conversationId": "4a5b4b9d-dab7-42d0-a977-6740c9349588",
  "type": "userStream.chunk",
  "audioChunk": "Base64EncodedAudioData"
}

userStream.stop

userStream.stop ends the audio stream. Answer with userStream.stopped.

{
  "conversationId": "4a5b4b9d-dab7-42d0-a977-6740c9349588",
  "type": "userStream.stop"
}

session.resume

Live Hub sends session.resume when it reconnects after losing the WebSocket. Answer with session.accepted, or with session.error to decline the reconnection.

{
  "conversationId": "4a5b4b9d-dab7-42d0-a977-6740c9349588",
  "type": "session.resume"
}

session.end

session.end reports that the conversation is over.

{
  "conversationId": "4a5b4b9d-dab7-42d0-a977-6740c9349588",
  "type": "session.end",
  "reasonCode": "client-disconnected",
  "reason": "Client Side"
}

Messages from the bot

The bot sends the following messages to Live Hub.

session.accepted

session.accepted accepts a session.initiate or a session.resume.

Parameter Type Description
mediaFormat string The format you have chosen. It must be one of the supportedMediaFormats from session.initiate.
{
  "type": "session.accepted",
  "mediaFormat": "raw/lpcm16"
}

userStream.started

userStream.started confirms that you are ready for audio chunks.

{
  "type": "userStream.started"
}

userStream.stopped

userStream.stopped confirms that you will accept no more audio chunks.

{
  "type": "userStream.stopped"
}

userStream.speech.hypothesis

userStream.speech.hypothesis carries a partial recognition result. Send these messages: Live Hub uses them to drive barge-in.

Parameter Type Description
alternatives array The recognition alternatives, each with a text.
{
  "type": "userStream.speech.hypothesis",
  "alternatives": [
    {
      "text": "How are"
    }
  ]
}

userStream.speech.recognition

userStream.speech.recognition carries the final recognition result. It is mainly useful for logging.

Parameter Type Description
alternatives array The recognition alternatives, each with a text and a confidence between 0 and 1.
{
  "type": "userStream.speech.recognition",
  "alternatives": [
    {
      "text": "How are you.",
      "confidence": 0.83
    }
  ]
}

userStream.speech.started

userStream.speech.started reports that the bot detected the start of speech. It applies when the bot does its own voice activity detection. With the OpenAI Realtime API, for example, it maps to input_audio_buffer.speech_started. Send it: Live Hub uses it for barge-in and timeouts.

{
  "type": "userStream.speech.started"
}

userStream.speech.stopped

userStream.speech.stopped reports that the bot detected the end of speech; it maps to input_audio_buffer.speech_stopped on the OpenAI Realtime API. Send it: Live Hub uses it for barge-in and timeouts.

{
  "type": "userStream.speech.stopped"
}

userStream.speech.committed

userStream.speech.committed reports that the bot has committed the caller's speech for processing, normally at the end of an utterance; it maps to input_audio_buffer.committed on the OpenAI Realtime API. Send it: Live Hub uses it for barge-in and timeouts.

It usually follows a single userStream.speech.stopped, but not always: the bot may wait a moment to be sure the utterance is finished, so there can be a delay, or several userStream.speech.stopped messages before one userStream.speech.committed.

{
  "type": "userStream.speech.committed"
}

playStream.start

playStream.start opens a play stream, which is how the bot streams audio to the caller. Follow it with playStream.chunk messages, and close it with playStream.stop.

Parameter Type Description
streamId string An identifier for the stream, unique within the conversation.
mediaFormat string The stream's media format. It must be one of the values from session.initiate.
altText string Optional. Text to log in place of the audio.
activityParams object Optional. Further activity parameters, such as expectAnotherBotMessage.

Only one play stream can be open at a time. Close the current one with playStream.stop before starting another.

A play stream is for audio the bot produces itself. To have Live Hub speak text instead, send a message activity, which is available only when text-to-speech is configured. To play a pre-recorded file or buffer, use the playUrl activity.

{
  "type": "playStream.start",
  "streamId": "1",
  "mediaFormat": "raw/lpcm16"
}

playStream.chunk

playStream.chunk carries one chunk of audio for the open play stream. Chunks are accepted only while the stream is open.

Parameter Type Description
streamId string The stream's identifier.
audioChunk string The audio, base64-encoded.

Send audio at the rate it plays back. If you send it faster or slower, the caller hears the difference.

{
  "type": "playStream.chunk",
  "streamId": "1",
  "audioChunk": "Base64EncodedAudioData"
}

playStream.stop

playStream.stop closes the play stream.

Parameter Type Description
streamId string The stream's identifier.
{
  "type": "playStream.stop",
  "streamId": "1"
}

activities

The activities message carries a list of activities from the bot. You use it for two things in particular.

Play an audio buffer to the caller with a playUrl activity. Put the audio in playUrlUrl as a data URI: base64, prefixed data:audio/wav;base64 or data:application/octet-stream;base64 depending on whether it has a WAV header. playUrlMediaFormat gives the format, and the optional playUrlAltText supplies the corresponding text.

{
  "type": "activities",
  "activities": [
    {
      "type": "event",
      "name": "playUrl",
      "activityParams": {
        "playUrlAltText": "Welcome to our example",
        "playUrlUrl": "data:audio/wav;base64,UklGRmK4AABXQVZFZm10IBIAAAAGAA...",
        "playUrlMediaFormat": "wav/lpcm16"
      }
    }
  ]
}

Disconnect the call with a hangup activity.

Closing the WebSocket does not end the call: Live Hub treats that as a dropped connection and tries to reconnect. Only a hangup activity ends it: Live Hub responds with session.end, disconnects the call, and then closes the WebSocket.

{
  "type": "activities",
  "activities": [
    {
      "type": "event",
      "name": "hangup"
    }
  ]
}

session.error

session.error reports a fatal error. Live Hub disconnects the call and closes the WebSocket.

Parameter Type Description
reason string The error message.
{
  "type": "session.error",
  "reason": "Internal Server Error"
}

Agent assist

WebSocket mode supports assist bots natively, by marking which participant a message concerns.

After the call starts, send a startRecognition activity naming the participant whose audio you want to receive:

{
  "type": "activities",
  "activities": [
    {
      "type": "event",
      "name": "startRecognition",
      "activityParams": {
        "targetParticipant": "customer"
      }
    }
  ]
}

On an agent-assist call, Live Hub adds a participant parameter to userStream.start, userStream.chunk, and userStream.stop. Your bot must include the same parameter on userStream.started, userStream.stopped, userStream.speech.hypothesis, and userStream.speech.recognition.

Check connectivity

Two messages verify the connection during integration, and they are never part of a real call. The Validate bot connection configuration button on the bot connection uses them.

Live Hub sends:

{
  "type": "connection.validate"
}

Reply with:

{
  "type": "connection.validated",
  "success": true
}

Example call flow

The following diagram traces a complete call, from session.initiate through audio streaming to session.end.

A complete call, from session.initiate through audio streaming to session.end