Example: Function calling

View as Markdown

For the complete documentation index, see llms.txt.

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

  • Set up an inbound entrypoint for the caller:
  • Create a routing rule that points the destination (phone number / WhatsApp / SIP username / app user alias) to this scenario.
  • Store your ElevenLabs API key in Voximplant Secrets under ELEVENLABS_API_KEY.
  • Store your ElevenLabs Agent ID in Voximplant Secrets under ELEVENLABS_AGENT_ID.
  • Optional: set ELEVENLABS_BASE_URL in the example if your ElevenLabs account should use a custom Agents endpoint - i.e.: "https://api.eu.residency.elevenlabs.io"
  • 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);
7
8const TOOL_NAME = "get_weather";
9// Optional. Leave empty to use the default ElevenLabs Agents endpoint.
10const ELEVENLABS_BASE_URL = "";
11
12VoxEngine.addEventListener(AppEvents.CallAlerting, async ({call}) => {
13 let voiceAIClient;
14
15 call.addEventListener(CallEvents.Disconnected, () => VoxEngine.terminate());
16 call.addEventListener(CallEvents.Failed, () => VoxEngine.terminate());
17
18 try {
19 call.answer();
20
21 // Create client and connect to ElevenLabs Agents
22 const agentsClientParameters = {
23 xiApiKey: VoxEngine.getSecretValue("ELEVENLABS_API_KEY"),
24 agentId: VoxEngine.getSecretValue("ELEVENLABS_AGENT_ID"),
25 onWebSocketClose: (event) => {
26 Logger.write("===ElevenLabs.WebSocket.Close===");
27 if (event) Logger.write(JSON.stringify(event));
28 VoxEngine.terminate();
29 },
30 };
31 if (ELEVENLABS_BASE_URL) {
32 agentsClientParameters.baseUrl = ELEVENLABS_BASE_URL;
33 }
34 voiceAIClient = await ElevenLabs.createAgentsClient(agentsClientParameters);
35
36 // Bridge media between the call and ElevenLabs Agents
37 VoxEngine.sendMediaBetween(call, voiceAIClient);
38
39 // ---------------------- Event handlers -----------------------
40 // Barge-in: keep conversation responsive
41 voiceAIClient.addEventListener(ElevenLabs.AgentsEvents.Interruption, () => {
42 Logger.write("===BARGE-IN: ElevenLabs.AgentsEvents.Interruption===");
43 voiceAIClient.clearMediaBuffer();
44 });
45
46 // Handle user transcripts
47 voiceAIClient.addEventListener(ElevenLabs.AgentsEvents.UserTranscript, (event) => {
48 const payload = event?.data?.payload || event?.data || {};
49 const text = payload.text || payload.transcript || payload.user_transcript;
50 if (text) {
51 Logger.write(`===USER=== ${text}`);
52 } else {
53 Logger.write("===USER_TRANSCRIPT===");
54 Logger.write(JSON.stringify(payload));
55 }
56 });
57
58 // Handle tool calls
59 voiceAIClient.addEventListener(ElevenLabs.AgentsEvents.ClientToolCall, (event) => {
60 const payload = event?.data?.payload || event?.data || {};
61 const toolName = payload.tool_name || payload.toolName || payload.name;
62 const toolCallId = payload.tool_call_id || payload.toolCallId || payload.id;
63
64 if (!toolName || !toolCallId) {
65 Logger.write("===TOOL_CALL_MISSING_FIELDS===");
66 Logger.write(JSON.stringify(payload));
67 return;
68 }
69
70 let args = {};
71 const rawArgs = payload.parameters || payload.args || payload.arguments;
72 if (typeof rawArgs === "string") {
73 try {
74 args = JSON.parse(rawArgs);
75 } catch (error) {
76 Logger.write("===TOOL_ARGS_PARSE_ERROR===");
77 Logger.write(rawArgs);
78 Logger.write(error);
79 }
80 } else if (rawArgs && typeof rawArgs === "object") {
81 args = rawArgs;
82 }
83
84 if (toolName !== TOOL_NAME) {
85 voiceAIClient.clientToolResult({
86 tool_call_id: toolCallId,
87 tool_name: toolName,
88 result: {error: `Unhandled tool: ${toolName}`},
89 });
90 return;
91 }
92
93 const location = args.location || "Unknown";
94 const result = {
95 location,
96 temperature_f: 72,
97 condition: "sunny",
98 };
99
100 voiceAIClient.clientToolResult({
101 tool_call_id: toolCallId,
102 tool_name: toolName,
103 result,
104 });
105 });
106
107 voiceAIClient.addEventListener(ElevenLabs.AgentsEvents.AgentResponse, (event) => {
108 const payload = event?.data?.payload || event?.data || {};
109 const text = payload.text || payload.response || payload.agent_response;
110 if (text) {
111 Logger.write(`===AGENT=== ${text}`);
112 } else {
113 Logger.write("===AGENT_RESPONSE===");
114 Logger.write(JSON.stringify(payload));
115 }
116 });
117
118 // Consolidated "log-only" handlers - key ElevenLabs/VoxEngine debugging events
119 [
120 ElevenLabs.AgentsEvents.ConversationInitiationMetadata,
121 ElevenLabs.AgentsEvents.AgentResponseCorrection,
122 ElevenLabs.AgentsEvents.ContextualUpdate,
123 ElevenLabs.AgentsEvents.AgentToolResponse,
124 ElevenLabs.AgentsEvents.VadScore,
125 ElevenLabs.AgentsEvents.Ping,
126 ElevenLabs.AgentsEvents.HTTPResponse,
127 ElevenLabs.AgentsEvents.WebSocketError,
128 ElevenLabs.AgentsEvents.ConnectorInformation,
129 ElevenLabs.AgentsEvents.Unknown,
130 ElevenLabs.Events.WebSocketMediaStarted,
131 ElevenLabs.Events.WebSocketMediaEnded,
132 ].forEach((eventName) => {
133 voiceAIClient.addEventListener(eventName, (event) => {
134 Logger.write(`===${event.name}===`);
135 if (event?.data) Logger.write(JSON.stringify(event.data));
136 });
137 });
138 } catch (error) {
139 Logger.write("===UNHANDLED_ERROR===");
140 Logger.write(error);
141 voiceAIClient?.close();
142 VoxEngine.terminate();
143 }
144});