Example: Function calling
This example answers an inbound Voximplant call, connects it to ElevenLabs Agents, and handles client-side tool calls inside VoxEngine.
Jump to the Full VoxEngine scenario.
Prerequisites
- Store your ElevenLabs API key in Voximplant
ApplicationStorageunderELEVENLABS_API_KEY. - Store your ElevenLabs Agent ID in Voximplant
ApplicationStorageunderELEVENLABS_AGENT_ID. - Configure a client tool in your ElevenLabs Agent named
get_weatherthat expects alocationparameter.
Tool handling
ElevenLabs Agents send tool requests via ElevenLabs.AgentsEvents.ClientToolCall. The example extracts the tool name and parameters, runs a stub implementation, and sends the result back using clientToolResult:
Tool handling
1 agentsClient.addEventListener(ElevenLabs.AgentsEvents.ClientToolCall, (event) => { 2 const payload = event?.data?.payload || event?.data || {}; 3 const toolName = payload.tool_name || payload.toolName || payload.name; 4 const toolCallId = payload.tool_call_id || payload.toolCallId || payload.id; 5 6 if (toolName !== "get_weather") return; 7 8 const result = { location: "San Francisco", temperature_f: 72, condition: "sunny" }; 9 10 agentsClient.clientToolResult({ 11 tool_call_id: toolCallId, 12 tool_name: toolName, 13 result, 14 }); 15 });
Barge-in
Barge-in
1 agentsClient.addEventListener(ElevenLabs.AgentsEvents.Interruption, () => { 2 agentsClient.clearMediaBuffer(); 3 });
Notes
See the VoxEngine API Reference for more details.
Full VoxEngine scenario
voxeengine-elevenlabs-tool-call.js
1 /** 2 * Voximplant + ElevenLabs Agents connector demo 3 * Scenario: answer an incoming call and handle ElevenLabs tool calls. 4 */ 5 6 require(Modules.ElevenLabs); 7 require(Modules.ApplicationStorage); 8 9 const TOOL_NAME = "get_weather"; 10 11 VoxEngine.addEventListener(AppEvents.CallAlerting, async ({call}) => { 12 let voiceAIClient; 13 14 call.addEventListener(CallEvents.Disconnected, () => VoxEngine.terminate()); 15 call.addEventListener(CallEvents.Failed, () => VoxEngine.terminate()); 16 17 try { 18 call.answer(); 19 20 // Create client and connect to ElevenLabs Agents 21 voiceAIClient = await ElevenLabs.createAgentsClient({ 22 xiApiKey: (await ApplicationStorage.get("ELEVENLABS_API_KEY")).value, 23 agentId: (await ApplicationStorage.get("ELEVENLABS_AGENT_ID")).value, 24 onWebSocketClose: (event) => { 25 Logger.write("===ElevenLabs.WebSocket.Close==="); 26 if (event) Logger.write(JSON.stringify(event)); 27 VoxEngine.terminate(); 28 }, 29 }); 30 31 // Bridge media between the call and ElevenLabs Agents 32 VoxEngine.sendMediaBetween(call, voiceAIClient); 33 34 // ---------------------- Event handlers ----------------------- 35 // Barge-in: keep conversation responsive 36 voiceAIClient.addEventListener(ElevenLabs.AgentsEvents.Interruption, () => { 37 Logger.write("===BARGE-IN: ElevenLabs.AgentsEvents.Interruption==="); 38 voiceAIClient.clearMediaBuffer(); 39 }); 40 41 // Handle user transcripts 42 voiceAIClient.addEventListener(ElevenLabs.AgentsEvents.UserTranscript, (event) => { 43 const payload = event?.data?.payload || event?.data || {}; 44 const text = payload.text || payload.transcript || payload.user_transcript; 45 if (text) { 46 Logger.write(`===USER=== ${text}`); 47 } else { 48 Logger.write("===USER_TRANSCRIPT==="); 49 Logger.write(JSON.stringify(payload)); 50 } 51 }); 52 53 // Handle tool calls 54 voiceAIClient.addEventListener(ElevenLabs.AgentsEvents.ClientToolCall, (event) => { 55 const payload = event?.data?.payload || event?.data || {}; 56 const toolName = payload.tool_name || payload.toolName || payload.name; 57 const toolCallId = payload.tool_call_id || payload.toolCallId || payload.id; 58 59 if (!toolName || !toolCallId) { 60 Logger.write("===TOOL_CALL_MISSING_FIELDS==="); 61 Logger.write(JSON.stringify(payload)); 62 return; 63 } 64 65 let args = {}; 66 const rawArgs = payload.parameters || payload.args || payload.arguments; 67 if (typeof rawArgs === "string") { 68 try { 69 args = JSON.parse(rawArgs); 70 } catch (error) { 71 Logger.write("===TOOL_ARGS_PARSE_ERROR==="); 72 Logger.write(rawArgs); 73 Logger.write(error); 74 } 75 } else if (rawArgs && typeof rawArgs === "object") { 76 args = rawArgs; 77 } 78 79 if (toolName !== TOOL_NAME) { 80 voiceAIClient.clientToolResult({ 81 tool_call_id: toolCallId, 82 tool_name: toolName, 83 result: {error: `Unhandled tool: ${toolName}`}, 84 }); 85 return; 86 } 87 88 const location = args.location || "Unknown"; 89 const result = { 90 location, 91 temperature_f: 72, 92 condition: "sunny", 93 }; 94 95 voiceAIClient.clientToolResult({ 96 tool_call_id: toolCallId, 97 tool_name: toolName, 98 result, 99 }); 100 }); 101 102 voiceAIClient.addEventListener(ElevenLabs.AgentsEvents.AgentResponse, (event) => { 103 const payload = event?.data?.payload || event?.data || {}; 104 const text = payload.text || payload.response || payload.agent_response; 105 if (text) { 106 Logger.write(`===AGENT=== ${text}`); 107 } else { 108 Logger.write("===AGENT_RESPONSE==="); 109 Logger.write(JSON.stringify(payload)); 110 } 111 }); 112 113 // Consolidated "log-only" handlers - key ElevenLabs/VoxEngine debugging events 114 [ 115 ElevenLabs.AgentsEvents.ConversationInitiationMetadata, 116 ElevenLabs.AgentsEvents.AgentResponseCorrection, 117 ElevenLabs.AgentsEvents.ContextualUpdate, 118 ElevenLabs.AgentsEvents.AgentToolResponse, 119 ElevenLabs.AgentsEvents.VadScore, 120 ElevenLabs.AgentsEvents.Ping, 121 ElevenLabs.AgentsEvents.HTTPResponse, 122 ElevenLabs.AgentsEvents.WebSocketError, 123 ElevenLabs.AgentsEvents.ConnectorInformation, 124 ElevenLabs.AgentsEvents.Unknown, 125 ElevenLabs.Events.WebSocketMediaStarted, 126 ElevenLabs.Events.WebSocketMediaEnded, 127 ].forEach((eventName) => { 128 voiceAIClient.addEventListener(eventName, (event) => { 129 Logger.write(`===${event.name}===`); 130 if (event?.data) Logger.write(JSON.stringify(event.data)); 131 }); 132 }); 133 } catch (error) { 134 Logger.write("===UNHANDLED_ERROR==="); 135 Logger.write(error); 136 voiceAIClient?.close(); 137 VoxEngine.terminate(); 138 } 139 });