0% found this document useful (0 votes)
61 views148 pages

Vapi and GoHighLevel Info

The document outlines the functionality and usage of Code Tools within the Vapi platform, which allow users to execute custom TypeScript code for tasks such as data transformation and API requests without the need for a server. It provides step-by-step instructions for creating and configuring a Code Tool, along with examples for customer lookup and order processing tools. Best practices for security, performance, error handling, and limitations are also discussed to guide users in effectively utilizing Code Tools.

Uploaded by

graeme
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
61 views148 pages

Vapi and GoHighLevel Info

The document outlines the functionality and usage of Code Tools within the Vapi platform, which allow users to execute custom TypeScript code for tasks such as data transformation and API requests without the need for a server. It provides step-by-step instructions for creating and configuring a Code Tool, along with examples for customer lookup and order processing tools. Best practices for security, performance, error handling, and limitations are also discussed to guide users in effectively utilizing Code Tools.

Uploaded by

graeme
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Search / Ask AI

Assistants Tools

Code Tool
Execute custom TypeScript code directly within your assistant without setting up a server.

The Code Tool allows you to write and execute custom TypeScript code that runs when your
assistant needs to perform a specific action. Unlike custom function tools that require you to host
a server, code tools run directly on Vapi’s infrastructure.

When to Use Code Tools

Code tools are ideal when you need to:

⦁ Transform or process data during a conversation

⦁ Make HTTP requests to external APIs

⦁ Perform calculations or business logic

⦁ Avoid the overhead of setting up and maintaining a webhook server

Creating a Code Tool

Step 1: Navigate to the Tools Section

1. Open your Vapi Dashboard

2. Click Tools in the left sidebar

3. Click Create Tool and select Code

Step 2: Configure Your Code Tool

The dashboard provides a visual interface to configure your code tool:


1. Tool Name: A descriptive identifier (e.g., get_customer_data )

2. Description: Explain what your tool does - this helps the AI understand when to use it

3. TypeScript Code: Write the code that will execute when the tool is called

4. Parameters: Define the input parameters your code expects

5. Environment Variables: Store sensitive values like API keys securely

Step 3: Write Your Code

Your code has access to two objects:

⦁ args : Contains the parameters passed by the assistant

⦁ env : Contains your environment variables

1 // Access parameters from the assistant


