Example: Placing an outbound call

View as Markdown

For the complete documentation index, see llms.txt.

This example starts a VoxEngine session, places an outbound PSTN call, and bridges audio to ElevenLabs Agents once the callee answers.

Jump to the Full VoxEngine scenario.

Prerequisites

  • 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.
  • Ensure outbound calling is enabled for your Voximplant application and that your caller ID is verified.

Outbound call parameters

The example expects destination and caller ID in customData (read via VoxEngine.customData()):

Custom data example
1{"destination":"+15551234567","callerId":"+15557654321"}

Launch the routing rule

For quick testing, you can start this outbound scenario from the Voximplant Control Panel:

  1. Open your Voximplant application and go to the Routing tab.
  2. Select the routing rule that has this scenario attached.
  3. Click Run.
  4. Provide Custom data (max 200 bytes) with destination and callerId:
Custom data example
1{"destination":"+15551234567","callerId":"+15557654321"}

For production, start the routing rule via the Management API startScenarios method (pass rule_id, and pass the same JSON string in script_custom_data).

Alternate outbound destinations

This example uses VoxEngine.callPSTN(...) for PSTN dialing. You can also route outbound calls to other destination types in VoxEngine:

  • SIP (VoxEngine.callSIP): dial a SIP URI to reach a PBX, carrier, SIP trunk, or other SIP endpoint.
  • WhatsApp (VoxEngine.callWhatsappUser): place a WhatsApp Business-initiated call (requires a WhatsApp Business account and enabled numbers).
  • Voximplant users (VoxEngine.callUser): calls another app user inside the same Voximplant application such as web SDK, mobile SDK, or SIP user.

Relevant guides:

Connect call audio

After the callee answers, the example creates an ElevenLabs.AgentsClient and bridges audio:

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

Barge-in

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

Notes

See the VoxEngine API Reference for more details.

Full VoxEngine scenario

voxeengine-elevenlabs-outbound.js
1/**
2 * Voximplant + ElevenLabs Agents connector demo
3 * Scenario: place an outbound PSTN call and bridge it to ElevenLabs Agents.
4 */
5
6require(Modules.ElevenLabs);
7
8const MAX_CALL_MS = 2 * 60 * 1000; // Maximum call duration of 2 minutes
9// Optional. Leave empty to use the default ElevenLabs Agents endpoint.
10const ELEVENLABS_BASE_URL = "";
11
12VoxEngine.addEventListener(AppEvents.Started, async () => {
13 let call;
14 let voiceAIClient;
15 let hangupTimer;
16
17 try {
18 // This can be provided when manually running a routing rule in the Control Panel,
19 // or via Management API using the `script_custom_data` parameter.
20 // example: {"destination": "+15551234567", "callerId": "+15557654321"}
21 const {destination, callerId} = JSON.parse(VoxEngine.customData());
22
23 call = VoxEngine.callPSTN(destination, callerId);
24 // Alternative outbound paths (uncomment to use):
25 // call = VoxEngine.callUser({username: destination, callerid: callerId});
26 // call = VoxEngine.callSIP(`sip:${destination}@your-sip-domain`, callerId);
27 // call = VoxEngine.callWhatsappUser({number: destination, callerid: callerId});
28
29 call.addEventListener(CallEvents.Failed, () => VoxEngine.terminate());
30 call.addEventListener(CallEvents.Disconnected, () => {
31 if (hangupTimer) clearTimeout(hangupTimer);
32 VoxEngine.terminate();
33 });
34
35 call.addEventListener(CallEvents.Connected, async () => {
36 hangupTimer = setTimeout(() => {
37 Logger.write("===HANGUP_TIMER===");
38 call.hangup();
39 }, MAX_CALL_MS);
40
41 // Create client and connect to ElevenLabs Agents
42 const agentsClientParameters = {
43 xiApiKey: VoxEngine.getSecretValue("ELEVENLABS_API_KEY"),
44 agentId: VoxEngine.getSecretValue("ELEVENLABS_AGENT_ID"),
45 onWebSocketClose: (event) => {
46 Logger.write("===ElevenLabs.WebSocket.Close===");
47 if (event) Logger.write(JSON.stringify(event));
48 VoxEngine.terminate();
49 },
50 };
51 if (ELEVENLABS_BASE_URL) {
52 agentsClientParameters.baseUrl = ELEVENLABS_BASE_URL;
53 }
54 voiceAIClient = await ElevenLabs.createAgentsClient(agentsClientParameters);
55
56 // Bridge media between the call and ElevenLabs Agents
57 VoxEngine.sendMediaBetween(call, voiceAIClient);
58
59 // ---------------------- Event handlers -----------------------
60 // Barge-in: keep conversation responsive
61 voiceAIClient.addEventListener(ElevenLabs.AgentsEvents.Interruption, () => {
62 Logger.write("===BARGE-IN: ElevenLabs.AgentsEvents.Interruption===");
63 voiceAIClient.clearMediaBuffer();
64 });
65
66 voiceAIClient.addEventListener(ElevenLabs.AgentsEvents.UserTranscript, (event) => {
67 const payload = event?.data?.payload || event?.data || {};
68 const text = payload.text || payload.transcript || payload.user_transcript;
69 if (text) {
70 Logger.write(`===USER=== ${text}`);
71 } else {
72 Logger.write("===USER_TRANSCRIPT===");
73 Logger.write(JSON.stringify(payload));
74 }
75 });
76
77 voiceAIClient.addEventListener(ElevenLabs.AgentsEvents.AgentResponse, (event) => {
78 const payload = event?.data?.payload || event?.data || {};
79 const text = payload.text || payload.response || payload.agent_response;
80 if (text) {
81 Logger.write(`===AGENT=== ${text}`);
82 } else {
83 Logger.write("===AGENT_RESPONSE===");
84 Logger.write(JSON.stringify(payload));
85 }
86 });
87
88 // Consolidated "log-only" handlers - key ElevenLabs/VoxEngine debugging events
89 [
90 ElevenLabs.AgentsEvents.ConversationInitiationMetadata,
91 ElevenLabs.AgentsEvents.AgentResponseCorrection,
92 ElevenLabs.AgentsEvents.ContextualUpdate,
93 ElevenLabs.AgentsEvents.AgentToolResponse,
94 ElevenLabs.AgentsEvents.VadScore,
95 ElevenLabs.AgentsEvents.Ping,
96 ElevenLabs.AgentsEvents.HTTPResponse,
97 ElevenLabs.AgentsEvents.WebSocketError,
98 ElevenLabs.AgentsEvents.ConnectorInformation,
99 ElevenLabs.AgentsEvents.Unknown,
100 ElevenLabs.Events.WebSocketMediaStarted,
101 ElevenLabs.Events.WebSocketMediaEnded,
102 ].forEach((eventName) => {
103 voiceAIClient.addEventListener(eventName, (event) => {
104 Logger.write(`===${event.name}===`);
105 if (event?.data) Logger.write(JSON.stringify(event.data));
106 });
107 });
108 });
109 } catch (error) {
110 Logger.write("===UNHANDLED_ERROR===");
111 Logger.write(error);
112 voiceAIClient?.close();
113 VoxEngine.terminate();
114 }
115});