Example: Answering an incoming call

View as Markdown

For the complete documentation index, see llms.txt.

This example answers an inbound Voximplant call and bridges audio to ElevenLabs Agents for real-time speech-to-speech conversations.

Jump to the Full VoxEngine scenario.

Prerequisites

  • Set up an inbound entrypoint for the caller:
  • Create a routing rule that points the destination (phone number / WhatsApp / SIP username / app user alias) to this scenario.
  • Store your ElevenLabs API key in Voximplant Secrets under ELEVENLABS_API_KEY.
  • Store your ElevenLabs Agent ID in Voximplant Secrets under ELEVENLABS_AGENT_ID.
  • Optional: set ELEVENLABS_BASE_URL in the example if your ElevenLabs account should use a custom Agents endpoint.

Session setup

ElevenLabs Agents are configured in the ElevenLabs console. In VoxEngine, you only need the API key and agent ID.

In the full example, the client is created with:

Create Agents client
1const agentsClientParameters = {
2 xiApiKey: VoxEngine.getSecretValue("ELEVENLABS_API_KEY"),
3 agentId: VoxEngine.getSecretValue("ELEVENLABS_AGENT_ID"),
4};
5if (ELEVENLABS_BASE_URL) {
6 agentsClientParameters.baseUrl = ELEVENLABS_BASE_URL;
7}
8agentsClient = await ElevenLabs.createAgentsClient(agentsClientParameters);
Configure prompts and tools in ElevenLabs

Prompts, voices, and tools live in your ElevenLabs Agent configuration. Update them in the ElevenLabs console and reuse the same agent ID in VoxEngine.

Connect call audio

Once you have an ElevenLabs.AgentsClient, bridge audio both ways between the call and the agent:

Connect call audio
1VoxEngine.sendMediaBetween(call, agentsClient);

Barge-in

To keep the conversation interruption-friendly, the example listens for ElevenLabs.AgentsEvents.Interruption and clears the media buffer so any in-progress TTS audio is canceled when the caller starts talking:

Barge-in
1agentsClient.addEventListener(ElevenLabs.AgentsEvents.Interruption, () => {
2 agentsClient.clearMediaBuffer();
3});

Events

The scenario logs transcripts and key lifecycle events. For example:

Events (example from the scenario)
1agentsClient.addEventListener(ElevenLabs.AgentsEvents.UserTranscript, (event) => {
2 const payload = event?.data?.payload || event?.data || {};
3 const text = payload.text || payload.transcript || payload.user_transcript;
4 if (text) Logger.write(`USER: ${text}`);
5});

Notes

See the VoxEngine API Reference for more details.

Full VoxEngine scenario

voxeengine-elevenlabs-inbound.js
1/**
2 * Voximplant + ElevenLabs Agents connector demo
3 * Scenario: answer an incoming call and bridge it to ElevenLabs Agents.
4 */
5
6require(Modules.ElevenLabs);
7
8// Optional. Leave empty to use the default ElevenLabs Agents endpoint.
9const ELEVENLABS_BASE_URL = "";
10
11VoxEngine.addEventListener(AppEvents.CallAlerting, async ({call}) => {
12 let voiceAIClient;
13
14 // Termination handlers
15 call.addEventListener(CallEvents.Disconnected, () => VoxEngine.terminate());
16 call.addEventListener(CallEvents.Failed, () => VoxEngine.terminate());
17
18 try {
19 call.answer();
20 // call.record({hd_audio: true, stereo: true}); // Optional: record the call
21
22 // Create client and connect to ElevenLabs Agents
23 const agentsClientParameters = {
24 xiApiKey: VoxEngine.getSecretValue("ELEVENLABS_API_KEY"),
25 agentId: VoxEngine.getSecretValue("ELEVENLABS_AGENT_ID"),
26 onWebSocketClose: (event) => {
27 Logger.write("===ElevenLabs.WebSocket.Close===");
28 if (event) Logger.write(JSON.stringify(event));
29 VoxEngine.terminate();
30 },
31 };
32 if (ELEVENLABS_BASE_URL) {
33 agentsClientParameters.baseUrl = ELEVENLABS_BASE_URL;
34 }
35 voiceAIClient = await ElevenLabs.createAgentsClient(agentsClientParameters);
36
37 // Bridge media between the call and ElevenLabs Agents
38 VoxEngine.sendMediaBetween(call, voiceAIClient);
39
40 // ---------------------- Event handlers -----------------------
41 // Barge-in: keep conversation responsive
42 voiceAIClient.addEventListener(ElevenLabs.AgentsEvents.Interruption, () => {
43 Logger.write("===BARGE-IN: ElevenLabs.AgentsEvents.Interruption===");
44 voiceAIClient.clearMediaBuffer();
45 });
46
47 voiceAIClient.addEventListener(ElevenLabs.AgentsEvents.UserTranscript, (event) => {
48 const payload = event?.data?.payload || event?.data || {};
49 const text = payload.text || payload.transcript || payload.user_transcript;
50 if (text) {
51 Logger.write(`===USER=== ${text}`);
52 } else {
53 Logger.write("===USER_TRANSCRIPT===");
54 Logger.write(JSON.stringify(payload));
55 }
56 });
57
58 voiceAIClient.addEventListener(ElevenLabs.AgentsEvents.AgentResponse, (event) => {
59 const payload = event?.data?.payload || event?.data || {};
60 const text = payload.text || payload.response || payload.agent_response;
61 if (text) {
62 Logger.write(`===AGENT=== ${text}`);
63 } else {
64 Logger.write("===AGENT_RESPONSE===");
65 Logger.write(JSON.stringify(payload));
66 }
67 });
68
69 // Consolidated "log-only" handlers - key ElevenLabs/VoxEngine debugging events
70 [
71 ElevenLabs.AgentsEvents.ConversationInitiationMetadata,
72 ElevenLabs.AgentsEvents.AgentResponseCorrection,
73 ElevenLabs.AgentsEvents.ContextualUpdate,
74 ElevenLabs.AgentsEvents.AgentToolResponse,
75 ElevenLabs.AgentsEvents.VadScore,
76 ElevenLabs.AgentsEvents.Ping,
77 ElevenLabs.AgentsEvents.HTTPResponse,
78 ElevenLabs.AgentsEvents.WebSocketError,
79 ElevenLabs.AgentsEvents.ConnectorInformation,
80 ElevenLabs.AgentsEvents.Unknown,
81 ElevenLabs.Events.WebSocketMediaStarted,
82 ElevenLabs.Events.WebSocketMediaEnded,
83 ].forEach((eventName) => {
84 voiceAIClient.addEventListener(eventName, (event) => {
85 Logger.write(`===${event.name}===`);
86 if (event?.data) Logger.write(JSON.stringify(event.data));
87 });
88 });
89 } catch (error) {
90 Logger.write("===UNHANDLED_ERROR===");
91 Logger.write(error);
92 voiceAIClient?.close();
93 VoxEngine.terminate();
94 }
95});