*** ## title: 'Example: Placing an outbound call' 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](#full-voxengine-scenario).** ## Prerequisites * Create a routing rule with this scenario attached (outgoing calls don’t use patterns) so you can run it from the Control Panel or trigger it via API: [https://voximplant.com/docs/getting-started/basic-concepts/routing-rules](https://voximplant.com/docs/getting-started/basic-concepts/routing-rules) * Have a PSTN `destination` number you can dial (E.164 format), for example your mobile number. * Configure a valid outbound `callerId` (for example, a rented Voximplant number or a verified caller ID): [https://voximplant.com/docs/getting-started/basic-concepts/phone-numbers](https://voximplant.com/docs/getting-started/basic-concepts/phone-numbers) * Pass `destination` and `callerId` as routing rule custom data (this example reads them from `VoxEngine.customData()`): `{"destination":"+15551234567","callerId":"+15557654321"}`. * Store your Deepgram API key secret value in Voximplant `ApplicationStorage` under `DEEPGRAM_API_KEY`. `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](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`: ```json title="Custom data example" {"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](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: ```js title="Place the call" call = VoxEngine.callPSTN(destination, callerId); call.addEventListener(CallEvents.Connected, async () => { // ... }); ``` ## 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: ```js title="Create the client and bridge audio" voiceAgentClient = await Deepgram.createVoiceAgentClient({ apiKey, settingsOptions: SETTINGS_OPTIONS }); VoxEngine.sendMediaBetween(call, voiceAgentClient); ``` ## Barge-in To keep the conversation interruption-friendly, the example listens for `Deepgram.VoiceAgentEvents.UserStartedSpeaking` and clears the media buffer: ```js title="Barge-in" voiceAgentClient.addEventListener(Deepgram.VoiceAgentEvents.UserStartedSpeaking, () => { voiceAgentClient.clearMediaBuffer(); }); ``` ## 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: * Voice Agent events: [https://voximplant.com/docs/references/voxengine/deepgram/voiceagentevents](https://voximplant.com/docs/references/voxengine/deepgram/voiceagentevents) * WebSocket media events: [https://voximplant.com/docs/references/voxengine/deepgram/events](https://voximplant.com/docs/references/voxengine/deepgram/events) ## Notes [See the VoxEngine API Reference for more details](https://voximplant.com/docs/references/voxengine/deepgram). ## Full VoxEngine scenario ```javascript title={"voxeengine-deepgram-place-outbound-call.js"} maxLines={0} /** * Voximplant + Deepgram Voice Agent connector demo * Scenario: place an outbound PSTN call and bridge it to Deepgram Voice Agent. */ require(Modules.Deepgram); require(Modules.ApplicationStorage); const SYSTEM_PROMPT = ` You are Voxi, a helpful voice assistant for phone callers. Keep responses short and telephony-friendly (usually 1-2 sentences). `; // -------------------- Deepgram Voice Agent settings -------------------- const SETTINGS_OPTIONS = { tags: ["voximplant", "deepgram", "voice_agent_connector", "outbound_call_demo"], agent: { language: "en", greeting: "Hi! This is Voxi from Voximplant. How can I help today?", listen: { provider: { type: "deepgram", model: "flux-general-en", }, }, think: { provider: { type: "open_ai", model: "gpt-4o-mini", }, prompt: SYSTEM_PROMPT, }, speak: { provider: { type: "deepgram", model: "aura-2-cordelia-en", }, }, }, }; VoxEngine.addEventListener(AppEvents.Started, async () => { let call; let voiceAIClient; try { // This can be provided when manually running a routing rule in the Control Panel, // or via Management API using the `script_custom_data` parameter. // example: {"destination": "+15551234567","callerId": "+15557654321"} const {destination, callerId} = JSON.parse(VoxEngine.customData()); // Place the outbound call call = VoxEngine.callPSTN(destination, callerId); // Alternative outbound paths (uncomment to use): // call = VoxEngine.callUser({username: destination, callerid: callerId}); // call = VoxEngine.callSIP(`sip:${destination}@your-sip-domain`, callerId); // call = VoxEngine.callWhatsappUser({number: destination, callerid: callerId}); // Termination functions - add cleanup and logging as needed call.addEventListener(CallEvents.Failed, ()=>VoxEngine.terminate()); call.addEventListener(CallEvents.Disconnected, ()=>VoxEngine.terminate()); call.addEventListener(CallEvents.Connected, async () => { // Optional: record once connected call.record({hd_audio: true, stereo: true}); // Create client and wire media after the callee answers const apiKey = (await ApplicationStorage.get("DEEPGRAM_API_KEY")).value; voiceAIClient = await Deepgram.createVoiceAgentClient({ apiKey, settingsOptions: SETTINGS_OPTIONS, }); VoxEngine.sendMediaBetween(call, voiceAIClient); // Barge-in: keep conversation responsive voiceAIClient.addEventListener(Deepgram.VoiceAgentEvents.UserStartedSpeaking, () => { Logger.write("===BARGE-IN: Deepgram.VoiceAgentEvents.UserStartedSpeaking==="); voiceAIClient.clearMediaBuffer(); }); // Capture transcript voiceAIClient.addEventListener(Deepgram.VoiceAgentEvents.ConversationText, (event) => { const {role, text} = event?.data?.payload || {}; if (role && text) Logger.write(`===TRANSCRIPT=== ${role}: ${text}`); }); // Consolidated "log-only" handlers - key Deepgram/VoxEngine debugging events [ Deepgram.VoiceAgentEvents.Welcome, Deepgram.VoiceAgentEvents.SettingsApplied, Deepgram.VoiceAgentEvents.AgentThinking, Deepgram.VoiceAgentEvents.AgentAudioDone, Deepgram.VoiceAgentEvents.ConnectorInformation, Deepgram.VoiceAgentEvents.HTTPResponse, Deepgram.VoiceAgentEvents.Warning, Deepgram.VoiceAgentEvents.Error, Deepgram.VoiceAgentEvents.Unknown, Deepgram.Events.WebSocketMediaStarted, Deepgram.Events.WebSocketMediaEnded, ].forEach((eventName) => { voiceAIClient.addEventListener(eventName, (event) => { Logger.write(`===${event.name}===`); Logger.write(JSON.stringify(event)); }); }); }); } catch (e) { Logger.write("===UNHANDLED_ERROR==="); Logger.write(e); voiceAIClient?.close(); VoxEngine.terminate(); } }); ```