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 Deepgram Voice Agent once the callee answers.

⬇️ Jump to the Full VoxEngine scenario.

Prerequisites

About outbound Caller ID

VoxEngine.callPSTN(...) requires a valid callback-capable caller ID (for example, a rented Voximplant number or a verified caller ID). See https://voximplant.com/docs/getting-started/basic-concepts/phone-numbers.

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 Management API startScenarios (pass rule_id, and pass the same JSON string in script_custom_data): https://voximplant.com/docs/references/httpapi/scenarios#startscenarios

Place the outbound call

Outbound calls are placed with VoxEngine.callPSTN(number, callerid, parameters?).

In the full example, see VoxEngine.customData(), the destination / callerId parse, and the AppEvents.Started handler:

Place the call
1call = VoxEngine.callPSTN(destination, callerId);
2call.addEventListener(CallEvents.Connected, async () => {
3 // ...
4});

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:

Session setup

As in the inbound example, the Deepgram Voice Agent session is configured via a settingsOptions object passed to Deepgram.createVoiceAgentClient(...).

In the full example, see SETTINGS_OPTIONS.agent:

  • listen: speech-to-text provider
  • think: LLM provider + prompt
  • speak: text-to-speech provider

Connect call audio

For outbound, it’s typically best to create the Voice Agent and bridge audio only after the callee answers (so the agent doesn’t speak into ringback).

In the example, the CallEvents.Connected handler does:

Create the client and bridge audio
1voiceAgentClient = await Deepgram.createVoiceAgentClient({ apiKey, settingsOptions: SETTINGS_OPTIONS });
2VoxEngine.sendMediaBetween(call, voiceAgentClient);

Barge-in

To keep the conversation interruption-friendly, the example listens for Deepgram.VoiceAgentEvents.UserStartedSpeaking and clears the media buffer:

Barge-in
1voiceAgentClient.addEventListener(Deepgram.VoiceAgentEvents.UserStartedSpeaking, () => {
2 voiceAgentClient.clearMediaBuffer();
3});

Events

The scenario logs a transcript example via Deepgram.VoiceAgentEvents.ConversationText and also logs a small set of lifecycle/debug events.

Available events are documented in the Voximplant references:

Notes

See the VoxEngine API Reference for more details.

Full VoxEngine scenario

voxeengine-deepgram-place-outbound-call.js
1/**
2 * Voximplant + Deepgram Voice Agent connector demo
3 * Scenario: place an outbound PSTN call and bridge it to Deepgram Voice Agent.
4 */
5
6require(Modules.Deepgram);
7const SYSTEM_PROMPT = `
8You are Voxi, a helpful voice assistant for phone callers.
9Keep responses short and telephony-friendly (usually 1-2 sentences).
10`;
11
12// -------------------- Deepgram Voice Agent settings --------------------
13const SETTINGS_OPTIONS = {
14 tags: ["voximplant", "deepgram", "voice_agent_connector", "outbound_call_demo"],
15 agent: {
16 language: "en",
17 greeting: "Hi! This is Voxi from Voximplant. How can I help today?",
18 listen: {
19 provider: {
20 type: "deepgram",
21 model: "flux-general-en",
22 },
23 },
24 think: {
25 provider: {
26 type: "open_ai",
27 model: "gpt-4o-mini",
28 },
29 prompt: SYSTEM_PROMPT,
30 },
31 speak: {
32 provider: {
33 type: "deepgram",
34 model: "aura-2-cordelia-en",
35 },
36 },
37 },
38};
39
40VoxEngine.addEventListener(AppEvents.Started, async () => {
41 let call;
42 let voiceAIClient;
43
44 try {
45 // This can be provided when manually running a routing rule in the Control Panel,
46 // or via Management API using the `script_custom_data` parameter.
47 // example: {"destination": "+15551234567","callerId": "+15557654321"}
48 const {destination, callerId} = JSON.parse(VoxEngine.customData());
49
50 // Place the outbound call
51 call = VoxEngine.callPSTN(destination, callerId);
52 // Alternative outbound paths (uncomment to use):
53 // call = VoxEngine.callUser({username: destination, callerid: callerId});
54 // call = VoxEngine.callSIP(`sip:${destination}@your-sip-domain`, callerId);
55 // call = VoxEngine.callWhatsappUser({number: destination, callerid: callerId});
56
57 // Termination functions - add cleanup and logging as needed
58 call.addEventListener(CallEvents.Failed, ()=>VoxEngine.terminate());
59 call.addEventListener(CallEvents.Disconnected, ()=>VoxEngine.terminate());
60
61 call.addEventListener(CallEvents.Connected, async () => {
62 // Optional: record once connected
63 call.record({hd_audio: true, stereo: true});
64
65 // Create client and wire media after the callee answers
66 const apiKey = VoxEngine.getSecretValue('DEEPGRAM_API_KEY');
67 voiceAIClient = await Deepgram.createVoiceAgentClient({
68 apiKey,
69 settingsOptions: SETTINGS_OPTIONS,
70 });
71 VoxEngine.sendMediaBetween(call, voiceAIClient);
72
73 // Barge-in: keep conversation responsive
74 voiceAIClient.addEventListener(Deepgram.VoiceAgentEvents.UserStartedSpeaking, () => {
75 Logger.write("===BARGE-IN: Deepgram.VoiceAgentEvents.UserStartedSpeaking===");
76 voiceAIClient.clearMediaBuffer();
77 });
78
79 // Capture transcript
80 voiceAIClient.addEventListener(Deepgram.VoiceAgentEvents.ConversationText, (event) => {
81 const {role, text} = event?.data?.payload || {};
82 if (role && text) Logger.write(`===TRANSCRIPT=== ${role}: ${text}`);
83 });
84
85 // Consolidated "log-only" handlers - key Deepgram/VoxEngine debugging events
86 [
87 Deepgram.VoiceAgentEvents.Welcome,
88 Deepgram.VoiceAgentEvents.SettingsApplied,
89 Deepgram.VoiceAgentEvents.AgentThinking,
90 Deepgram.VoiceAgentEvents.AgentAudioDone,
91 Deepgram.VoiceAgentEvents.ConnectorInformation,
92 Deepgram.VoiceAgentEvents.HTTPResponse,
93 Deepgram.VoiceAgentEvents.Warning,
94 Deepgram.VoiceAgentEvents.Error,
95 Deepgram.VoiceAgentEvents.Unknown,
96 Deepgram.Events.WebSocketMediaStarted,
97 Deepgram.Events.WebSocketMediaEnded,
98 ].forEach((eventName) => {
99 voiceAIClient.addEventListener(eventName, (event) => {
100 Logger.write(`===${event.name}===`);
101 Logger.write(JSON.stringify(event));
102 });
103 });
104 });
105 } catch (e) {
106 Logger.write("===UNHANDLED_ERROR===");
107 Logger.write(e);
108 voiceAIClient?.close();
109 VoxEngine.terminate();
110 }
111});