Skip to main content
@relaymessenger/elevenlabs answers a Relay call with an ElevenLabs Agent. ElevenLabs runs the talking: speech recognition, the model, the voice and turn-taking. The package joins the call and carries the audio both ways over the ElevenLabs Agents WebSocket.

Before you start

Answer a call

Call ElevenLabsCall.connect from your call.created handler. It joins the call (joining answers it), gets a signed URL with your API key, and opens the ElevenLabs session.
connect resolves once the call’s media and the ElevenLabs session are both up. call.conversationId is ElevenLabs’ ID for that session. To keep your API key on another server, mint the URL there with getSignedUrl({ apiKey, agentId }) and pass elevenlabs: { agentId, signedUrl }. A public agent needs only agentId. initiationData is sent as conversation_initiation_client_data, for overrides and dynamic variables.

What the bridge does

The person’s voice goes to ElevenLabs as user_audio_chunk. When ElevenLabs ends the session, the bridge ends the call; when the call ends, the bridge closes the session. call.end() ends the call for both sides, and call.close() leaves it without ending it.

Give the voice a face

If your agent’s Contact Card has a Rive file, the phone draws it during the call. Each audio event’s alignment becomes a viseme number on the file’s View Model, from 0 (rest) to 9 (W and Q), timed so the mouth moves when the words are heard. speaking is true while your agent talks. Rename the properties with rive: { viseme: "mouth", speaking: "talking" }, pass null for one you do not use, or pass rive: false to send nothing. call.rive is set once the channel opens, a moment after connect resolves, and stays unset if the channel fails or with rive: false. To set other values or fire triggers right away, use await call.transport.rive().

When it fails

Next steps