*** ## title: 'Example: Placing an outbound call' ## Overview This example starts a VoxEngine session, places an outbound PSTN call, and connects the callee to a Grok voice agent when the media WebSocket is ready. **⬇️ Jump to the [Full VoxEngine scenario](#full-voxengine-scenario).** ## Prerequisites * A Grok API key stored in `ApplicationStorage` as `XAI_API_KEY`. * A verified or rented caller ID that can place outbound PSTN calls. * Start this scenario via a routing rule (for production use the Management API to start the rule). ## Custom data Pass the destination and caller ID when starting the scenario: ```json {"destination":"+16174018889","callerId":"+13322776633"} ``` ## Connect call audio Bridge the PSTN call to Grok once the WebSocket media session is live: ```js VoxEngine.sendMediaBetween(call, voiceAgentAPIClient); ``` ## Notes [See the VoxEngine API Reference for more details](https://voximplant.com/docs/references/voxengine/grok). ## Full VoxEngine scenario ```javascript title={"voxeengine-grok-place-outbound-call.js"} maxLines={0} require(Modules.Grok); require(Modules.ApplicationStorage); const SYSTEM_PROMPT = ` Your name is Voxi. You are a helpful voice assistant for phone callers representing the company Voximplant (pronounced VOX-im-plant). Keep responses short and telephony-friendly (usually 1-2 sentences). If the user asks for a live agent or an operator, call the "forward_to_agent" function. If the user says goodbye, call the "hangup_call" function. `; // -------------------- Grok Voice Agent settings -------------------- const SESSION_PARAMETERS = { session: { voice: "Ara", turn_detection: {type: "server_vad"}, instructions: SYSTEM_PROMPT, tools: [ {type: "web_search"}, { type: "file_search", vector_store_ids: ["collection_4c5a63ab-f739-4c13-93d2-05b74095c34a"], // optional RAG max_num_results: 5, }, { type: "x_search", allowed_x_handles: ["voximplant", "aylarov"], }, { type: "function", name: "forward_to_agent", description: "Forward the user to a live agent", parameters: { type: "object", properties: {}, required: [], }, }, { type: "function", name: "hangup_call", description: "Hangup the call", parameters: { type: "object", properties: {}, required: [], }, }, ], }, }; // Helper to parse custom data JSON from call setup function parseCustomData() { const raw = VoxEngine.customData(); if (!raw) return {}; try { return JSON.parse(raw); } catch (error) { Logger.write("===CUSTOM_DATA_PARSE_FAILED==="); Logger.write(error); return {}; } } VoxEngine.addEventListener(AppEvents.Started, async () => { let voiceAIClient; let call; let hangupCall = false; let forwardToLiveAgent = false; const {destination, callerId} = parseCustomData(); if (!destination || !callerId) { Logger.write( "Missing destination or callerId. Provide custom data: {\"destination\":\"+16174018889\",\"callerId\":\"+13322776633\"}", ); VoxEngine.terminate(); return; } 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()); try { voiceAIClient = await Grok.createVoiceAgentAPIClient({ xAIApiKey: (await ApplicationStorage.get("XAI_API_KEY")).value, onWebSocketClose: (event) => { Logger.write(`===${event.name}===>${JSON.stringify(event.data)}`); VoxEngine.terminate(); }, }); voiceAIClient.addEventListener(Grok.VoiceAgentAPIEvents.ConversationCreated, (event) => { Logger.write(`===${event.name}===>${JSON.stringify(event.data)}`); voiceAIClient.sessionUpdate(SESSION_PARAMETERS); }); voiceAIClient.addEventListener(Grok.VoiceAgentAPIEvents.SessionUpdated, (event) => { Logger.write(`===${event.name}===>${JSON.stringify(event.data)}`); voiceAIClient.responseCreate({instructions: "Hello."}); }); // Keep it interruption-friendly (barge-in). voiceAIClient.addEventListener(Grok.VoiceAgentAPIEvents.InputAudioBufferSpeechStarted, (event) => { Logger.write(`===${event.name}===>${JSON.stringify(event.data)}`); voiceAIClient.clearMediaBuffer(); }); // Function calling. voiceAIClient.addEventListener(Grok.VoiceAgentAPIEvents.ResponseFunctionCallArgumentsDone, (event) => { Logger.write(`===${event.name}===>${JSON.stringify(event.data)}`); const {name, call_id} = event?.data?.payload || {}; let output; if (name !== "forward_to_agent" && name !== "hangup_call") { Logger.write(`===Ignoring unhandled function call: ${name}===`); return; } if (name === "forward_to_agent") { forwardToLiveAgent = true; output = {result: "Forwarding your call to a live agent. Please hold on."}; } else if (name === "hangup_call") { hangupCall = true; output = {result: "Have a great day, goodbye!"}; } voiceAIClient.conversationItemCreate({ item: { type: "function_call_output", call_id, output: JSON.stringify(output), }, }); voiceAIClient.responseCreate({}); }); // -------------------- Log Other Events -------------------- [ CallEvents.FirstAudioPacketReceived, Grok.Events.WebSocketMediaStarted, Grok.VoiceAgentAPIEvents.InputAudioBufferSpeechStopped, Grok.VoiceAgentAPIEvents.ConversationItemInputAudioTranscriptionCompleted, Grok.VoiceAgentAPIEvents.ConversationItemAdded, Grok.VoiceAgentAPIEvents.ResponseCreated, Grok.VoiceAgentAPIEvents.ResponseOutputItemAdded, Grok.VoiceAgentAPIEvents.ResponseDone, Grok.VoiceAgentAPIEvents.ResponseOutputAudioTranscriptDelta, Grok.VoiceAgentAPIEvents.ResponseOutputAudioTranscriptDone, Grok.VoiceAgentAPIEvents.ResponseOutputAudioDelta, Grok.VoiceAgentAPIEvents.ResponseOutputAudioDone, Grok.VoiceAgentAPIEvents.ResponseOutputItemDone, Grok.VoiceAgentAPIEvents.ConnectorInformation, Grok.VoiceAgentAPIEvents.InputAudioBufferCommitted, Grok.VoiceAgentAPIEvents.WebSocketError, Grok.VoiceAgentAPIEvents.Unknown, ].forEach((evtName) => { voiceAIClient.addEventListener(evtName, (e) => { Logger.write(`===${e.name}===>${JSON.stringify(e)}`); }); }); voiceAIClient.addEventListener(Grok.Events.WebSocketMediaStarted, () => { VoxEngine.sendMediaBetween(call, voiceAIClient); }); voiceAIClient.addEventListener(Grok.Events.WebSocketMediaEnded, (event) => { Logger.write(`===${event.name}===>${JSON.stringify(event.data)}`); if (hangupCall) callCloseHandler(); else if (forwardToLiveAgent) { call.say("Here is where I would forward the call via the phone network or SIP."); call.addEventListener(CallEvents.PlaybackFinished, callCloseHandler); } else return; }); } catch (error) { Logger.write("===SOMETHING_WENT_WRONG==="); Logger.write(error); VoxEngine.terminate(); } }); ```