Tools
Create custom tool
Add an HTTP endpoint of yours as a tool an assistant can call during a live call. The tool is added to the assistant’s toolIds in the same request.
POST
/
tool
/
custom
Create custom tool
curl --request POST \
--url https://api.verbeo.ai/v1/tool/custom \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"agent_id": "<string>",
"tool_name": "<string>",
"description": {},
"endpoint_url": "<string>",
"http_method": "<string>",
"headers": {},
"parameters": {},
"timeout_seconds": 123,
"tool_behavior": {},
"result_scheduling": {}
}
'import requests
url = "https://api.verbeo.ai/v1/tool/custom"
payload = {
"agent_id": "<string>",
"tool_name": "<string>",
"description": {},
"endpoint_url": "<string>",
"http_method": "<string>",
"headers": {},
"parameters": {},
"timeout_seconds": 123,
"tool_behavior": {},
"result_scheduling": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
agent_id: '<string>',
tool_name: '<string>',
description: {},
endpoint_url: '<string>',
http_method: '<string>',
headers: {},
parameters: {},
timeout_seconds: 123,
tool_behavior: {},
result_scheduling: {}
})
};
fetch('https://api.verbeo.ai/v1/tool/custom', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.verbeo.ai/v1/tool/custom",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'agent_id' => '<string>',
'tool_name' => '<string>',
'description' => [
],
'endpoint_url' => '<string>',
'http_method' => '<string>',
'headers' => [
],
'parameters' => [
],
'timeout_seconds' => 123,
'tool_behavior' => [
],
'result_scheduling' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.verbeo.ai/v1/tool/custom"
payload := strings.NewReader("{\n \"agent_id\": \"<string>\",\n \"tool_name\": \"<string>\",\n \"description\": {},\n \"endpoint_url\": \"<string>\",\n \"http_method\": \"<string>\",\n \"headers\": {},\n \"parameters\": {},\n \"timeout_seconds\": 123,\n \"tool_behavior\": {},\n \"result_scheduling\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.verbeo.ai/v1/tool/custom")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"agent_id\": \"<string>\",\n \"tool_name\": \"<string>\",\n \"description\": {},\n \"endpoint_url\": \"<string>\",\n \"http_method\": \"<string>\",\n \"headers\": {},\n \"parameters\": {},\n \"timeout_seconds\": 123,\n \"tool_behavior\": {},\n \"result_scheduling\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.verbeo.ai/v1/tool/custom")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"agent_id\": \"<string>\",\n \"tool_name\": \"<string>\",\n \"description\": {},\n \"endpoint_url\": \"<string>\",\n \"http_method\": \"<string>\",\n \"headers\": {},\n \"parameters\": {},\n \"timeout_seconds\": 123,\n \"tool_behavior\": {},\n \"result_scheduling\": {}\n}"
response = http.request(request)
puts response.read_bodyAdds a custom tool to an assistant and appends its id to the assistant’s
Nothing is left behind when a request fails: if the assistant’s
config.toolIds, so the assistant can use it on its next call. When the assistant calls the tool, Verbeo sends the arguments to your endpoint_url: see Tool call webhooks for the request your endpoint receives and the answer it gives.
Body
string
required
UUID of the assistant the tool belongs to.
string
required
The name the model calls the tool by. Trimmed, then 1–64 characters: a letter or an underscore first, then letters, digits and underscores only, e.g.
lookup_order. Unique within the assistant. end_call, send_sms, transfer_call and report_voicemail are reserved for built-in tools.string | null
What the model is told about the tool. Say when to use it: “Call this when the caller asks where their order is.” The model decides from this whether to call the tool at all.
string
required
Your endpoint: a full
https:// (or http://) URL. It is returned as you send it, so put credentials in headers, not in the URL.string
required
GET or POST, in any case; it is stored upper-case. A GET sends the arguments as a query string, a POST as a JSON body with the call’s context.object
default:"{}"
Header names and their string values, sent with every request to your endpoint: an
Authorization key, say. Responses show every value as "********". A value of "********" is refused here, since a new tool has no saved value for it to stand for.object | null
The JSON Schema of the arguments the model fills in from the conversation.
null or {}: the tool takes no arguments. Otherwise:- It needs a
propertiesobject, each key a parameter name: a letter or an underscore first, then letters, digits and underscores. required, if present, lists parameter names.const,oneOf,anyOf,allOf,not,$ref,if,thenandelseare not supported, in a parameter, a nested object’s properties or an array’sitems. For a fixed value, useenumwith one value.
number
default:"30"
How long Verbeo waits for your endpoint to answer: a whole number of seconds, from
5 to 300.string | null
default:"blocking"
blocking: the assistant waits for your answer. non_blocking: the assistant keeps talking while your endpoint works. null is the default.string | null
default:"when_idle"
Only with
tool_behavior: "non_blocking". when_idle: the assistant gives your result once it has finished talking. silent: the assistant confirms the request at once and never announces the result, but can use it if the caller asks. null is the default.tool_behavior and result_scheduling take effect on assistants that run Gemini 3.8 Live; other models ignore them. See While the tool runs.
Fields that are not a custom tool’s are ignored, is_native and a native tool’s settings among them.
Request
curl -X POST "https://api.verbeo.ai/v1/tool/custom" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"tool_name": "lookup_order",
"description": "Call this when the caller asks where their order is.",
"endpoint_url": "https://api.example.com/verbeo/orders",
"http_method": "POST",
"headers": { "Authorization": "Bearer my-endpoint-secret" },
"parameters": {
"type": "object",
"properties": {
"order_number": { "type": "string", "description": "The order number the caller reads out" }
},
"required": ["order_number"]
},
"timeout_seconds": 20
}'
const res = await fetch("https://api.verbeo.ai/v1/tool/custom", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VERBEO_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
agent_id: "550e8400-e29b-41d4-a716-446655440000",
tool_name: "lookup_order",
description: "Call this when the caller asks where their order is.",
endpoint_url: "https://api.example.com/verbeo/orders",
http_method: "POST",
headers: { Authorization: `Bearer ${process.env.MY_ENDPOINT_SECRET}` },
parameters: {
type: "object",
properties: {
order_number: { type: "string", description: "The order number the caller reads out" },
},
required: ["order_number"],
},
timeout_seconds: 20,
}),
});
const { data: tool } = await res.json();
import os, requests
tool = requests.post(
"https://api.verbeo.ai/v1/tool/custom",
headers={"Authorization": f"Bearer {os.environ['VERBEO_API_KEY']}"},
json={
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"tool_name": "lookup_order",
"description": "Call this when the caller asks where their order is.",
"endpoint_url": "https://api.example.com/verbeo/orders",
"http_method": "POST",
"headers": {"Authorization": f"Bearer {os.environ['MY_ENDPOINT_SECRET']}"},
"parameters": {
"type": "object",
"properties": {
"order_number": {"type": "string", "description": "The order number the caller reads out"},
},
"required": ["order_number"],
},
"timeout_seconds": 20,
},
).json()["data"]
Response
201 Created with the new tool object. The header you sent comes back as "********".
{
"success": true,
"data": {
"id": "8b2f6c1e-3d4a-4f5b-9c7e-1a2b3c4d5e6f",
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"tool_name": "lookup_order",
"description": "Call this when the caller asks where their order is.",
"endpoint_url": "https://api.example.com/verbeo/orders",
"http_method": "POST",
"headers": { "Authorization": "********" },
"parameters": {
"type": "object",
"properties": {
"order_number": { "type": "string", "description": "The order number the caller reads out" }
},
"required": ["order_number"]
},
"timeout_seconds": 20,
"is_native": false,
"tool_behavior": null,
"result_scheduling": null,
"created_at": "2026-01-15T10:30:00.123456+00:00",
"updated_at": "2026-01-15T10:30:00.123456+00:00",
"…": "the rest of the tool object"
}
}
Errors
| Code | Status | When |
|---|---|---|
VALIDATION_FAILED | 422 | A missing or malformed field; a tool_name that is reserved (tool_name: "end_call" is reserved for a built-in tool); an endpoint_url that is not an http or https URL; an http_method other than GET or POST; a parameters schema Verbeo cannot declare; timeout_seconds outside 5–300; a result_scheduling without tool_behavior: "non_blocking"; or a header value of "********". The message names the field. |
CONFLICT | 409 | The assistant already has a tool of that name: This agent already has a tool named "lookup_order". |
NOT_FOUND | 404 | No such assistant, or it belongs to another workspace: Agent not found. |
toolIds cannot be updated, the tool is removed again.Was this page helpful?
⌘I
Create custom tool
curl --request POST \
--url https://api.verbeo.ai/v1/tool/custom \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"agent_id": "<string>",
"tool_name": "<string>",
"description": {},
"endpoint_url": "<string>",
"http_method": "<string>",
"headers": {},
"parameters": {},
"timeout_seconds": 123,
"tool_behavior": {},
"result_scheduling": {}
}
'import requests
url = "https://api.verbeo.ai/v1/tool/custom"
payload = {
"agent_id": "<string>",
"tool_name": "<string>",
"description": {},
"endpoint_url": "<string>",
"http_method": "<string>",
"headers": {},
"parameters": {},
"timeout_seconds": 123,
"tool_behavior": {},
"result_scheduling": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
agent_id: '<string>',
tool_name: '<string>',
description: {},
endpoint_url: '<string>',
http_method: '<string>',
headers: {},
parameters: {},
timeout_seconds: 123,
tool_behavior: {},
result_scheduling: {}
})
};
fetch('https://api.verbeo.ai/v1/tool/custom', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.verbeo.ai/v1/tool/custom",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'agent_id' => '<string>',
'tool_name' => '<string>',
'description' => [
],
'endpoint_url' => '<string>',
'http_method' => '<string>',
'headers' => [
],
'parameters' => [
],
'timeout_seconds' => 123,
'tool_behavior' => [
],
'result_scheduling' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.verbeo.ai/v1/tool/custom"
payload := strings.NewReader("{\n \"agent_id\": \"<string>\",\n \"tool_name\": \"<string>\",\n \"description\": {},\n \"endpoint_url\": \"<string>\",\n \"http_method\": \"<string>\",\n \"headers\": {},\n \"parameters\": {},\n \"timeout_seconds\": 123,\n \"tool_behavior\": {},\n \"result_scheduling\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.verbeo.ai/v1/tool/custom")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"agent_id\": \"<string>\",\n \"tool_name\": \"<string>\",\n \"description\": {},\n \"endpoint_url\": \"<string>\",\n \"http_method\": \"<string>\",\n \"headers\": {},\n \"parameters\": {},\n \"timeout_seconds\": 123,\n \"tool_behavior\": {},\n \"result_scheduling\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.verbeo.ai/v1/tool/custom")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"agent_id\": \"<string>\",\n \"tool_name\": \"<string>\",\n \"description\": {},\n \"endpoint_url\": \"<string>\",\n \"http_method\": \"<string>\",\n \"headers\": {},\n \"parameters\": {},\n \"timeout_seconds\": 123,\n \"tool_behavior\": {},\n \"result_scheduling\": {}\n}"
response = http.request(request)
puts response.read_body