Executing Tools
Handle tool calls at runtime — implicit calls in your client, explicit calls on your server, and what to do about slow work
When the agent invokes a tool mid-conversation, the call is delivered to you and your code returns the result. Where it's delivered depends on the tool's execution flow: implicit calls go to your connected client, explicit calls go to a URL on your server.
Implicit tool calls
Receive the call in your client and send the output back
Timeout
The 10-second window, and how to handle slow work
Explicit tool calls
HTTP and WebSocket endpoints on your server
WebSocket Tools
Live conversation feed, UI pushes, deferred results — what a wss:// tool unlocks
Implicit tool calls
When an implicit tool is invoked during a session, the Napster API sends an event to your connected client with the tool call details. Your client handles the logic and sends the result back. This works over both WebRTC (via the Web SDK) and WebSocket connections.
Receiving a tool call
When the agent invokes an implicit tool, the server delivers a function_implicitly_called event to your client:
{
"type": "function_implicitly_called",
"data": {
"item_id": "item_001",
"call_id": "call_abc123",
"name": "get_order_status",
"arguments": {
"order_id": "ORD-7890"
}
}
}| Field | Description |
|---|---|
item_id | The ID of the conversation item that triggered the tool call |
call_id | A unique identifier for this specific tool call — use this when sending the response |
name | The name of the tool being invoked |
arguments | The parameters the agent populated for the tool call |
Sending the tool output
Once you've processed the tool call, send the result back with a send_function_output event:
{
"type": "send_function_output",
"data": {
"call_id": "call_abc123",
"output": {
"status": "shipped",
"tracking_number": "1Z999AA10123456784"
},
"delay": false
}
}| Field | Description |
|---|---|
call_id | The call_id from the function_implicitly_called event |
output | The result of the tool call — this is passed back to the agent |
delay | When true, the response is held until the agent finishes speaking before being processed |
Timeout
Tool calls have a default timeout of 10 seconds. If no response is received within that window, the Napster API sends "Failed to fetch information" to the model as the tool result and delivers a function_call_timeout event to your client.
For work that can't finish inside the window, respond with an interim result right away (so the agent can tell the user the result is on its way) and deliver the real outcome afterwards. The rule: the late outcome is delivered by whoever handled the tool call, over the connection it already holds. How that looks depends on the kind of tool — not on the session channel:
| Tool | Who handled the call | Delivering the late outcome |
|---|---|---|
| Implicit | Your client | The client injects it with a send_message command — see the example below. |
| Explicit, WebSocket URL | Your functions server | The server sends context_update on the tool connection. Works whether the session channel is WebRTC or WebSocket. |
| Explicit, HTTP URL | Your server, per-request | No persistent connection to push through — the work must fit inside the timeout. If it can't, switch the tool to a WebSocket URL. |
Deferred results from an implicit tool
Your client handled the call, so it also delivers the outcome — using the session connection it already holds. First answer the tool call with an interim result so the agent can say the work has started; later, inject the real outcome as a system message with trigger_response: true so the agent announces it.
On WebRTC, both go through the Web SDK instance:
// 1. Beat the timeout — answer the tool call with an interim result
instance.sendCommand({
type: "send_function_output",
data: {
call_id,
output: { status: "working", message: "Generating the report now — it'll be ready shortly." },
},
});
// 2. Later, when the real work finishes — inject the outcome
instance.sendCommand({
type: "send_message",
data: {
role: "system",
text: "The report is ready: 1,204 rows. Tell the user and offer to walk through it.",
trigger_response: true,
},
});On a WebSocket session channel, the messages are identical — your client just writes them to the socket:
// 1. Interim result
ws.send(JSON.stringify({
type: "send_function_output",
data: { call_id, output: { status: "working", message: "Generating the report now." } },
}));
// 2. Later — the real outcome
ws.send(JSON.stringify({
type: "send_message",
data: { role: "system", text: "The report is ready: 1,204 rows.", trigger_response: true },
}));Explicit tool calls
When a tool's flow is "explicit", the tool call is forwarded to the url you configured when creating it — either an HTTP endpoint or a WebSocket endpoint on your server, with any custom headers you set.
The flow is the same regardless of protocol:
- The LLM decides to call the tool based on the conversation and populates the parameters.
- The Napster API delivers the tool call to the
urlyou configured, including the tool name and populated arguments. - Your server executes the logic — for example, querying a database, calling an internal API, or triggering an action in a third-party system — and returns the result.
- The Napster API passes the result back to the LLM, which uses it to continue the conversation with the user.
Using an HTTP endpoint:
{
"flow": "explicit",
"url": "https://your-api.example.com/tools/get-order-status",
"headers": {
"x-api-key": "your-internal-api-key"
}
}With an HTTP URL, the platform makes a request to your endpoint per tool call. Simple to set up — but there's no connection outside of tool calls.
Using a WebSocket endpoint:
{
"flow": "explicit",
"url": "wss://your-api.example.com/tools/get-order-status",
"headers": {
"x-api-key": "your-internal-api-key"
}
}With a WebSocket URL, the platform connects to your server at session start and keeps the connection open for the entire session. Tool calls skip the connection overhead — and the open connection turns your tool into much more than a request handler. It lets your server:
- Know who it's talking to before the first call — receive the session ID and your own user/tenant context the moment the session starts.
- Follow the conversation live — stream the full user↔agent exchange in real time, so your tool answers with complete context.
- Drive the user's screen — push UI state to the client (confirmation cards, progress, page updates) in sync with the conversation.
- Deliver slow results after the timeout — return an interim answer now and inject the real outcome into the conversation whenever it's ready.