*** ## title: 'Example: Placing an outbound call' ## Overview This example starts a VoxEngine session, places an outbound PSTN call, and connects the callee to Gemini Live API when the session is ready. **⬇️ 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 Gemini API key in Voximplant `ApplicationStorage` under `GEMINI_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) ## Notes * The example uses the Gemini Developer API (`Gemini.Backend.GEMINI_API`), not Vertex AI. * Audio is connected after `Gemini.LiveAPIEvents.SetupComplete` fires. [See the VoxEngine API Reference for more details](https://voximplant.com/docs/references/voxengine/gemini). ## Full VoxEngine scenario ```javascript title={"voxeengine-gemini-place-outbound-call.js"} maxLines={0} /** * Voximplant + Gemini Live API connector demo * Scenario: place an outbound PSTN call and bridge it to Gemini Live API. */ require(Modules.Gemini); 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). `; // -------------------- Gemini Live API settings -------------------- const CONNECT_CONFIG = { responseModalities: ["AUDIO"], systemInstruction: { parts: [{text: SYSTEM_PROMPT}], }, inputAudioTranscription: {}, outputAudioTranscription: {}, }; VoxEngine.addEventListener(AppEvents.Started, async () => { let voiceAIClient; let call; 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); // Termination functions - add cleanup and logging as needed call.addEventListener(CallEvents.Disconnected, VoxEngine.terminate); call.addEventListener(CallEvents.Failed, VoxEngine.terminate); call.addEventListener(CallEvents.Connected, async () => { // call.record({ hd_audio: true, stereo: true }); // Optional: record the call voiceAIClient = await Gemini.createLiveAPIClient({ apiKey: (await ApplicationStorage.get("GEMINI_API_KEY")).value, model: "gemini-2.5-flash-native-audio-preview-12-2025", backend: Gemini.Backend.GEMINI_API, connectConfig: CONNECT_CONFIG, onWebSocketClose: () => { Logger.write(`===Gemini.WebSocket.Close===`); VoxEngine.terminate(); }, }); voiceAIClient.addEventListener(Gemini.LiveAPIEvents.SetupComplete, () => { VoxEngine.sendMediaBetween(call, voiceAIClient); voiceAIClient.sendClientContent({ turns: [{role: "user", parts: [{text: "Say hello and ask how you can help."}]}], turnComplete: true, }); }); // Capture transcripts + handle barge-in voiceAIClient.addEventListener(Gemini.LiveAPIEvents.ServerContent, (event) => { const payload = event?.data?.payload || {}; if (payload.inputTranscription?.text) { Logger.write(`===USER=== ${payload.inputTranscription.text}`); } if (payload.outputTranscription?.text) { Logger.write(`===AGENT=== ${payload.outputTranscription.text}`); } if (payload.interrupted) { Logger.write("===BARGE-IN=== Gemini.LiveAPIEvents.ServerContent"); voiceAIClient.clearMediaBuffer(); } }); // Log all Gemini events for illustration/debugging [ Gemini.LiveAPIEvents.SetupComplete, Gemini.LiveAPIEvents.ServerContent, Gemini.LiveAPIEvents.ToolCall, Gemini.LiveAPIEvents.ToolCallCancellation, Gemini.LiveAPIEvents.ConnectorInformation, Gemini.LiveAPIEvents.Unknown, Gemini.Events.WebSocketMediaStarted, Gemini.Events.WebSocketMediaEnded, ].forEach((eventName) => { voiceAIClient.addEventListener(eventName, (event) => { Logger.write(`===${event.name}===`); if (event?.data) Logger.write(JSON.stringify(event.data)); }); }); }); } catch (error) { Logger.write("===SOMETHING_WENT_WRONG==="); Logger.write(error); voiceAIClient?.close(); VoxEngine.terminate(); } }); ```