2 const { customerId, orderType } = args;
3
4 // Access secure environment variables
5 const { API_KEY, API_URL } = env;
6
7 // Make HTTP requests to external services
8 const response = await fetch(`${API_URL}/customers/${customerId}`, {
9 headers: {
10 'Authorization': `Bearer ${API_KEY}`,
11 'Content-Type': 'application/json'
12 }
13 });
14
15 const customer = await [Link]();
16
17 // Return data to the assistant
18 return {
19 name: [Link],
20 email: [Link],
21 memberSince: customer createdAt

Your code runs in an isolated environment with a configurable timeout (default: 10 seconds, max: 60
seconds).
Example: Customer Lookup Tool

Let’s create a tool that looks up customer information:

Configuration

Field Value

Tool Name get_customer

Description Retrieves customer information by their ID

Parameters

Name Type Required Description

customerId string Yes The unique customer identifier

Environment Variables

Name Value

API_KEY Your API key

API_BASE_URL [Link]

Code

1 const { customerId } = args;


2 const { API_KEY, API_BASE_URL } = env;
3
4 const response = await fetch(`${API_BASE_URL}/customers/${customerId}`, {
5 headers: {
6 'Authorization': `Bearer ${API_KEY}`
7 }
8 });
9
10 if (![Link]) {
11 return { error: 'Customer not found' };
12 }
13
14 const customer = await [Link]();
15
16 return {
17 name: [Link],
18 email: [Link],
19 plan: [Link],
20 status: [Link]

Example: Order Processing Tool

A more complex example that processes an order:

Parameters

Name Type Required Description

items array Yes Array of item objects with id and quantity

customerId string Yes The customer placing the order

shippingAddress string No Delivery address

Code

1 const { items, customerId, shippingAddress } = args;


2 const { ORDER_API_KEY, ORDER_API_URL } = env;
3
4 // Calculate total
5 let total = 0;
6 const itemDetails = [];
7
8 for (const item of items) {
9 const priceResponse = await fetch(`${ORDER_API_URL}/products/${[Link]}`);
10 const product = await [Link]();
11
12 const itemTotal = [Link] * [Link];
13 total += itemTotal;
14
15 [Link]({
16 name: [Link],
17 quantity: [Link],
18 price: [Link],
19 subtotal: itemTotal
20 });

Using Code Tools in Assistants

Once created, add your code tool to any assistant:

In the Dashboard

1. Go to Assistants → Select your assistant

2. Navigate to the Tools tab

3. Click Add Tool and select your code tool

4. Save your assistant configuration

Via API

$ curl --location --request PATCH '[Link] \


> --header 'Authorization: Bearer <YOUR_API_KEY>' \
> --header 'Content-Type: application/json' \
> --data '{
$ "model": {
$ "toolIds": ["your-code-tool-id"]
$ }
$ }'

Creating Code Tools via API

You can also create code tools programmatically:

$ curl --location '[Link] \


> --header 'Content-Type: application/json' \
> --header 'Authorization: Bearer <YOUR_API_KEY>' \
> --data '{
$ "type": "code",
$ "name": "get_customer",
$ "description": "Retrieves customer information by their ID",
$ "code": "const { customerId } = args;\nconst { API_KEY } = env;\n\nconst response
$ "parameters": {
$ "type": "object",
$ "properties": {
$ "customerId": {
$ "type": "string",
$ "description": "The unique customer identifier"
$ }
$ },
$ "required": ["customerId"]
$ },
$ "environmentVariables": [
$ {
$ " " "API KEY"

Best Practices

Security

⦁ Store sensitive values (API keys, secrets) in Environment Variables, not in your code

⦁ Environment variable values support Liquid templates to reference call variables

Performance

⦁ Keep code execution under the timeout limit

⦁ Use efficient API calls and avoid unnecessary loops

⦁ Consider caching strategies for repeated lookups

Error Handling

⦁ Always handle potential errors from API calls

⦁ Return meaningful error messages that help the assistant respond appropriately

1 const { customerId } = args;


2
3 try {
4 const response = await fetch(`${env.API_URL}/customers/${customerId}`);
5
6 if (![Link]) {
7 return {
8 error: true,
9 message: `Customer ${customerId} not found`
10 };
11 }
12
13 return await [Link]();
14 } catch (error) {
15 return {
16 error: true,
17 message: 'Unable to reach customer service'
18 };
19 }

Return Values
⦁ Return structured data that the assistant can easily interpret

⦁ Include relevant information the assistant needs to continue the conversation

Limitations

⦁ Timeout: Maximum execution time is 60 seconds (default: 10 seconds)

⦁ No file system access: Code runs in an isolated environment without file access

⦁ Memory: Code runs with limited memory allocation

⦁ Network: Only outbound HTTP/HTTPS requests are supported

Code Tool vs Custom Function Tool

Feature Code Tool Custom Function Tool

Server Required No Yes

Language TypeScript Any

Setup Complexity Low Higher

Customization Moderate Full control

Secrets Management Environment Variables Your server

Best For Quick integrations, API calls Complex logic, existing infrastructure
Choose Code Tools when you want to quickly add functionality without managing infrastructure.
Choose Custom Function Tools when you need full control over the execution environment or
have existing server infrastructure.

Was this page helpful? Yes No Edit this page

Client-side Tools (Web SDK)


Previous Next
Handle tool-calls in the browser without a server URL

Built with
Marketplace Modules Workflow Actions and Triggers Marketplace Workflow Actions

Creating a Marketplace
Workflow Action
Marketplace Workflow Actions are the customizable workflow actions managed in Marketplace. You
will be able to create custom actions to push or pull data from your application/API in a workflow
using customized fields and API endpoint.

Create a New Action #​


Navigate to the "Workflow" section, located under the Modules in the left-hand navigation
menu of your app..
Click on "Create Action" to initiate the process.

Define Action Information


Name: Provide a descriptive name for your action.
Key: Assign a unique identifier (e.g., mycustomaction ). This key is immutable and used to
reference the action within workflows.
Icon: Select an icon to represent the action visually in the workflow builder.
Short Description: Write a brief explanation of what the action does.
Summary: Provide detailed information about the action's functionality and use cases.
Action Configuration
Manage Fields
Construct form to collect the data required for sending to API
Create New Field
Name
Enter Field Name
Type
Select one of the following field types:
String, Numerical, Textarea, Select, Multiple Select, Radio, Toggle, Checkbox, Attachment, Rich
Text Editor, Hidden, Dynamic
Required
Enable if this is a required field in workflow.
Reference
Enter unique reference key. The value of this field will be bind to the provided key. Example:
action_a_name
Default Value
Enter or map a value. The value provided will be used as default value for this field when loaded
in workflow.
Alters Dynamic Field
If enabled, any changes made to this field value will trigger/ re-trigger loading the dynamic fields
to the workflow action configuration UI.
Validation Rules Validation Rules let you protect data quality by checking the value a user types
into a form field, table cell, or configuration input before it is saved or passed downstream.
If the value fails the check, HighLevel blocks the save/submit action and shows a custom error
message that you configure.

Typical use-cases

Scenario Example

Lead-capture form Require a properly-formatted US phone number

Web-hook payload Ensure a “status” field matches one of allowed strings

Custom action param Block users from entering Handlebar syntax in plain text

Field Types: Select / Multi Select / Radio


Option Type is applicable only for Select, Multi Select and Radio field types.

Select one of the following option types:

Constants
Load options by adding custom Label-Value constants
Internal Reference
Load options from HighLevel Internal Modules

Supported HighLevel Modules


External API
Load option from external API endpoint
URL (GET)
Provide a URL to support GET method and send a valid response as per the sample response
structure shared below.

Headers
Add headers as per your requirement

Sample Response Data

{
"options": [
{ "label": "Afghanistan", "value": "AF" },
{ "label": "Åland Islands", "value": "AX" },
{ "label": "Albania", "value": "AL" },
{ "label": "Algeria", "value": "DZ" },
{ "label": "American Samoa", "value": "AS" }
]
}

Field Type: Hidden


It will be hidden in the action configuration and the mapped data will be sent in the payload. Used
to collect essential information such as company_id, customerid, etc., from system data or from your
custom triggers.
Field Type: Dynamic
Dynamic fields are used to build custom fields from an API call. The API call should return the below
response structure to construct the fields in the Workflow action configuration form UI. Only one
Dynamic type can be created per action.
URL (POST)
Enter your API endpoint URL. When executed data is sent to this API endpoint via POST method.

Headers
Add headers as per your requirement

Sample Payload:

{
"data": {
"name": "John Doe",
"age": "29",
"gender": "male",
"hobbies": ["sports", "music"],
"address": "My Address",
"country": "US",
"profileType": "public",
"dataShare": true,
"tems": true
},
"extras": {
"locationId": "xyz",
"contactId": "abc",
"workflowId": "def"
},
"meta": {
"key": "custom_action_key",
"version": "1.0"
}
}

Sample Response Structure:


Sections are used to group the fields in UI

{
"inputs": [
{
"section": "Personal Info",
"fields": [
{ "field": "name", "title": "Name", "fieldType": "string", "required":
true },
{ "field": "age", "title": "Age", "fieldType": "numerical", "required":
true },
{ "field": "gender", "title": "Gender", "fieldType": "select",
"required": true,
"options": [
{ "label": "Male", "value": "male" },
{ "label": "Female", "value": "female" }
]
}
]
},
{
"section": "Location Info",
"fields": [
{ "field": "village", "title": "Village", "fieldType": "string",
"required": true },
{ "field": "city", "title": "City", "fieldType": "string", "required":
true },
{ "field": "fullAddress", "title": "Your Full Address", "fieldType":
"textarea", "required": true }
]
}
]
}
Sample structure for each Field Types
String

{ "field": "name", "title": "Name", "fieldType": "string", "required": true }

Numeric

{ "field": "name", "title": "Name", "fieldType": "numeric", "required": true }

Textarea

{ "field": "description", "title": "Description", "fieldType": "textarea",


"required": true }

Select

{
"field": "gender",
"title": "Gender",
"fieldType": "select",
"required": true,
"options": [
{ "label": "Male", "value": "male" },
{ "label": "Female", "value": "female" }
]
}

Multiple Select

{
"field": "hobbies",
"title": "Hobbies",
"fieldType": "multiselect",
"required": true,
"options": [
{ "label": "Sport", "value": "sport" },
{ "label": "Music", "value": "music" }
]
}

Radio

{
"field": "profileType",
"title": "Profile Type",
"fieldType": "radio",
"required": true,
"options": [
{ "label": "Public", "value": "public" },
{ "label": "Private", "value": "private" }
]
}

Toggle

{ "field": "dataShare", "title": "Allow my data to be stored", "fieldType":


"toggle", "required": true }

Checkbox

{ "field": "terms", "title": "Terms & conditions", "fieldType": "checkbox",


"required": true }

Validation Rules (Types)


The Validation Rules feature helps app developers ensure data integrity by enforcing input checks
on form fields. Developers can choose from three flexible validation methods:
Pre-defined Rules
Easily apply common validations such as email, phone number, URL, numerical values, and
handlebar syntax checks.
Regex Support
Use custom regular expressions to validate inputs against specific patterns.

Arrow Function
Write custom arrow functions that receive the input value and return true or false based on
whether the validation passes or fails.
For every validation rule, a custom error message must be provided to display meaningful feedback
when validation fails.

Multi-branch
The Multi-Branch Feature enables the creation of branches that can dynamically adjust based on
various predefined conditions. By allowing multiple branches within a workflow, each contact can be
directed down the appropriate path based on their interactions or status.

Branch Section: Defines the name or identifier for the specific branch section.
Branch Section Description: Provides a brief description or details about the branch section.
Branch Name Label: Specifies the label that will be displayed for the branch name.
Branch Name Helptext: Offers additional information related to the branch name.
Delete Branch Title: Sets the title or label used when deleting a branch.
Delete Branch Description: Describes when a branch is deleted.

Options:

Allow New Branches: Enables users to add new branches within the action.
Is Predefined Branches Editable: Allows users to edit predefined branches within the action.
Show Branches Section: Displays the branch section details to the user.
Disabled Allow new branch

Sample payload for branches


{
"data": {
"name": "John Doe",
"age": "29",
"gender": "male",
"hobbies": [ "sports", "music" ],
"address": "My Address",
"country": "US",
"profileType": "public",
"dataShare": true,
"tems": true,
"branches": [
{
"id": "a8d14b13-d7cc-4241-bd2c-53180f0ec278",
"name": "Branch name",
"fields": {
"branchFieldKey": "branchFieldValue"
}
}
]
},
"extras": {
"locationId": "xyz",
"contactId": "abc",
"workflowId": "def"
},
"meta": {
"key": "custom_action_key",
"version": "1.0"
}
}

Action Execution
Allows you to choose between an API or a custom code.

API
URL (POST)
Enter your API endpoint URL. When this action is executed data is sent to this API endpoint via POST
method.

Headers
Add required header data that has to be included while sending data to the API endpoint

Sample Payload:

{
"data": {
"name": "John Doe",
"age": "29",
"gender": "male",
"hobbies": ["sports", "music"],
"address": "My Address",
"country": "US",
"profileType": "public",
"dataShare": true,
"tems": true
},
"extras": {
"locationId": "xyz",
"contactId": "abc",
"workflowId": "def"
},
"meta": {
"key": "custom_action_key",
"version": "1.0"
}
}

Custom code
Custom Code allows users to create custom logic they want to achieve. This provides flexibility and
control beyond the pre-built APIs, enabling users to automate complex tasks and integrate with
various services not supported by API.

Code Editor
You can write the code in the Code Editor
You can input HTTP requests like Get, Put, Post, Delete etc via the button.
You can also use custom values using the picker.
Output should be a JavaScript Object or Array of Objects.

Test and format your Code

Testing the code is a mandatory step, if the test is not done then user will not be able to use the
output of the code in the subsequent steps.
To test the code click on the "Test Code" button.
Post clicking on Run test button, if there are no errors in the code them it will show "Test Result
Success" and if there is an error in code then the result will be "Test Result Failed" and you
would have to recheck the code to remove the error.
You can also format the code using "Format code" button.

Pause Execution
This toggle is used the contact will be held at this action unless resume webhook is requested.
If this toggle is true then provided extras object needs to be passed as body payload for resume
workflow endpoint.

Show API details button shows a sample response to be passed onto to the webhook for Success
Execution and Failed Execution.

Sync: When the pause execution is turned off along with branching support, the contact will be
moved to provided branch using branchId property from API response or from Custom Code
using return statement. The branchId here will be the branch through with the contact will
move forward.
Async: When the pause execution is turned off, the branch ID needs to be sent to the webhook
for resuming which is present in "show API details" button. More info present in Pause
functionality.

Response Data
Add sample response data to configure custom variables.

Enter a valid sample response JSON structure that will be sent as a response to the Send Data API
endpoint.

Arrays are supported in response data. This data can be utilized in custom variables based on
references and is available for use in Array Functions, Custom Code, and Custom Webhooks.

Manage Custom Variables


Add Custom variables using sample response data, for users to use in workflows.

Add Custom Variable


Name
Enter label name
Reference
Select a reference key from the sample response saved to Response Data.

Submit for Review

The action version will be in draft state by default. After updating the action information and
configuration the action version should be submitted for review.

Click on Submit for review and add required changelog information for the submitted version.
Once approved the version submitted for review will be published live to all Sub-accounts.

Create New Version


Click on + New Version to create a new version for the action.
On clicking + New Version It will create a new draft version with all the previously published data
prefilled.

Delete Action
Once an Action is deleted, it will be deleted permanently and cannot be restored. The deleted action
will be removed from Marketplace App and Workflow Action list. If a deleted action is part of any
workflow the action execution will be skipped.
Enter action name to confirm delete
For more detailed information, refer to the official HighLevel guide on Marketplace Workflow
Actions.
Marketplace Modules Workflow Actions and Triggers Marketplace Workflow Triggers

Creating a Marketplace
Workflow Trigger
Marketplace Workflow Triggers are the customizable workflow triggers managed in Marketplace.
You will be able to create custom triggers to push data from your application/API to a workflow.

Video Walkthrough on How to create Marketplace Workflow Trigger

Create a New Trigger #​


Navigate to the Workflow section, located under the Modules in the left-hand navigation menu
of your app.
Click on "Create Trigger" to initiate the process.
Define Trigger Information
Name: Provide a descriptive name for your trigger.
Key: Assign a unique identifier (e.g., mycustomtrigger ). This key is immutable and used to
reference the trigger within workflows.
Icon: Select an icon to represent the trigger visually in the workflow builder.
Short Description: Write a brief explanation of what the trigger does.
Summary: Provide detailed information about the trigger's functionality and use cases.

Configure Trigger Data


Input a sample JSON payload that represents the data structure the trigger will handle. This
sample is used to configure filters and custom variables.
Manage Filters
Filters allow users to define conditions under which the trigger activates.
Create New Filter:

Name: Enter a name for the filter.


Type: Choose from the following field types:
String (Simple text matching)
Select / Multi-Select
Dynamic
Required: Specify if the filter is mandatory.
Reference: Map the filter to a key in the sample trigger data.
Alters Dynamic Filter: If enabled, any changes made to this filter value will trigger/re-trigger
loading the dynamic filters in the workflow trigger configuration UI.
Type: Select / Multi Select
Option Type is applicable only for Select and Multi Select field types.

Select one of the following option types:

Constants Load options by adding custom Label-Value constants


Internal Reference Load options from HighLevel Internal Modules. Select one of the HighLevel
Modules to load options list.

Supported HighLevel Modules


External API Load option from external API endpoint
URL (GET) Provide a URL to support GET method and send a valid response as per the sample
response structure shared below.

Headers Add headers as per your requirement

Sample Response Data

{
"options": [
{ "label": "Afghanistan", "value": "AF" },
{ "label": "Åland Islands", "value": "AX" },
{ "label": "Albania", "value": "AL" },
{ "label": "Algeria", "value": "DZ" },
{ "label": "American Samoa", "value": "AS" }
]
}

Type: Dynamic

Dynamic filters are used to build custom filters from an API call. The API call should return the below
response structure to construct the filters in the Workflow trigger configuration form UI. Only one
Dynamic type can be created per trigger.
URL (POST) Enter your API endpoint URL. When executed data is sent to this API endpoint via POST
method in the below mentioned payload format and a valid response is expected as per the sample
response structure shared below.

Headers Add headers as per your requirement

Sample Payload: The form data is sent as payload to the dynamic field API

{
"data": {
"name": "John Doe",
"age": "29",
"gender": "male",
"hobbies": ["sports", "music"],
"address": "My Address",
"country": "US",
"profileType": "public"
},
"extras": {
"locationId": "xyz",
"contactId": "abc",
"workflowId": "def"
},
"meta": {
"key": "custom_trigger_key",
"version": "1.0"
}
}

Sample Response Structure:

{
"filters": [
{ "field": "name", "title": "Name", "fieldType": "string", "required": true
},
{ "field": "gender", "title": "Gender", "fieldType": "select", "required":
true,
"options": [
{ "label": "Male", "value": "male" },
{ "label": "Female", "value": "female" }
]
}
]
}

Sample structure for each Filter Type

String

{ "field": "name", "title": "Name", "fieldType": "string", "required": true }

Select

{
"field": "gender",
"title": "Gender",
"fieldType": "select",
"required": true,
"options": [
{ "label": "Male", "value": "male" },
{ "label": "Female", "value": "female" }
]
}

Multiple Select

{
"field": "hobbies",
"title": "Hobbies",
"fieldType": "multiselect",
"required": true,
"options": [
{ "label": "Sport", "value": "sport" },
{ "label": "Music", "value": "music" }
]
}

Manage Custom Variables


Custom variables allow users to map data from the trigger payload to variables used within the
workflow.
Add Custom Variable:

Name: Enter a label for the variable.


Reference: Select a key from the sample trigger data to bind to this variable.

Set Up Subscription URL


The Subscription URL is an API endpoint that receives trigger configuration details whenever
the trigger is created, updated, or deleted in a workflow.

URL (POST): Enter your API endpoint URL.


Headers: Add any required headers for the API call.
Payload Format: The payload sent to this endpoint will include trigger data, metadata, and
additional information such as location ID, workflow ID, and company ID.

Trigger "CREATED" in workflow

{
"triggerData": {
"id": "def",
"key": "trigger_a",
"filters": [],
"eventType": "CREATED",
"targetUrl": "[Link]
marketplace/triggers/execute/abc/def"
},
"meta": { "key": "trigger_a", "version": "2.4" },
"extras": { "locationId": "ghj", "workflowId": "qwe", "companyId": "asd" }
}

Trigger "UPDATED" in workflow

{
"triggerData": {
"id": "def",
"key": "trigger_a",
"filters": [
{
"field": "country",
"id": "country",
"operator": "==",
"title": "Country",
"type": "select",
"value": "USA"
}
],
"eventType": "UPDATED",
"targetUrl": "[Link]
marketplace/triggers/execute/abc/def"
},
"meta": { "key": "trigger_a", "version": "2.4" },
"extras": { "locationId": "ghj", "workflowId": "qwe", "companyId": "asd" }
}

Trigger "DELETED" in workflow

{
"triggerData": {
"id": "def",
"key": "trigger_a",
"filters": [
{
"field": "country",
"id": "country",
"operator": "==",
"title": "Country",
"type": "select",
"value": "USA"
}
],
"eventType": "DELETED",
"targetUrl": "[Link]
marketplace/triggers/execute/abc/def"
},
"meta": { "key": "trigger_a", "version": "2.4" },
"extras": { "locationId": "ghj", "workflowId": "qwe", "companyId": "asd" }
}

Submit for Review


Once the trigger is configured:

Click on "Submit for Review."


Provide changelog information for the submitted version.
Upon approval, the trigger becomes available to all sub-accounts.

Version Management
Create New Version: Click on "+ New Version" to create a new draft version of the trigger. This
version will prefill all previously published data.
Submit for Review: Each new version must be submitted for review and approved before it
becomes live.

Delete Trigger
To delete a trigger, enter the trigger name to confirm deletion.

Once deleted, the trigger is permanently removed and cannot be restored.


Any workflows using the deleted trigger will skip its execution.

Can Workflows Execute Without Contact?


Workflow can run contactless without any Contact data dependency so you can send any
payload data via Marketplace Triggers and use it in workflow.
You can proceed without contact and use actions that are not dependent on contact
information. Custom Webhook, Google Sheet, Slack, ChatGPT and all Internal Tools can be
executed without contact.
If necessary, you can use the Create/Update or Find Contact actions to retrieve the contact data
to the workflow.

Examples:
Send order data to trigger and add the order information to google sheet, use if/else to
categorize based on order value and send a slack notification.
Retrieve the contact with Contact ID using Find contact action

For more detailed information, refer to the official HighLevel guide on Marketplace Workflow
Triggers.
Search / Ask AI

Assistants Tools

Custom Tools
Learn how to create and configure Custom Tools for use by your Vapi assistants.

This guide shows you how to create custom tools for your Vapi assistants. We recommend using
the Vapi dashboard’s dedicated Tools section, which provides a visual interface for creating and
managing tools that can be reused across multiple assistants. For advanced users, API
configuration is also available.

Creating Tools in the Dashboard (Recommended)

Step 1: Navigate to the Tools Section

1. Open your Vapi Dashboard

2. Click Tools in the left sidebar

3. Click Create Tool to start building your custom tool

Step 2: Configure Your Tool


The dashboard provides a user-friendly interface to configure your tool:

1. Tool Type: Select “Function” for custom API integrations

2. Tool Name: Give your tool a descriptive name (e.g., “Weather Lookup”)

3. Description: Explain what your tool does

4. Tool Configuration:

⦁ Tool Name: The identifier for your function (e.g., get_weather )

⦁ Parameters: Define the input parameters your function expects

⦁ Server URL: The endpoint where your function is hosted


Step 3: Configure Messages

Set up the messages your assistant will speak during tool execution. For example, if you want
custom messages you can add something like this:

⦁ Request Start: “Checking the weather forecast. Please wait…”

⦁ Request Complete: “The weather information has been retrieved.”

⦁ Request Failed: “I couldn’t get the weather information right now.”

⦁ Request Delayed: “There’s a slight delay with the weather service.”

Step 4: Advanced Settings

Configure additional options:

⦁ Async Mode: Enable if the tool should run asynchronously

⦁ Timeout Settings: Set how long to wait for responses

⦁ Error Handling: Define fallback behaviors

Example: Creating a Weather Tool

Let’s walk through creating a weather lookup tool:

Dashboard Configuration
1. Tool Name: “Weather Lookup”

2. Description: “Retrieves current weather information for any location”

3. Function Name: get_weather

4. Parameters:

⦁ location (string, required): “The city or location to get weather for”

5. Server URL: [Link]

This example uses OpenWeatherMap’s free API. You’ll need to sign up at [Link] to
get a free API key and add it as a query parameter: ?appid=YOUR_API_KEY&q={location}
Messages Configuration

⦁ Request Start: “Let me check the current weather for you…”

⦁ Request Complete: “Here’s the weather information you requested.”

⦁ Request Failed: “I’m having trouble accessing weather data right now.”

Using Tools in Assistants

Once created, your tools can be easily added to any assistant:

In the Dashboard

1. Go to Assistants → Select your assistant

2. Navigate to the Tools tab

3. Click Add Tool and select your custom tool from the dropdown

4. Save your assistant configuration

In Workflows
Tools created in the Tools section are automatically available in the workflow builder:

1. Add a Tool Node to your workflow

2. Select your custom tool from the Tool dropdown

3. Configure any node-specific settings

Using the Vapi CLI

Manage your custom tools directly from the terminal:

$ # List all tools


$ vapi tool list
$
$ # Get tool details
$ vapi tool get <tool-id>
$
$ # Create a new tool (interactive)
$ vapi tool create
$
$ # Test a tool with sample data
$ vapi tool test <tool-id>
$
$ # Delete a tool
$ vapi tool delete <tool-id>

Use the Vapi CLI to forward tool calls to your local server:

$ # Terminal 1: Create tunnel (e.g., with ngrok)


$ ngrok http 4242
$
$ # Terminal 2: Forward events
$ vapi listen --forward-to localhost:3000/tools/webhook

vapi listen is a local forwarder that requires a separate tunneling service. Configure your tool’s
server URL to use the tunnel’s public URL for testing. Learn more →

Alternative: API Configuration

For advanced users who prefer programmatic control, you can also create and manage tools via
the Vapi API:

Creating Tools via API

$ curl --location '[Link] \


> --header 'Content-Type: application/json' \
> --header 'Authorization: Bearer <YOUR_API_KEY>' \
> --data '{
$ "type": "function",
$ "function": {
$ "name": "get_weather",
$ "description": "Retrieves current weather information for any location",
$ "parameters": {
$ "type": "object",
$ "properties": {
$ "location": {
$ "type": "string",
$ "description": "The city or location to get weather for"
$ }
$ },
$ "required": ["location"]
$ }
$ },
$ "server": {

Adding Tools to Assistants via API

$ curl --location --request PATCH '[Link] \


> --header 'Authorization: Bearer <YOUR_API_KEY>' \
> --header 'Content-Type: application/json' \
> --data '{
$ "model": {
$ "provider": "openai",
$ "model": "gpt-4o",
$ "toolIds": ["your-tool-id-here"]
$ }
$ }'

Request Format: Understanding the Tool Call Request

When your server receives a tool call request from Vapi, it will be in the following format:

1 {
2 "message": {
3 "timestamp": 1678901234567,
4 "type": "tool-calls",
5 "toolCallList": [
6 {
7 "id": "toolu_01DTPAzUm5Gk3zxrpJ969oMF",
8 "name": "get_weather",
9 "arguments": {
10 "location": "San Francisco"
11 }
12 }
13 ],
14 "toolWithToolCallList": [
15 {
16 "type": "function",
17 "name": "get_weather",
18 "parameters": {
19 "type": "object",
20 "properties": {

For the complete API reference, see ServerMessageToolCalls Type Definition .

Server Response Format: Providing Results and Context

When your Vapi assistant calls a tool (via the server URL you configured), your server will receive
an HTTP request containing information about the tool call. Upon processing the request and
executing the desired function, your server needs to send back a response in the following JSON
format:

1 {
2 "results": [
3 {
4 "toolCallId": "X",
5 "result": "Y"
6 }
7 ]
8 }

Breaking down the components:

⦁ toolCallId (X): This is a unique identifier included in the initial request from Vapi. It allows the
assistant to match the response with the corresponding tool call, ensuring accurate processing
and context preservation.

⦁ result (Y): This field holds the actual output or result of your tool’s execution. The format and
content of “result” will vary depending on the specific function of your tool. It could be a
string, a number, an object, an array, or any other data structure that is relevant to the tool’s
purpose.

Example:

Let’s revisit the weather tool example from before. If the tool successfully retrieves the weather for
a given location, the server response might look like this:
1 {
2 "results": [
3 {
4 "toolCallId": "call_VaJOd8ZeZgWCEHDYomyCPfwN",
5 "result": "San Francisco's weather today is 62°C, partly cloudy."
6 }
7 ]
8 }

Some Key Points:

⦁ Pay attention to the required parameters and response format of your functions.

⦁ Ensure your server is accessible and can handle the incoming requests from Vapi.

⦁ Make sure to add “Tools Calls” in both the Server and Client messages and remove the
function calling from it.

By following these guidelines and adapting the sample payload, you can easily configure a variety
of tools to expand your Vapi assistant’s capabilities and provide a richer, more interactive user
experience.

Video Tutorial:

Tools Tutorial - Step by Step - Vapi - Functions, DTMF, End Call, Transfers, API
Was this page helpful? Yes No Edit this page

Code Tool
Previous Next
Execute custom TypeScript code directly within your assistant without setti…

Built with
Search / Ask AI

Assistants Tools

Custom tools troubleshooting


Overview

Troubleshoot and fix common issues with custom tool integrations in your Vapi assistants.

In this guide, you’ll learn to:

⦁ Diagnose why tools aren’t triggering

⦁ Fix response format errors

⦁ Resolve parameter and token issues

⦁ Handle multiple tool scenarios

Quick diagnosis

Start with the most common issue for your symptoms:

Tool won't trigger No result returned


Symptoms: Assistant doesn’t call your Symptoms: Logs show “no result
tool Check prompting and schema setup returned” Fix response format issues

Response ignored Parameters cut off


Symptoms: Tool returns data but Symptoms: Tool parameters or
assistant ignores it Resolve parsing and responses truncated Increase token
format problems limits
Tool won’t trigger

Your assistant doesn’t call the tool even when it should.

Check your assistant prompting

Use the exact tool name in your assistant instructions. If your tool is named get_weather , reference
get_weather in prompts, not weather_tool .

Verify required parameters

Check that your tool schema includes all required parameters:

Tool schema

1 {
2 "name": "get_weather",
3 "parameters": {
4 "type": "object",
5 "properties": {
6 "city": {
7 "type": "string",
8 "description": "City name for weather lookup"
9 }
10 },
11 "required": ["city"] // Must be array of required parameter names
12 }
13 }

Enable schema validation

Add strict: true to catch validation errors early:

Tool configuration

1 {
2 "name": "get_weather",
3 "description": "Get current weather for a city",
4 "parameters": {
5 // ... your parameters
6 },
7 "strict": true,
8 "maxTokens": 500
9 }

Check your call logs for “Schema validation errors” to identify parameter issues.

No result returned error

Logs show “ok, no result returned” or similar messages.

Use the correct response format

Your webhook must return this exact JSON structure:

✅ Success response ✅ Error response

1 {
2 "results": [
3 {
4 "toolCallId": "call_123",
5 "result": "Your response as single-line string"
6 }
7 ]
8 }

Common format mistakes

Wrong HTTP status code

Line breaks in response

Missing results array

Tool call ID mismatch

Wrong result data type


Response ignored

Tool returns data but the assistant doesn’t use it in conversation.

Fix line breaks and formatting

❌ Line breaks cause parsing errors ✅ Single-line string works

1 {
2 "results": [
3 {
4 "toolCallId": "call_123",
5 "result": "Temperature: 72°F\nCondition: Sunny\nHumidity: 45%"
6 }
7 ]
8 }

Verify HTTP status and JSON structure

1 Check HTTP status

Ensure your webhook returns HTTP 200. Any other status code causes the response to be
ignored.

3 Validate JSON format

Use a JSON validator to ensure your response structure is valid.

4 Match tool call IDs

For multiple tools, return results in the same order as calls were triggered, with matching
toolCallId values.

Token truncation

Tool parameters or responses are getting cut off.


Increase token limits

The default token limit is only 100. Increase it for complex tools:

Tool configuration

1 {
2 "name": "complex_tool",
3 "description": "Tool that needs more tokens",
4 "parameters": {
5 // ... your parameters
6 },
7 "maxTokens": 500 // Increase from default 100
8 }

Look for “Token truncation warnings” in your call logs to identify when this occurs.

Multiple tools scenarios

Some tools in parallel calls fail or return wrong results.

Handle multiple tool responses

Return all results in the same order as the calls were triggered:

Multiple tool response

1 {
2 "results": [
3 {
4 "toolCallId": "call_1",
5 "result": "First tool success"
6 },
7 {
8 "toolCallId": "call_2",
9 "error": "Second tool failed"
10 },
11 {
12 "toolCallId": "call_3",
13 "result": "Third tool success"
14 }
15 ]
16 }

Use HTTP 200 for the entire response, even if some individual tools error. Handle errors within the
results array using the error field.

Async vs sync behavior

Tool behavior doesn’t match your expectations.

Sync tools (recommended) Async tools

Configuration: "async": false (default)

Behavior:

⦁ Wait for webhook response before resolving

⦁ Tool call resolution depends on your response

⦁ Use for immediate operations

1 {
2 "name": "sync_tool",
3 "async": false, // or omit (default)
4 // ... other config
5 }

Most tools should use sync behavior unless you specifically need async processing for long-running
operations.

Reference: Required formats

Response format template


Success response Error response

1 {
2 "results": [
3 {
4 "toolCallId": "call_123",
5 "result": "Single-line string response"
6 }
7 ]
8 }

Tool schema template

Complete tool configuration

1 {
2 "name": "tool_name",
3 "description": "Clear description of what the tool does",
4 "parameters": {
5 "type": "object",
6 "properties": {
7 "param1": {
8 "type": "string",
9 "description": "Parameter description"
10 }
11 },
12 "required": ["param1"]
13 },
14 "strict": true,
15 "maxTokens": 500,
16 "async": false
17 }

Critical response rules

Always return HTTP 200 - Even for errors

Use single-line strings - No \n line breaks


Match tool call IDs exactly - From request to response

Include results array - Required structure

String types only - For result/error values

Debugging with call logs

Look for these key error messages in your call logs:

Error Message What It Means How to Fix

”ok, no result returned” Wrong response format Use correct JSON structure

”Tool call ID mismatches” toolCallId doesn’t match Ensure exact ID match

”HTTP errors” Webhook not returning 200 Return HTTP 200 always

”Schema validation errors” Missing required parameters Check required array

”Token truncation warnings” Need more tokens Increase maxTokens

”Response parsing errors” Malformed JSON/line breaks Fix JSON format

Was this page helpful? Yes No Edit this page

Google Calendar Integration


Previous Next
Connect your assistant to Google Calendar for seamless appointment sche…

Built with
Search / Ask AI

Assistants Tools

Default Tools
Adding Transfer Call, End Call, Dial Keypad, and API Request capabilities to your assistants.

Vapi voice assistants are given additional functions: transferCall , endCall , sms , dtmf (to dial a
keypad with DTMF ), and apiRequest . These functions can be used to transfer calls, hang up
calls, send SMS messages, enter digits on the keypad, and integrate business logic with your
existing APIs.

To add Default Tools to your agent, you need to add them in the tools array of your assistant. You
can do this in your api request, or by creating a new tool in the dashboard tools page, and assigning
it to your assistant.

Transfer Call
This function is provided when transferCall is included in the assistant’s list of available tools
(see configuration options here). This function can be used to transfer the call to any of the
destinations defined in the tool configuration (see details on destination options here).

1 {
2 "model": {
3 "provider": "openai",
4 "model": "gpt-4o",
5 "messages": [
6 {
7 "role": "system",
8 "content": "You are an assistant at a law firm. When the user asks to be tran
9 }
10 ],
11 "tools": [
12 {
13 "type": "transferCall",
14 "destinations" : {
15 {
16 "type": "number",
17 "number": "+16054440129"
18 }
19 }
20 }
2 ]

End Call
This function is provided when endCall is included in the assistant’s list of available tools (see
configuration options here). The assistant can use this function to end the call.

1 {
2 "model": {
3 "provider": "openai",
4 "model": "gpt-4o",
5 "messages": [
6 {
7 "role": "system",
8 "content": "You are an assistant at a law firm. If the user is being mean, us
9 }
10 ],
11 "tools": [
12 {
13 "type": "endCall"
14 }
15 ]
16 }
17 }

Send Text

This function is provided when sms is included in the assistant’s list of available tool (see
configuration options here). The assistant can use this function to send SMS messages using a
configured Twilio account.

1 {
2 "model": {
3 "provider": "openai",
4 "model": "gpt-4o",
5 "messages": [
6 {
7 "role": "system",
8 "content": "You are an assistant. When the user asks you to send a text messa
9 }
10 ],
11 "tools": [
12 {
13 "type": "sms",
14 "metadata": {
15 "from": "+15551234567"
16 }
17 }
18 ]
19 }
20 }

Dial Keypad (DTMF)

This function is provided when dtmf is included in the assistant’s list of available tools (see
configuration options here). The assistant will be able to enter digits on the keypad. Useful for IVR
navigation or data entry.

1 {
2 "model": {
3 "provider": "openai",
4 "model": "gpt-4o",
5 "messages": [
6 {
7 "role": "system",
8 "content": "You are an assistant at a law firm. When you hit a menu, use the
9 }
10 ],
11 "tools": [
12 {
13 "type": "dtmf"
14 }
15 ]
16 }
17 }

There are three methods for sending DTMF in a phone call:

1. In-band: tones are transmitted as part of the regular audio stream. This is the simplest
method, but it can suffer from quality issues if the audio stream is compressed or degraded.
2. Out-of-band via RFC 2833: tones are transmitted separately from the audio stream, within
RTP (Real-Time Protocol) packets. It’s typically more reliable than in-band DTMF, particularly
for VoIP applications where the audio stream might be compressed. RFC 2833 is the standard
that initially defined this method. It is now replaced by RFC 4733 but this method is still
referred by RFC 2833.

3. Out-of-band via SIP INFO messages: tones are sent as separate SIP INFO messages. While
this can be more reliable than in-band DTMF, it’s not as widely supported as the RFC 2833
method.

Vapi’s DTMF tool integrates with telephony provider APIs to send DTMF tones using the out-of-band
RFC 2833 method. This approach is widely supported and more reliable for transmitting the signals,
especially in VoIP environments. Note, the tool’s effectiveness depends on the IVR system’s
configuration and their capturing method. See our IVR navigation guide for best practices.

API Request

This tool allows your assistant to make HTTP requests to any external API endpoint during
conversations. This tool fills the gap between Vapi and your existing business logic, bringing your
own endpoints into the conversation flow. See configuration options here.

Dynamic Variables with LiquidJS

Use LiquidJS syntax to reference conversation variables and user data in your URLs, headers, and
request bodies. This allows your API requests to adapt dynamically based on the conversation
context.

Basic Examples

GET Request Example

1 {
2 "model": {
3 "provider": "openai",
4 "model": "gpt-4o",
5 "messages": [
6 {
7 "role": "system",
8 "content": "You help users check their order status. When they provide an ord
9 }
10 ],
11 "tools": [
12 {
13 "type": "apiRequest",
14 "function": {
15 "name": "api_request_tool"
16 },
17 "name": "checkOrderStatus",
18 "url": "[Link]
19 "method": "GET",
20 "body": {

POST Request Example

1 {
2 "model": {
3 "provider": "openai",
4 "model": "gpt-4o",
5 "messages": [
6 {
7 "role": "system",
8 "content": "You help users book appointments. When they want to schedule, use
9 }
10 ],
11 "tools": [
12 {
13 "type": "apiRequest",
14 "function": {
15 "name": "api_request_tool"
16 },
17 "name": "bookAppointment",
18 "url": "[Link]
19 "method": "POST",
20 "headers": {
21 "type": "object"

Advanced Configuration

With Retry Logic

1 {
2 "type": "apiRequest",
3 "function": {
4 "name": "api_request_tool"
5 },
6 "name": "checkOrderStatus",
7 "url": "[Link]
8 "method": "GET",
9 "body": {
10 "type": "object",
11 "properties": {
12 "orderNumber": {
13 "description": "The user's order number",
14 "type": "string"
15 }
16 },
17 "required": [
18 "orderNumber"
19 ]
20 },

Custom Functions: Deprecated

Was this page helpful? Yes No Edit this page

Voicemail Tool
Previous Next
Learn how to use the assistant-controlled voicemail tool for flexible voice…

Built with
OAuth 2.0 OAuth 2.0 Get Access Token

Get Access Token


POST [Link]

Use Access Tokens to access GoHighLevel resources on behalf of an authenticated


location/company.

Request
APPLICATION/X-WWW-FORM-URLENCODED

BODY REQUIRED

client_id string REQUIRED

The ID provided by GHL for your integration

client_secret string REQUIRED

grant_type string REQUIRED

Possible values: [ authorization_code , refresh_token , client_credentials ]

code string

refresh_token string

user_type string
The type of token to be requested
Possible values: [ Company , Location ]
Example: Location

redirect_uri string
The redirect URI for your application
Example: [Link]

Responses 200 400 401 422


Successful response

APPLICATION/JSON

Schema Example (auto)

SCHEMA

access_token string
Example: ab12dc0ae1234a7898f9ff06d4f69gh

token_type string
Example: Bearer

expires_in number
Example: 86399

refresh_token string
Example: xy34dc0ae1234a4858f9ff06d4f66ba

scope string
Example: conversations/[Link] conversations/[Link]

userType string
Example: Location

locationId string
Location ID - Present only for Sub-Account Access Token
Example: l1C08ntBrFjLS0elLIYU

companyId string
Company ID
Example: l1C08ntBrFjLS0elLIYU

approvedLocations string[]
Approved locations to generate location access token
Example: ["l1C08ntBrFjLS0elLIYU"]

userId string REQUIRED

USER ID - Represent user id of person who performed installation


Example: l1C08ntBrFjLS0elLIYU
planId string
Plan Id of the subscribed plan in paid apps.
Example: l1C08ntBrFjLS0elLIYU

isBulkInstallation boolean
Example: Bearer

Share your feedback

★★★★★

CURL NODEJS PYTHON PHP JAVA GO RUBY POWERSH

 

CURL

1 curl -L -X POST '[Link] \


2 -H 'Content-Type: application/x-www-form-urlencoded' \
3 -H 'Accept: application/json'

REQUEST COLLAPSE ALL

Base URL

[Link]

Body REQUIRED

client_id REQUIRED

The ID provided by GHL for your integration

client_secret REQUIRED

client_secret

grant_type REQUIRED
---

code

code

refresh_token

refresh_token

user_type

---

redirect_uri

The redirect URI for your application

SEND API REQUEST

RESPONSE CLEAR

Click the Send API Request button above and see the response here!
OAuth 2.0 OAuth 2.0 Get Location Access Token from Agency Token

Get Location Access Token from Agency Token


POST [Link]

This API allows you to generate locationAccessToken from AgencyAccessToken

Requirements
Scope(s)

[Link]

Auth Method(s)

OAuth Access Token

Token Type(s)

Agency Token

Request
HEADER PARAMETERS

Version string REQUIRED

Possible values: [ 2021-07-28 ]


API Version

APPLICATION/X-WWW-FORM-URLENCODED

BODY REQUIRED

companyId string REQUIRED

Company Id of location you want to request token for

locationId string REQUIRED

The location ID for which you want to obtain accessToken


Responses 200 400 401 422

Successful response

APPLICATION/JSON

Schema Example (auto)

SCHEMA

access_token string
Location access token which can be used to authenticate & authorize API under following scope
Example: ab12dc0ae1234a7898f9ff06d4f69gh

token_type string
Example: Bearer

expires_in number
Time in seconds remaining for token to expire
Example: 86399

scope string
Scopes the following accessToken have access to
Example: conversations/[Link] conversations/[Link]

locationId string
Location ID - Present only for Sub-Account Access Token
Example: l1C08ntBrFjLS0elLIYU

planId string
Plan Id of the subscribed plan in paid apps.
Example: l1C08ntBrFjLS0elLIYU

userId string REQUIRED

USER ID - Represent user id of person who performed installation


Example: l1C08ntBrFjLS0elLIYU

Share your feedback


★★★★★

AUTHORIZATION: AUTHORIZATION

CURL NODEJS PYTHON PHP JAVA GO RUBY POWERSH

 

CURL

1 curl -L -X POST '[Link] \


2 -H 'Content-Type: application/x-www-form-urlencoded' \
3 -H 'Accept: application/json' \
4 -H 'Authorization: Bearer <TOKEN>'

REQUEST COLLAPSE ALL

Base URL

[Link]

Auth

Bearer Token

Bearer Token

Parameters

Version — header REQUIRED

---

Body REQUIRED

companyId REQUIRED

Company Id of location you want to request token for

locationId REQUIRED
The location ID for which you want to obtain accessToken

SEND API REQUEST

RESPONSE CLEAR

Click the Send API Request button above and see the response here!
OAuth 2.0 OAuth 2.0 Get Location where app is installed

Get Location where app is installed


GET [Link]

This API allows you fetch location where app is installed upon

Requirements
Scope(s)

[Link]

Auth Method(s)

OAuth Access Token Private Integration Token

Token Type(s)

Agency Token

Request
HEADER PARAMETERS

Version string REQUIRED

Possible values: [ 2021-07-28 ]


API Version

QUERY PARAMETERS

skip string
Parameter to skip the number installed locations
Default value: 0
Example: 1

limit string
Parameter to limit the number installed locations
Default value: 20
Example: 10

query string
Parameter to search for the installed location by name
Example: location name

isInstalled boolean
Filters out location which are installed for specified app under the specified company
Example:

companyId string REQUIRED

Parameter to search by the companyId


Example: tDtDnQdgm2LXpyiqYvZ6

appId string REQUIRED

Parameter to search by the appId


Example: tDtDnQdgm2LXpyiqYvZ6

versionId string
VersionId of the app
Example: tDtDnQdgm2LXpyiqYvZ6

onTrial boolean
Filters out locations which are installed for specified app in trial mode
Example:

planId string
Filters out location which are installed for specified app under the specified planId
Example:

Responses 200 400 401 422

Successful response

APPLICATION/JSON

Schema Example (auto)

SCHEMA

locations object[]
count number
Total location count under the company
Example: 1231

installToFutureLocations boolean
Boolean to control if user wants app to be automatically installed to future locations
Example: true

Share your feedback

★★★★★

AUTHORIZATION: AUTHORIZATION

CURL NODEJS PYTHON PHP JAVA GO RUBY POWERSH

 

CURL

1 curl -L '[Link] \
2 -H 'Accept: application/json' \
3 -H 'Authorization: Bearer <TOKEN>'

REQUEST COLLAPSE ALL

Base URL

[Link]

Auth

Bearer Token

Bearer Token
Parameters

companyId — query REQUIRED

Parameter to search by the companyId

appId — query REQUIRED

Parameter to search by the appId

Version — header REQUIRED

---

Show optional parameters

SEND API REQUEST

RESPONSE CLEAR

Click the Send API Request button above and see the response here!
Search / Ask AI

Get started

Guides
Explore real-world, cloneable examples to build voice agents with Assistants and Squads

Appointment Scheduling Medical Triage & Scheduling


BUILT WITH ASSISTANTS BUILT WITH SQUADS

Build an appointment scheduling Build a medical triage and scheduling


assistant that can schedule assistant that can triage patients and
appointments for a barbershop schedule appointments for a clinic

Ecommerce Order Management Property Management


BUILT WITH SQUADS BUILT WITH SQUADS

Build an ecommerce order management Build a call routing workflow that


assistant that can track orders and dynamically routes tenant calls based on
process returns verification and inquiry type
Lead Qualification Multilingual Support (Structured)
BUILT WITH ASSISTANTS BUILT WITH SQUADS

Create an outbound sales agent that can Build a structured multilingual support
schedule appointments automatically workflow with language selection and
dedicated conversation paths

Dynamic Multilingual Agent Support Escalation


BUILT WITH ASSISTANTS BUILT WITH ASSISTANTS

Build a dynamic agent with automatic Build an intelligent support escalation


language detection and real-time system with dynamic routing based on
language switching customer tier and issue complexity

Docs Agent Inbound Support


BUILT WITH ASSISTANTS BUILT WITH ASSISTANTS

Build a docs agent that can answer Build a technical support assistant that
questions about your documentation remembers where you left off between
calls

Voice Widget Vapi CLI


BUILT WITH ASSISTANTS DEVELOPER TOOL

Easily integrate the Vapi Voice Widget Build voice AI agents faster with the Vapi
into your website for enhanced user CLI - project integration, local testing,
interaction and IDE enhancement
Was this page helpful? Yes No Edit this page

Previous Vapi CLI Next

Built with
Authorization OAuth 2.0 Access Token Generation: Agency vs. Sub-Account Scenarios

Handling Access Tokens for Apps with Target User: Agency

Handling Access Tokens for


Apps with Target User: Agency
This guide explains how the installation flow works for the Agency targeted APPs , how to obtain the
access token.

Overview
For apps whose Target User is set as Agency, the app will only be visible to the Agency
Admin/Owner, and only they can install it.

Installation Flow
1. Install the app on your Agency account.
2. After installation, the redirect URL will be triggered from our end, and the authorization code
will be shared.
3. Use this authorization code to exchange for an Access Token using the Get Access Token API
endpoint.

Note: The Access Token generated will be of user type company(Agency Level Token).

Sample Request

curl -X POST [Link]


-H 'Accept: application/json'
-H 'Content-Type: application/x-www-form-urlencoded'
-d 'client_id=68a2fd84fab6670f45220ebf-megyp358'
-d 'client_secret=673011da-b03a-4768-bbff-0f45821cd6fe'
-d 'grant_type=authorization_code'
-d 'code=16d0b6ceb51350ba437870074ad25bc65e8c1d8d'
-d 'user_type=Company'

Sample Response

{
"access_token":
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoQ2xhc3MiOiJDb21wYW55IiwiYXV0aENsYQ",
"token_type": "Bearer",
"expires_in": 86399,
"refresh_token":
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoQ2xhc3MiOiJDb21wYW55IiwiYXV0aEN",
"scope": "[Link]",
"refreshTokenId": "68a2feef89153fe9b8d196bc",
"userType": "Company",
"companyId": "GNb7aIv4rQFVb9iwNl5K",
"isBulkInstallation": false,
"userId": "Rg6BRRiHh7dS9gJy3W8a"
}

 
Authorization OAuth 2.0 Access Token Generation: Agency vs. Sub-Account Scenarios

Handling Access Tokens for Apps with Target User: Sub-Account

Handling Access Tokens for


Apps with Target User: Sub-
Account
This document explains how to manage Access Tokens when your app’s Target User is set to Sub-
Account.

Installation Options
When you set the target user to Sub-Account during app creation, you can configure who can install
the app. The type of token generated depends on the option chosen and who installs the app.

Who can install the APP: Agency Only


The app will be visible only to Agency Admins/Owners.
Only Agency Admin/Owner can install the app.

Installation Flow

1. Install the app on your account.


2. After installation, the redirect URL will be triggered and an authorization code will be shared.
3. Use this code to exchange for an Access Token via the Get Access Token API.

⚠️ Note: The Access Token generated here will be of type Company (Agency-level).

Sample Request
curl --request POST
--url [Link]
--header 'Accept: application/json'
--header 'Content-Type: application/x-www-form-urlencoded'
--data-urlencode client_id=68a32958b5154ca8bbdc4d40-meh5chaj
--data-urlencode client_secret=a5949eb7-4d46-4bfd-95c1-e338d4952e6b
--data-urlencode grant_type=authorization_code
--data-urlencode code=059ff0439402599b0ecb45388a9d4b9fc2d17123
--data-urlencode user_type=Company

Sample Response

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 86399,
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"scope": "[Link]",
"refreshTokenId": "68a32a7fb5154c26d5dd218c",
"userType": "Company",
"companyId": "GNb7aIv4rQFVb9iwNl5K",
"isBulkInstallation": true,
"userId": "Rg6BRRiHh7dS9gJy3W8a"
}

4. To access Sub-Account–specific API endpoints, the Agency-level Access Token must first be
exchanged for a Sub-Account (Location-level) Access Token. This exchange can be performed
using the Get Location Access Token from Agency Token API.

Note: You can configure a webhook URL for your app, and the App Install event will
automatically be subscribed by default.

When the APP is installed this event will be triggered, this webhook provides details such as the
locationId where the app has been installed. You can use the locationId along with your
Agency-level Access Token to exchange it for a Sub-Account (Location-level) Access Token.

Sample App Install Event Payload


{
"type": "INSTALL",
"appId": "665c6bb13d4e5364bdec0e2f",
"versionId": "665c6bb13d4e5364bdec0e2f",
"installType": "Location",
"locationId": "HjiMUOsCCHCjtxzEf8PR",
"companyId": "GNb7aIv4rQFVb9iwNl5K",
"userId": "Rg6BRRiHh7dS9gJy3W8a",
"companyName": "Marketplace and Integrations Prod Agency",
"isWhitelabelCompany": true,
"whitelabelDetails": {
"logoUrl": "[Link]
"domain": "[Link]"
},
"timestamp": "2025-06-25T06:57:06.225Z",
"webhookId": "1a533f85-1f1e-4886-891e-ee0cf4666e90"
}

Sample Request for Get Location Access Token from Agency Token

curl -L '[Link]
-H 'Content-Type: application/x-www-form-urlencoded'
-H 'Accept: application/json'
-H 'Version: 2021-07-28'
-H 'Authorization: Bearer {AGENCY_ACCESS_TOKEN}'
-d 'companyId=GNb7aIv4rQFVb9iwNl5K'
-d 'locationId=HjiMUOsCCHCjtxzEf8PR'

Sample Response

{
"access_token":
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2OGEzMmRhNjlkN2EzY2E5NT",
"token_type": "Bearer",
"expires_in": 86400,
"refresh_token":
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoQ2xhc3MiOiJMb2NhdGlvbiIsImF",
"scope": "[Link] [Link] [Link]",
"userType": "Location",
"companyId": "GNb7aIv4rQFVb9iwNl5K",
"locationId": "HjiMUOsCCHCjtxzEf8PR",
"userId": "Rg6BRRiHh7dS9gJy3W8a",
"traceId": "8cf33664-9f4f-4392-adf6-71b8bed2592a"
}

Who can install the APP: Everyone


This type of app can be installed by both Agency users and Sub-Account users. The type of Access
Token generated will depend on who performs the installation.

Scenario 1: Agency User installs the app


In this case, the Access Token generated will be of type Company (Agency-level). To access Sub-
Account resources, you must exchange this token for a Location-level token using the Get Location
Access Token from Agency Token API endpoint.

Installation Flow

1. Install the app on your account.


2. After installation, the redirect URL will be triggered and an authorization code will be shared.
3. Use this code to exchange for an Access Token via the Get Access Token API.

⚠️ Note: The Access Token generated here will be of type Company (Agency-level).

Sample Request

curl --request POST


--url [Link]
--header 'Accept: application/json'
--header 'Content-Type: application/x-www-form-urlencoded'
--data-urlencode client_id=68a32958b5154ca8bbdc4d40-meh5chaj
--data-urlencode client_secret=a5949eb7-4d46-4bfd-95c1-e338d4952e6b
--data-urlencode grant_type=authorization_code
--data-urlencode code=059ff0439402599b0ecb45388a9d4b9fc2d17123
--data-urlencode user_type=Company

Sample Response
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 86399,
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"scope": "[Link]",
"refreshTokenId": "68a32a7fb5154c26d5dd218c",
"userType": "Company",
"companyId": "GNb7aIv4rQFVb9iwNl5K",
"isBulkInstallation": true,
"userId": "Rg6BRRiHh7dS9gJy3W8a"
}

4. To access Sub-Account–specific API endpoints, the Agency-level Access Token must first be
exchanged for a Sub-Account (Location-level) Access Token. This exchange can be performed
using the Get Location Access Token from Agency Token API.

Note: You can configure a webhook URL for your app, and the App Install event will
automatically be subscribed by default.

When the APP is installed this event will be triggered, this webhook provides details such as the
locationId where the app has been installed. You can use the locationId along with your
Agency-level Access Token to exchange it for a Sub-Account (Location-level) Access Token.

Sample App Install Event Payload

{
"type": "INSTALL",
"appId": "665c6bb13d4e5364bdec0e2f",
"versionId": "665c6bb13d4e5364bdec0e2f",
"installType": "Location",
"locationId": "HjiMUOsCCHCjtxzEf8PR",
"companyId": "GNb7aIv4rQFVb9iwNl5K",
"userId": "Rg6BRRiHh7dS9gJy3W8a",
"companyName": "Marketplace and Integrations Prod Agency",
"isWhitelabelCompany": true,
"whitelabelDetails": {
"logoUrl": "[Link]
"domain": "[Link]"
},
"timestamp": "2025-06-25T06:57:06.225Z",
"webhookId": "1a533f85-1f1e-4886-891e-ee0cf4666e90"
}

Sample Request for Get Location Access Token from Agency Token

curl -L '[Link]
-H 'Content-Type: application/x-www-form-urlencoded'
-H 'Accept: application/json'
-H 'Version: 2021-07-28'
-H 'Authorization: Bearer {AGENCY_ACCESS_TOKEN}'
-d 'companyId=GNb7aIv4rQFVb9iwNl5K'
-d 'locationId=HjiMUOsCCHCjtxzEf8PR'

Sample Response

{
"access_token":
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI2OGEzMmRhNjlkN2EzY2E5NT",
"token_type": "Bearer",
"expires_in": 86400,
"refresh_token":
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoQ2xhc3MiOiJMb2NhdGlvbiIsImF",
"scope": "[Link] [Link] [Link]",
"userType": "Location",
"companyId": "GNb7aIv4rQFVb9iwNl5K",
"locationId": "HjiMUOsCCHCjtxzEf8PR",
"userId": "Rg6BRRiHh7dS9gJy3W8a",
"traceId": "8cf33664-9f4f-4392-adf6-71b8bed2592a"
}

Scenario 2: Sub-Account User installs the app


In this case, the Access Token generated will be of type Location (Sub-Account level).

Installation Flow

1. Install the app on your account.


2. After installation, the redirect URL will be triggered and an authorization code will be shared.
3. Use this code to exchange for an Access Token via the Get Access Token API.
Sample Request

curl --request POST \


--url [Link] \
--header 'Accept: application/json' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode client_id=68a42f3c2a64bb65c985c618-mei99elp \
--data-urlencode client_secret=86f9901c-b57d-4395-a406-ff178cd8a57d \
--data-urlencode grant_type=authorization_code \
--data-urlencode code=4a1a74401abd1b46d923543c4a366eb3f21b5cbf \
--data-urlencode user_type=Location

{
"access_token":
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoQ2xhc3MiOiJMb2NhdGlvbiIsImF1dGhDbGFzc
"token_type": "Bearer",
"expires_in": 86399,
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoQ2xhc3MiOiJMb2N",
"scope": "[Link]",
"refreshTokenId": "68a4332c2a64bbc7a1888971",
"userType": "Location",
"companyId": "GNb7aIv4rQFVb9iwNl5K",
"locationId": "HjiMUOsCCHCjtxzEf8PR",
"isBulkInstallation": false,
"userId": "57n5nmVqHA1ghBM8UKhU"
}

 

Summary
Agency-only installation: If “Who can install the app” is set to Agency only, the generated
token will be of type Company (Agency-level). To run APIs and perform actions at the Sub-
Account level, this token must be exchanged for a Location-level token.
Everyone installation: If “Who can install the app” is set to Everyone, there are two possible
scenarios:
Agency User Installs the APP → The generated token will be of type Company (Agency-
level). To run APIs and perform actions at the Sub-Account level, this token must be
exchanged for a Location-level token.
Sub-Account User Installs the APP → The generated token will be of type Location . This
token can be used directly to call APIs and perform tasks without further exchange.
Search / Ask AI

Get started

Introduction
Build voice AI agents that can make and receive phone calls

What is Vapi?

Vapi is the developer platform for building voice AI agents. We handle the complex infrastructure
so you can focus on creating great voice experiences.

Voice agents allow you to:

⦁ Have natural conversations with users

⦁ Make and receive phone calls

⦁ Integrate with your existing systems and APIs

⦁ Handle complex workflows like appointment scheduling, customer support, and more

How voice agents work

Every Vapi assistant combines three core technologies:

Speech-to-Text Large Language Model


Converts user speech into text that your Processes the conversation and
agent can understand generates intelligent responses
Text-to-Speech
Converts your agent’s responses back
into natural speech

You have full control over each component, with dozens of providers and models to choose from;
OpenAI, Anthropic, Google, Gladia, Deepgram, ElevenLabs, and many, many more.

Two ways to build voice agents

Vapi offers two main primitives, designed for different use cases:

Assistants Squads
Best for: Most use cases and fast Best for: Multi-assistant setups with
iteration specialization

Assistants use a single system prompt Squads orchestrate multiple assistants


plus tools and structured outputs. with context-preserving transfers. Ideal
Perfect for: for:
⦁ Customer support ⦁ Medical triage and scheduling
⦁ Lead qualification ⦁ E‑commerce orders, returns, VIP
⦁ Booking and routing ⦁ Property management routing

Key capabilities

⦁ Real-time conversations: Sub-600ms response times with natural turn-taking

⦁ Phone integration: Make and receive calls on any phone number

⦁ Web integration: Embed voice calls directly in your applications

⦁ Tool integration: Connect to your APIs, databases, and existing systems

⦁ Multi-assistant orchestration (Squads): Compose specialized assistants with seamless


transfers
Choose your path

Phone Calls Web Integration


⦁ Create a voice agent for ⦁ Add voice capabilities to your web
inbound/outbound calls application
⦁ Build customer support or sales ⦁ Integrate voice chat into your
automation existing product
⦁ Get started with no coding required ⦁ Build with code and SDKs

Build your first voice agent in 5 minutes Embed live voice conversations directly in
using our dashboard. your app.

Developer tools

Vapi CLI

The Vapi CLI brings the full power of the platform to your terminal:

CLI Overview
Install in seconds with:

$ curl -sSL [Link] | bash

Everything from the dashboard, now in your terminal.

Popular use cases

Customer Support Sales & Lead Qualification


BUILT WITH ASSISTANTS BUILT WITH ASSISTANTS

Automate inbound support calls with Make outbound sales calls, qualify leads,
agents that can access your knowledge and schedule appointments with
base and escalate to humans when sophisticated branching logic.
needed.

Appointment Scheduling Medical Triage & Scheduling


BUILT WITH ASSISTANTS BUILT WITH SQUADS

Handle booking requests, check Emergency routing and appointment


availability, and confirm appointments scheduling for healthcare.
with conditional routing.

E-commerce Order Management See more examples


BUILT WITH SQUADS See our collection of examples covering

Order tracking, returns, and customer a wide range of use cases.

support workflows.

Was this page helpful? Yes No Edit this page

Phone calls
Next
Learn to make your first phone call with a voice agent

Built with
Search / Ask AI

Get started

Phone calls
Learn to make your first phone call with a voice agent

Overview

Vapi makes it easy to build voice agents that can make and receive phone calls. In under 5
minutes, you’ll create a voice assistant and start talking to it over the phone.

In this quickstart, you’ll learn to:

⦁ Create an assistant using the Dashboard or programmatically

⦁ Set up a phone number

⦁ Make your first inbound and outbound calls

Prerequisites

⦁ A Vapi account

⦁ For SDK usage: API key from the Dashboard

Using the Vapi CLI? You can create assistants, manage phone numbers, and make calls directly from
your terminal:

$ # Install the CLI


$ curl -sSL [Link] | bash
$
$ # Login and create an assistant
$ vapi login
$ vapi assistant create
Learn more about the Vapi CLI →

Create your first voice assistant


Dashboard TypeScript (Server SDK) Python (Server SDK) cURL

1 Open the Vapi Dashboard

Go to [Link] and log in to your account.

2 Create a new assistant

In the dashboard, create a new assistant using the customer support specialist template.

Creating a new assistant

3 Configure your assistant

Set the first message and system prompt for your assistant:

First message:
Hi there, this is Alex from TechSolutions customer support. How can I help you toda

System prompt:

You are Alex, a customer service voice assistant for TechSolutions. Your primary pu
- Sound friendly, patient, and knowledgeable without being condescending
- Use a conversational tone with natural speech patterns
- Speak with confidence but remain humble when you don't know something
- Demonstrate genuine concern for customer issues

Set up a phone number


Dashboard TypeScript (Server SDK) Python (Server SDK) cURL

1 Create a phone number

In the Phone Numbers tab, create a free US phone number or import an existing number
from another provider.

Create a phone number


Free Vapi phone numbers are only available for US national use. For international calls, you’ll
need to import a number from Twilio or another provider.

2 Attach your assistant to the number

Select your assistant in the inbound settings for your phone number. When this number is
called, your assistant will automatically answer.

Make your first calls

1 Test inbound calling

Call the phone number you just created. Your assistant will pick up and start the
conversation with your configured first message.

2 Place an outbound call


Dashboard TypeScript (Server SDK) Python (Server SDK) cURL

In the dashboard, go to the outbound calls section:

1. Enter your own phone number as the target

2. Select your assistant

3. Click “Make Call”


Making an outbound call

Your assistant will call the specified number immediately.

3 Test web calling (optional)

You can also test your assistant directly in the dashboard by clicking the call button—no
phone number required.
Next steps

Now that you have a working voice assistant:

⦁ Customize the conversation: Update the system prompt to match your use case

⦁ Add tools: Connect your assistant to external APIs and databases

⦁ Configure models: Try different speech and language models for better performance

⦁ Scale with APIs: Use Vapi’s REST API to create assistants programmatically

Ready to integrate voice into your application? Check out the Web integration guide to embed voice
calls directly in your app.

Was this page helpful? Yes No Edit this page

Web calls
Previous Next
Build voice interfaces and backend integrations using Vapi's Web and Serv…

Built with
Authorization Private Integrations Token

Private Integrations
Private Integrations allow you to build powerful custom integrations between your HighLevel
account and any other third-party app.

If you are looking to integrate your HighLevel account with a third-party app, you have two options:

1. Find and install the relevant app from the App Marketplace
2. Build your own private integration by yourself or with the help of a developer using APIs.

Private Integrations help you achieve #2 securely.

Video Walkthrough

Key Advantages of Private Integrations


Simple: Generate Private Integration tokens from your account settings and manage them with
ease.
Secure: You get to restrict the scopes/permissions that a developer can access on your account.

Private Integrations are available for both Agencies and Sub-Accounts.

What's the difference between Private


Integrations and API Keys?
Private Integrations API Keys

More Secure: You get to restrict the


Less Secure: A developer gets unrestricted
scopes/permissions that a developer can access
access to all your account data
on your account
Private Integrations API Keys

Out-dated: API Keys work on API v1.0


State-of-art: Private Integrations allows you to
which has reached end-of-life and is no
access API v2.0 which is state of the art
longer maintained

More Features: API v2.0 has more powerful APIs Less Features: API v1.0 has limited APIs

What's the difference between Private


Integrations and OAuth2 Access Tokens?
Private Integrations, to put it simply, are static/fixed OAuth2 Access Tokens.

Private Integrations Access Tokens

Programmatic Generation: API Tokens are


Generated from the UI: Private Integration
generated by exchanging OAuth access code
token can be generated easily from the UI
for the tokens using Get Access Token API

Static/Fixed: Private Integration Tokens are


Refreshed Daily: Access Tokens expire daily and
static/fixed and do not automatically refresh
need to be refreshed
unless you rotate them from the UI

How do I use Private Integrations?


Private Integration tokens are used in the Authorization header, just like other Access Tokens.

Example:

curl --request GET \


--url [Link] \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <YOUR PRIVATE INTEGRATION TOKEN>' \
--header 'Version: 2021-07-28'

Testing a Private Integration with API Calls


Once your Private Integration is created, you may want to test it by pushing data to an API endpoint.
Here’s an example of how to test the integration by adding a new contact:

curl --request POST \


--url [Link] \
--header 'Authorization: Bearer <YOUR PRIVATE INTEGRATION TOKEN>' \
--header 'Content-Type: application/json' \
--header 'Version: 2021-07-28' \
--data '{ "firstName": "John", "lastName": "Doe", "email":
"[Link]@[Link]", "phone": "+1234567890", "locationId": "LOCATION_ID" }'

Make sure to:

Replace LOCATION_ID with the actual sub-account ID.

Replace Authorization value with your generated Private Integration token.

For a full list of available endpoints and testing capabilities, visit our official developer
documentation.

How do I manage Private Integrations?


Who can create Private Integrations?
By default, all agency admins can create and manage Private Integrations. You can restrict this
permission at a user level.

Navigate to:
Settings > Team > Edit the specific agency admin > Roles & Permissions, and enable/disable
Private Integrations for the agency admin.

You may apply restrictions at two levels:

Allow the agency admin to view and manage the agency's private integrations
Allow the agency admin to view and manage the sub-accounts' private integrations

Where can I find Private Integrations?


You can find Private Integrations under agency settings.
If you don't find it under settings, please make sure that you have enabled the feature on Labs.

How do I create a new Private Integration?


Step 1: Click on "Create new Integration"
Step 2: Give your Private Integration a name and description to help you and your team identify
what it's for.
Step 3: Select the scopes/permissions that you want the private integration to have access to on
your agency account. Ensure that you are selecting only the required scopes for better data security.
Step 4: Copy the token generated and share it with your third-party app developer.

Note: Please ensure that you are sharing the token with trusted parties only. Do not share it
publicly.
Don't forget to copy the token generated as you won't be able to do it again later.

Best Practices to Maintain Security of My


Private Integration Token
We recommend that you rotate your Private Integration tokens every 90 days.

How to rotate your token:


Step 1: Navigate to Private Integrations under settings, and click on the Private Integration you have
created.
Step 2: Click on "Rotate and expire this token later".
Step 3: Click "Continue" in response to the warning message if you are sure that you want to
proceed with rotation.
Step 4: Copy the new token and update it on your third-party app.

You will have a 7-day window where both the old and the new tokens will continue to work. After 7
days, the old token will expire.

During this window, you can:

"Cancel rotation" if your developer needs more time.


"Expire Now" if the third party app has been updated.

What if my token has been compromised?


Step 1: Navigate to Private Integrations under settings, and click on the Private Integration you have
created.
Step 2: Click on "Rotate and expire this token now".
Step 3: Click "Continue" in response to the warning message if you are sure that you want to
proceed with rotation.
Step 4: Copy the new token and update it on your third-party app.

Note: Don't forget to copy the token generated as you won't be able to do it again later.

Can I edit the Private Integration permissions


without updating the token?
Yes, you can edit the Private Integration name, description and scopes/permissions any time after
you've created it.

How:
1. Navigate to Private Integrations under settings, and select "Edit" from the three-dot menu.
2. Update the Private Integration name and description if required. Click on "Next".
3. If required, update the scopes/permissions that you want the private integration to have access
to on your account. Ensure that you are selecting only the required scopes for better data
security. Click on "Update" to save the updates made.

Note: Updating the Private Integration details does not generate a new token. The existing
token will continue to work.

How do I delete the Private Integration once I


no longer need it?
You can delete the Private Integration once you are no longer using the third-party app.

To do so, navigate to Private Integrations under settings, and select "Delete" from the three-dot
menu.

Share your feedback

★★★★★
Search / Ask AI

Get started

Vapi CLI
Overview

The Vapi CLI is the official command-line interface that brings world-class developer experience to
your terminal and IDE. Build, test, and deploy voice AI applications without leaving your
development environment.

In this guide, you’ll learn to:

⦁ Install and authenticate with the Vapi CLI

⦁ Initialize Vapi in existing projects

⦁ Manage assistants, phone numbers, and workflows from your terminal

⦁ Forward webhooks to your local development server

⦁ Turn your IDE into a Vapi expert with MCP integration

Installation

Install the Vapi CLI in seconds with our automated scripts:

macOS/Linux Windows Docker

$ curl -sSL [Link] | bash

Quick start
1 Authenticate

Connect your Vapi account:

$ vapi login

This opens your browser for secure OAuth authentication.

2 Initialize your project

Add Vapi to an existing project:

$ vapi init

The CLI auto-detects your tech stack and sets up everything you need.

3 Create your first assistant

Build a voice assistant:

$ vapi assistant create

Follow the interactive prompts to configure your assistant.

Key features

🚀 Project integration
Drop Vapi into any existing codebase with intelligent auto-detection:

$ vapi init
$ # Detected: [Link] application
$ # ✓ Installed @vapi-ai/web SDK
$ # ✓ Generated components/[Link]
$ # ✓ Created pages/api/vapi/[Link]
$ # ✓ Added environment template
Supports React, Vue, [Link], Python, Go, Flutter, React Native, and dozens more frameworks.

🤖 MCP integration
Turn your IDE into a Vapi expert with Model Context Protocol:

$ vapi mcp setup

Your IDE’s AI assistant (Cursor, Windsurf, VSCode) gains complete, accurate knowledge of Vapi’s
APIs and best practices. No more hallucinated code or outdated examples.

🔗 Local webhook testing


Forward webhooks to your local server for debugging:

$ # Terminal 1: Create tunnel (e.g., with ngrok)


$ ngrok http 4242
$
$ # Terminal 2: Forward webhooks
$ vapi listen --forward-to localhost:3000/webhook

Important: vapi listen is a local forwarder only - it does NOT provide a public URL. You need a
separate tunneling service (like ngrok) to expose the CLI’s port to the internet. Update your webhook
URLs in Vapi to use the tunnel’s public URL.

🔐 Multi-account management
Switch between organizations and environments seamlessly:

$ # List all authenticated accounts


$ vapi auth status
$
$ # Switch between accounts
$ vapi auth switch production
$
$ # Add another account
$ vapi auth login
📱 Complete feature parity
Everything you can do in the dashboard, now in your terminal:

⦁ Assistants: Create, update, list, and delete voice assistants

⦁ Phone numbers: Purchase, configure, and manage phone numbers

⦁ Calls: Make outbound calls and view call history

⦁ Workflows: Manage conversation flows (visual editing in dashboard)

⦁ Campaigns: Create and manage AI phone campaigns at scale

⦁ Tools: Configure custom functions and integrations

⦁ Webhooks: Set up and test event delivery

⦁ Logs: View system logs, call logs, and debug issues

Common commands

Assistant management

Phone number management

Call operations

Debugging and logs

Configuration

The CLI stores configuration in ~/.[Link] . You can also use environment variables:

$ # Set API key via environment


$ export VAPI_API_KEY=your-api-key
$
$ # View current configuration
$ vapi config get
$
$ # Update configuration
$ vapi config set <key> <value>
$
$ # Manage analytics preferences
$ vapi config analytics disable

Auto-updates

The CLI automatically checks for updates and notifies you when new versions are available:

$ # Check for updates manually


$ vapi update check
$
$ # Update to latest version
$ vapi update

Next steps

Now that you have the Vapi CLI installed:

⦁ Initialize a project: Add Vapi to your existing codebase

⦁ Set up MCP: Enhance your IDE with Vapi intelligence

⦁ Test webhooks locally: Debug webhooks with tunneling services

⦁ Manage authentication: Work with multiple accounts

Resources:

⦁ GitHub Repository

⦁ Report Issues

⦁ Discord Community
Was this page helpful? Yes No Edit this page

Assistants quickstart
Previous Next
Build your first assistant and make a phone call in minutes

Built with
Search / Ask AI

Assistants Tools

Voicemail Tool
Learn how to use the assistant-controlled voicemail tool for flexible voicemail handling

Overview

The voicemail tool gives your assistant direct control over when and how to leave voicemail
messages. Unlike automatic voicemail detection, which operates independently of your assistant,
this tool allows your assistant to decide when it’s reached a voicemail system and leave a
configured message.

Key benefits:

⦁ Maximum flexibility - Assistant decides when and what to say

⦁ Cost-effective - Only triggers when needed

⦁ Context-aware - Messages can be customized based on conversation

⦁ Simple integration - Works like other built-in tools

How it works

When you add the voicemail tool to your assistant:

1. Your assistant listens for voicemail indicators (greetings mentioning “unavailable”, “leave a
message”, etc.)

2. Upon detecting voicemail, the assistant calls the tool

3. The tool delivers your configured message

4. The call ends automatically after message delivery


This approach differs from automatic voicemail detection, which detects voicemail at the system
level. The voicemail tool puts detection and response entirely in the assistant’s hands.

Configuration

Add the voicemail tool to your assistant’s tools array:

API Configuration TypeScript SDK Python SDK

1 {
2 "model": {
3 "provider": "openai",
4 "model": "gpt-4o",
5 "messages": [
6 {
7 "type": "system",
8 "content": "You are a sales representative for Acme Corp. If at any point you
9 }
10 ],
11 "tools": [
12 {
13 "type": "voicemail",
14 "function": {
15 "name": "leave_voicemail",
16 "description": "Leave a voicemail message when you detect you've reached a
17 },
18 "messages": [
19 {
20 "type": "request-start",
21 "content": "Hi this is {{company}} {{message}} Please call us back at

Message Configuration

Define the voicemail message in the tool configuration:

Text-to-Speech Messages

1 {
2 "messages": [
3 {
4 "type": "request-start",
5 "content": "Hi, this is {{company}}. {{message}}. Please call us back at {{phone
6 }
7 ]
8 }

Use template variables like {{ company }} , {{ message }} , and {{ phone }} to make your
voicemail messages dynamic while keeping them consistent.

Pre-recorded Audio Messages

For consistent quality and pronunciation, use pre-recorded audio files by providing the URL in the
content field:

1 {
2 "messages": [
3 {
4 "type": "request-start",
5 "content": "[Link]
6 }
7 ]
8 }

Supported formats: .wav and .mp3 files

Pre-recorded audio messages are ideal for brand-specific messaging or when you need precise
pronunciation of phone numbers, website URLs, or company names.

Advanced Examples

Pre-recorded Audio Example

Using pre-recorded audio for professional voicemail messages:

1 {
2 "model": {
3 "provider": "openai",
4 "model": "gpt-4o",
5 "messages": [
6 {
7 "type": "system",
8 "content": "You are a sales representative calling prospects. If you reach vo
9 }
10 ],
11 "tools": [
12 {
13 "type": "voicemail",
14 "function": {
15 "name": "leave_voicemail",
16 "description": "Leave a professional pre-recorded voicemail message"
17 },
18 "messages": [
19 {
20 "type": "request-start",
21 "content": "[Link] com/professional sales voicemail mp3"

Dynamic voicemail with context

1 {
2 "model": {
3 "provider": "openai",
4 "model": "gpt-4o",
5 "messages": [
6 {
7 "type": "system",
8 "content": "You are calling leads about their recent inquiry. If you reach vo
9 }
10 ],
11 "tools": [
12 {
13 "type": "voicemail",
14 "function": {
15 "name": "leave_voicemail",
16 "description": "Leave a personalized voicemail message"
17 },
18 "messages": [
19 {
20 "type": "request-start",
21 "content": "Hi {{customer name}} this is {{agent name}} from {{company}}

Best Practices
Detection prompting

Be specific about voicemail indicators in your system prompt:

⦁ “unavailable”

⦁ “leave a message”

⦁ “voicemail”

⦁ “at the tone”

⦁ “beep”

Message structure

Keep voicemail messages:

⦁ Brief - Under 30 seconds

⦁ Clear - State name, company, and purpose

⦁ Actionable - Include callback number or next steps

⦁ Professional - Match your brand voice

Error handling

Consider edge cases:

⦁ Long voicemail greetings

⦁ Voicemail box full scenarios

⦁ Systems requiring keypad input

Voicemail Tool vs. Automatic Detection

Feature Voicemail Tool Automatic Detection

Control Assistant-driven System-driven

Flexibility High - custom logic Medium - predefined behavior

Cost Lower - only when used Higher - continuous monitoring


Feature Voicemail Tool Automatic Detection

Setup complexity Simple - just add tool Moderate - configure detection

Avoid combining the voicemail tool with automatic detection, as this could result in false positives
and other complications.

Choose the voicemail tool when you need maximum flexibility and cost efficiency. Choose automatic
detection when you need guaranteed system-level detection without relying on assistant prompting.

Common Use Cases

⦁ Sales outreach - Personalized follow-up messages

⦁ Appointment reminders - Leave detailed appointment information

⦁ Customer service - Callback scheduling with ticket numbers

⦁ Lead qualification - Leave targeted messages based on lead data

Next steps

⦁ Learn about other default tools

⦁ Explore automatic voicemail detection for system-level handling

⦁ See how to create custom tools for your specific needs

Was this page helpful? Yes No Edit this page

Custom Tools
Previous Next
Learn how to create and configure Custom Tools for use by your Vapi assis…
Built with
Search / Ask AI

Get started

Web calls
Build voice interfaces and backend integrations using Vapi's Web and Server SDKs

Overview

Build powerful voice applications that work across web browsers, mobile apps, and backend
systems. This guide covers both client-side voice interfaces and server-side call management
using Vapi’s comprehensive SDK ecosystem.

In this quickstart, you’ll learn to:

⦁ Create real-time voice interfaces for web and mobile

⦁ Build automated outbound and inbound call systems

⦁ Handle events and webhooks for call management

⦁ Implement voice widgets and backend integrations

Developing locally? The Vapi CLI makes it easy to initialize projects and test webhooks:

$ # Initialize Vapi in your project


$ vapi init
$
$ # Forward webhooks to local server
$ vapi listen --forward-to localhost:3000/webhook

Learn more about the Vapi CLI →

Choose your integration approach


Client-Side Voice Interfaces Server-Side Call Management
Best for: User-facing applications, voice Best for: Backend automation, bulk
widgets, mobile apps operations, system integrations
⦁ Browser-based voice assistants and ⦁ Automated outbound call campaigns
widgets ⦁ Inbound call routing and
⦁ Real-time voice conversations management
⦁ Mobile voice applications (iOS, ⦁ CRM integrations and bulk
Android, React Native, Flutter) operations
⦁ Direct user interaction with assistants ⦁ Webhook processing and real-time
events

Web voice interfaces

Build browser-based voice assistants and widgets for real-time user interaction.

Installation and setup


Web SDK React Native Flutter iOS

Build browser-based voice interfaces:

npm yarn pnpm bun

$ npm install @vapi-ai/web

1 import Vapi from '@vapi-ai/web';


2
3 const vapi = new Vapi('YOUR_PUBLIC_API_KEY');
4
5 // Start voice conversation
6 [Link]('YOUR_ASSISTANT_ID');
7
8 // Listen for events
9 [Link]('call-start', () => [Link]('Call started'));
10 [Link]('call-end', () => [Link]('Call ended'));
11 [Link]('message', (message) => {
12 if ([Link] === 'transcript') {
13 [Link](`${[Link]}: ${[Link]}`);
14 }
15 });

Voice widget implementation

Create a voice widget for your website:

HTML Script Tag React/TypeScript

The fastest way to get started. Copy this snippet into your website:

1 <script>
2 var vapiInstance = null;
3 const assistant = "assistant_id"; // Substitute with your assistant ID
4 const apiKey = "your_public_api_key"; // Substitute with your Public key from Vapi
5 const buttonConfig = {}; // Modify this as required
6
7 (function (d, t) {
8 var g = [Link](t),
9 s = [Link](t)[0];
10 [Link] =
11 "[Link]
12 [Link] = true;
13 [Link] = true;
14 [Link](g, s);
15
16 [Link] = function () {
17 vapiInstance = [Link]({
18 apiKey: apiKey, // mandatory
19 assistant: assistant, // mandatory
20 config: buttonConfig, // optional
21 });

Server-side call management

Automate outbound calls and handle inbound call processing with server-side SDKs.

Installation and setup


TypeScript Python Java Ruby C# Go

Install the TypeScript Server SDK:

npm yarn pnpm bun

$ npm install @vapi-ai/server-sdk

1 import { VapiClient } from "@vapi-ai/server-sdk";


2
3 const vapi = new VapiClient({
4 token: [Link].VAPI_API_KEY!
5 });
6
7 // Create an outbound call
8 const call = await [Link]({
9 phoneNumberId: "YOUR_PHONE_NUMBER_ID",
10 customer: { number: "+1234567890" },
11 assistantId: "YOUR_ASSISTANT_ID"
12 });
13
14 [Link](`Call created: ${[Link]}`);

Creating assistants
TypeScript Python Java Ruby C# Go

1 const assistant = await [Link]({


2 name: "Sales Assistant",
3 firstMessage: "Hi! I'm calling about your interest in our software solutions.",
4 model: {
5 provider: "openai",
6 model: "gpt-4o",
7 temperature: 0.7,
8 messages: [{
9 role: "system",
10 content: "You are a friendly sales representative. Keep responses under 30 word
11 }]
12 },
13 voice: {
14 provider: "11labs",
15 voiceId: "21m00Tcm4TlvDq8ikWAM"
16 }
17 });

Bulk operations

Run automated call campaigns for sales, surveys, or notifications:

TypeScript Python Java Ruby C# Go

1 async function runBulkCallCampaign(assistantId: string, phoneNumberId: string) {


2 const prospects = [
3 { number: "+1234567890", name: "John Smith" },
4 { number: "+1234567891", name: "Jane Doe" },
5 // ... more prospects
6 ];
7
8 const calls = [];
9 for (const prospect of prospects) {
10 const call = await [Link]({
11 assistantId,
12 phoneNumberId,
13 customer: prospect,
14 metadata: { campaign: "Q1_Sales" }
15 });
16 [Link](call);
17
18 // Rate limiting
19 await new Promise(resolve => setTimeout(resolve, 2000));
20 }
21

Webhook integration

Handle real-time events for both client and server applications:

TypeScript Python Java Ruby C# Go

1 import express from 'express';


2
3 const app = express();
4 [Link]([Link]());
5
6 [Link]('/webhook/vapi', async (req, res) => {
7 const { message } = [Link];
8
9 switch ([Link]) {
10 case 'status-update':
11 [Link](`Call ${[Link]}: ${[Link]}`);
12 break;
13 case 'transcript':
14 [Link](`${[Link]}: ${[Link]}`);
15 break;
16 case 'function-call':
17 return handleFunctionCall(message, res);
18 }
19
20 [Link](200).json({ received: true });

Next steps

Now that you understand both client and server SDK capabilities:

⦁ Explore use cases: Check out our examples section for complete implementations

⦁ Add tools: Connect your voice agents to external APIs and databases with custom tools

⦁ Configure models: Try different speech and language models for better performance

⦁ Scale with squads: Use Squads for multi-assistant setups and complex processes

Resources

Client SDKs:

⦁ Web SDK GitHub

⦁ React Native SDK GitHub

⦁ Flutter SDK GitHub

⦁ iOS SDK GitHub

⦁ Python Client GitHub


Server SDKs:

⦁ TypeScript SDK GitHub

⦁ Python SDK GitHub

⦁ Java SDK GitHub

⦁ Ruby SDK GitHub

⦁ C# SDK GitHub

⦁ Go SDK GitHub

Documentation:

⦁ API Reference

⦁ Discord Community

Was this page helpful? Yes No Edit this page

Guides
Previous Next
Explore real-world, cloneable examples to build voice agents with Assistan…

Built with
Marketplace Modules Web Widgets

Web Widgets
This guide aims to help developers create custom widgets for use in funnel builder and integrate
them seamlessly. We will cover how to create, set up, and render custom widgets using HTML, CSS,
and JavaScript or any JS frameworks like Angular, React, Vue along with communication between
your custom widget application and the funnel builder.

Prerequisites #​
Basic knowledge of HTML, CSS, JavaScript or Experience with JS frontend frameworks (Angular,
React, Vue or similar)
Familiarity with iFrames.
Understanding of event-driven programming.

Overview
Custom widgets allow you to extend functionalities of a funnel builder by embedding custom
elements like price banners or other interactive components.

Step-by-Step Guide
Step 1: Register yourself as a developer on the App Marketplace

Sign up as a developer on the App Marketplace.


Click on 'Create App' and start your app creation journey.

Step 2: Setting Up Your Custom Widget

Develop an independent web application which allows users to interact with UI elements and
generate HTML, CSS and JS (if required) which will render the custom widget element based on the
settings that they choose.
The application should have the following functions which emits HTML, CSS and JS
createHtml() => Returns the HTML code for the widget
createJS() => Returns the JS code required for the widget to run in the website (optional)
createCss() => Returns the css code required for the widget styles.

The application should use postmate for iFrame communication between the funnel builder. The
widget code can be emitted to the funnel builder via an event emit.

Example:

parent?.emit('code', {

html: html as string,

js: js as string,

elementStore: elementSettings as Object

})

Copy

Parameters:

html : HTML content required for widget to render along with styles

Example:

<style>{Your styles goes here}</style>


<div class="hl-banner">{Your HTML content goes here}</div>

Copy js : JS code required for the widget to run (optional)

Note Please make sure you don't wrap your js code inside <script /> tag .

If its a JS based application then all the code required for interacting with the funnel/website
popup, or other JS events specified in the upcoming sections should be included in the JS
emitted to the parent

elementStore: All the variables that represents the settings of the widget (variable names can be
anything of your preference)

Example:

settings: {
widgetHeight: number
widgetWidth: number
image: string
}
Copy

On application initialization or the initial handshake, expect for the following payload

{ elementStore: Object } // The elementStore which is emitted by your application


while sending the code to parent. Use this to prefill settings which is already
saved by user for your widget in the funnel builder.
Copy

and ensure that you emit the initial state of preview

parent?.emit('code', {
html: html as string,
js: js as string,
elementStore: elementSettings as Object
})

Note: ensure that you emit the initial state Make sure that the data received is filled to all the
respective settings of your widgets so that we can show the previously saved values on revisits

Step 3: Integrating With the Funnel Builder

1. Upload to Marketplace Build the project and upload the HTML, CSS & JS file or dist folder as a
zip to the marketplace app Ensure it adheres to the platform's guidelines and submission
requirements.

⚠️ Avoid using absolute path while building ensure you use relative paths in your project.
apps/

├── app1/

│ ├──[Link]

│ ├──css/

│ │ └── [Link]

│ └──js/

│ └── [Link]

└── app2/

├──[Link]

├──css/

│ └── [Link]

└──js/

└── [Link]

[Link]

absolute path : css/[Link] (Avoid this)

relative path: ./css/[Link]

2. Add Custom Widget to Funnel Elements

Once approved and available in the marketplace, the funnel builder will list your widget under a
“Custom Widgets” or similar section.
Users can install the custom widget from the marketplace.
Drag and Drop Widget to Funnel Builder

3. Limited Settings Configuration

Configure limited settings (like margin, padding, visibility, and custom classes) to be editable
directly from the funnel builder’s settings area.
Main widget settings should be configured through an external pop-up handled by your
application.

4. Render the Widget

Ensure the funnel builder can render the widget by interpreting the generated HTML, CSS, and
JavaScript.

Step 4: Communication Between Application and Funnel Builder

Using iFrames: Host(will take care of hosting) your settings application inside an iframe within the
funnel builder. Make sure it generates and communicates HTML, CSS, and JS code as settings are
adjusted.

Events and JS Integration: Custom widget events allow your custom widget to communicate with
the funnel preview environment. This communication is for creating interactive web applications
where actions in the widget can trigger responses in the funnel preview, resulting in a smoother and
more integrated user experience.

Key Concepts:

Event Emission: Your custom widget can send out signals (events) when users interact with it,
like clicking a button or changing a setting.
Event Handling: The funnel preview listens for these signals and performs certain actions in
response, like opening a popup or moving to the next step in a funnel.

Events:

1. customWidgetOpenPopup: This event triggers an action to open a popup on the preview side.

Example:

var event = new Event('customWidgetOpenPopup');


[Link](event)
Copy

2. customWidgetGoToNextStep: This event triggers an action to move to the next step/page in the
funnel/website.

Example:

var event = new Event('customWidgetGoToNextStGoToNextStep');


[Link](event)
Copy

Note: If you're using any framework router, make sure you use it in createMemoryHistory. For
reference, see the Vue Router Memory Mode.

CSS Note: Ensure that if you are utilizing media query for mobile devices, you also take into
account the compatibility with the funnel builder mobile mode by targeting the class with a .--
mobile prefix.

Example: Marketing Price Banner Widget: [Link]


banner

Checklist
When developing and integrating custom widgets into a funnel builder, it's crucial to ensure they
function effectively without causing any disruptions or conflicts. Please test your app to ensure it
meets the criteria mentioned in this checklist before submitting the app for review.

Ensure it does not disrupt builder functionality: Verify that the widget integrates smoothly
and does not interfere with the core functionalities of the funnel builder.
Confirm it does not conflict with other elements: Ensure that your widget does not overlap
or interfere with other elements already present in the builder, which could lead to visual or
functional issues.
Check for any external scripts: Be vigilant about any unintended external scripts being
included with your widget, especially if it's not meant to have such inclusions. These could
introduce security risks or functionality conflicts.
Verify that it does not disrupt the white labeling process: If the application supports white
labeling, ensure that the widget preserves this capability and does not inadvertently expose
brand-specific details.
Ensure app state remains consistent and persistent: Confirm that the widget maintains its
state across various interactions and revisits, ensuring a consistent user experience.
Check that the initial state is correctly displayed: On loading, the widget should accurately
reflect the initial state as intended, displaying all predefined settings and configurations.
Test all settings to ensure they function properly: Go through each configurable setting
within the widget to verify they perform as expected, without bugs or unexpected behavior.

By carefully reviewing each of these points, you can assure the quality and reliability of your custom
widgets in the funnel builder environment. This checklist serves as a quality assurance tool to catch
potential issues before deployment.

Upload Format Supported


According to the HighLevel Developer Guide for selling web widgets on the App Marketplace, there
is an option to upload files, but with specific guidelines:

You are expected to:

Build your widget project (i.e., HTML, CSS, JS).


Bundle it into a .zip file (not .rar).
Upload the .zip file to the App Marketplace during the widget setup process.
.RAR files are not supported.
Only .zip is mentioned as the accepted archive format.

Best Practice:

When creating your widget:

Use relative paths for assets.


Keep your build clean — no unnecessary files or folders.
Ensure your HTML, CSS, and JS are in the correct directory structure before zipping.
If you're using a frontend framework (like React or Vue), zip the build/dist folder, not the entire
project directory.

Developer Resources
For step-by-step technical instructions and best practices, please refer to the following resources:

Full Widget Creation Guide


Loom Video Walkthrough

Share your feedback

★★★★★

You might also like