Example: Function calling

View as MarkdownOpen in Claude

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 ApplicationStorage under ELEVENLABS_API_KEY.
  • Store your ElevenLabs Agent ID in Voximplant ApplicationStorage under ELEVENLABS_AGENT_ID.
  • Configure a client tool in your ElevenLabs Agent named get_weather that expects a location parameter.

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
1agentsClient.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
1agentsClient.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
6require(Modules.ElevenLabs);
7require(Modules.ApplicationStorage);
8
9const TOOL_NAME = "get_weather";
10
11VoxEngine.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});