Vapi Function Calling Tutorial: A Complete Walkthrough with Working Examples
Function calling is what makes a Vapi voice agent useful. Without it, the AI can only answer questions using whatever is in its system prompt. With it, the AI can check calendars, look up customer records, book appointments, transfer calls, and do anything else your backend can do. This tutorial covers the full lifecycle of a function call in Vapi, from defining the tool to parsing the response, with working examples you can adapt.
What a Function Call Actually Is
A function call in Vapi is just a structured HTTP webhook that the LLM decides to send mid-conversation. When the caller says something like "do you have availability tomorrow at 2pm?", the model reads the conversation, matches the intent against the tools you defined, extracts the relevant parameters (date, time, service type), and POSTs them to your webhook. Your webhook returns a response, the model reads it, and speaks the answer back to the caller. The whole loop happens in under a second if everything is tuned right.
Step 1: Define the Tool in the Assistant
Open your Vapi assistant and go to Tools. Create a custom function. Each function needs four fields: name, description, parameters (JSON schema), and server URL. The name is how the model references the tool internally. The description is what the model reads to decide when to invoke it. The parameters schema is what the model uses to extract arguments from the conversation. The server URL is where the call gets POSTed.
Example: a tool named checkAvailability with description "Use this whenever the caller wants to check appointment availability for a specific date and service type." Parameters: date (string, format date), serviceType (string, enum of your services). Server URL points at your n8n webhook or custom backend.
Step 2: Write the Description Like a Prompt
The tool description is the single biggest factor in whether the model actually calls the tool. Vague descriptions like "checks availability" get ignored half the time. Specific descriptions with trigger examples get called reliably. A good description format: what it does, when to call it, when not to call it.
Example for checkAvailability: "Use this whenever the caller asks about appointment availability, open slots, scheduling, or booking. Do not call this for general hours-of-operation questions (use getHours instead). Call this before confirming any booking."
Step 3: Handle the Incoming Payload
Vapi POSTs a payload shaped like an object with a message property containing type (tool-calls), toolCalls (array), and metadata about the call itself including callId, phoneNumber, and assistantId. Each entry in the toolCalls array has an id, a type of function, and a function object with name and arguments. Arguments arrive as a JSON object (already parsed, not a string in the current API version).
In your webhook handler, extract the toolCallId first because you need it in the response. Then read the function arguments and execute whatever business logic the tool requires.
When Vapi Tool Calls Fail to Trigger
Step 4: Return the Correct Response Shape
Vapi expects a response with a results array. Each entry in the array has a toolCallId and a result field. The toolCallId must match the one that came in. The result is a string or object that Vapi feeds back to the LLM as the function's output. The model then uses that result to phrase its response to the caller.
The most common mistake is returning the raw data as the result. If you return an array of timestamps, the model reads them verbatim. If you return a clean human-friendly string like "Available slots are Tuesday at 2pm, Wednesday at 10am, or Thursday at 4:30pm", the model uses it smoothly. Format the result for human speech, not for machine parsing.
Step 5: Async Tools for Slow Operations
If your tool operation takes longer than the 7.5 second Vapi timeout, use async tools. Set async to true in the tool configuration. Async tools return immediately with an acknowledgement like "let me look that up", and you send the actual result later via the Vapi API to inject it into the conversation. This prevents timeout errors on slow backends.
Common use cases for async: external API calls to slow CRMs, database queries on large datasets, anything that averages over 3 seconds. Keep sync tools for fast operations under 2 seconds.
Step 6: Multi-Tool Calls in a Single Turn
The model can request multiple tools in a single turn. The toolCalls array will have multiple entries, each with its own id. Your response must include all of them in the results array. If you only respond to the first one, the model gets confused and the second tool call hangs until timeout. Loop through toolCalls, execute each, and return all results.
Common Errors and Fixes
Error: "tool call completed but model did not speak." Fix: the result field is missing or malformed. Check that results is an array and each entry has both toolCallId and result. Error: "tool fires but arguments are empty." Fix: parameter schema requires fields the model cannot extract from context. Make the parameters more forgiving or add examples to the description. Error: "model never calls the tool." Fix: the description is too generic, or there is a competing tool with an overlapping description. Make descriptions specific and non-overlapping.
Writing Tool Parameter Schemas That Work
Keep parameters minimal. Every parameter the model has to extract from conversation is a chance for it to extract the wrong value. If a tool can work with just a phone number, do not also require the caller's name, email, and address. Fewer required parameters means more reliable tool calls.
Use enums for any parameter with a fixed set of values. Service type, appointment category, urgency level, all should be enums. Free-text parameters should be reserved for fields the model genuinely needs to extract from conversation.
Impact of Tool Parameter Count on Call Success Rate
Best Practices for Production Tools
Log every tool call and response to a database or Airtable. When calls fail, you need the payload, response, and timing to diagnose. Add authentication via a secret header on every tool. Validate it in your webhook before executing business logic. Wrap every tool in try-catch and return a graceful error response instead of letting the webhook 500, because a 500 causes Vapi to speak an awkward error message to the caller.
Finally, write tests. Simulate tool calls with curl or Postman and verify the response shape before ever connecting Vapi. The feedback loop is dramatically faster than placing real phone calls to test.
Join 215+ AI Agency Owners
Get free access to our all-in-one outreach platform, AI content templates, and a community of builders landing clients in days.