> ## Documentation Index
> Fetch the complete documentation index at: https://docs.verbeo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Tools

> Give an assistant the tools it can use during a live call: your own HTTP endpoints, and Verbeo's built-in end-call, transfer and SMS tools. The tool object, how secrets are handled, and how the endpoints fit together.

A **tool** is something an assistant can do during a live call. There are two kinds:

* A **custom tool** is an HTTP endpoint of yours. When the assistant decides to use it, Verbeo calls your `endpoint_url` with the arguments the model filled in, and the assistant uses your answer in the conversation. [Tool call webhooks](/api-reference/webhooks/tool-calls) describes exactly what your endpoint receives.
* A **native tool** is one Verbeo runs itself: `end_call` hangs up, `transfer_call` hands the call to a person, `send_sms` texts the caller.

Every tool belongs to one assistant: its `agent_id`. Listing tools always names the assistant.

## Endpoints

| Endpoint | Purpose |
| - | - |
| [`POST /tool/list`](/api-reference/tools/list) | One assistant's tools, both kinds, paged and searchable |
| [`GET /tool/{id}`](/api-reference/tools/get) | One tool |
| [`POST /tool/custom`](/api-reference/tools/create-custom) | Add a custom tool |
| [`PUT /tool/custom/{id}`](/api-reference/tools/update-custom) | Change a custom tool |
| [`POST /tool/native`](/api-reference/tools/create-native) | Add `end_call`, `send_sms` or `transfer_call` |
| [`PUT /tool/native/{id}`](/api-reference/tools/update-native) | Change a native tool's settings |
| [`DELETE /tool/{id}`](/api-reference/tools/delete) | Delete a tool of either kind |

Each kind is added and changed through its own routes, and each takes only its own fields: anything else in the body is ignored. A tool cannot change its kind or move to another assistant.

<Note>
  Listing is a `POST` because a list query is a document: page, size and a search term, typed as themselves rather than coerced from a query string. It reads, and changes nothing.
</Note>

## Tools and the assistant

An assistant uses a tool only while the tool's id is in its [`config.toolIds`](/api-reference/assistants/overview#the-configuration-document). These endpoints keep that list in step for you:

* Adding a tool appends its id to the assistant's `toolIds`.
* Adding a native tool also switches it on in the assistant's `config.nativeTools`, and every later change to it is copied there.
* Deleting a tool takes its id out of `toolIds`, and switches a native tool off in `nativeTools`.

The assistant's [`nativeTools`](/api-reference/assistants/overview#native-tools) and the native endpoints here are two ways to change the same tool. The assistant names the settings in camelCase (`staticDestination`, `smsContentType`); these endpoints use the tool's own field names (`transfer_call_destination`, `sms_content_type`). A `send_sms` tool's Twilio credentials, and the `timeout_seconds` of a custom or `send_sms` tool, can only be set here; `end_call` and `transfer_call` always wait 30 seconds.

<Warning>
  [Replacing an assistant's configuration](/api-reference/assistants/replace) (`PUT`) replaces its `toolIds` too: if the document you send leaves a tool's id out, the assistant stops using that tool, though the tool itself is kept. [Updating the assistant](/api-reference/assistants/update) (`PATCH`) keeps `toolIds` unless you send them, and a `toolIds` you send replaces the whole list.
</Warning>

## Secrets are never returned

No response carries a secret, whoever asks:

* Every value in a custom tool's `headers` comes back as `"********"`. The header names are kept, so you can see which headers a tool sends.
* A `send_sms` tool's `twilio_account_sid` comes back as `"********"` when one is saved, and `null` when none is.
* The Twilio auth token, `twilio_auth_token`, is never returned at all.

Sending `"********"` back keeps what is saved. You can read a tool, change what you need, and send it back whole without knowing its secrets:

| On an update, you send | What happens |
| - | - |
| A header whose value is `"********"` | The header keeps the value saved under that same name |
| A header with any other value | That value replaces the saved one |
| `headers` without one of the tool's headers | That header is removed: `headers` is replaced as a whole |
| `twilio_account_sid` or `twilio_auth_token` set to `"********"` | The saved value is kept |

The placeholder stands only for a value already saved under the same name. A create has nothing saved, so `"********"` there is refused with `422 VALIDATION_FAILED`:

```text theme={null}
headers.Authorization: "********" stands for a saved value, and a new tool has none: send the value itself
```

Renaming a header while keeping `"********"` as its value is refused the same way on an update:

```text theme={null}
headers.X-Api-Key: "********" keeps a saved value, and this tool has no header named "X-Api-Key": send the value itself
```

<Tip>
  Put the credentials your endpoint needs in `headers`, never in `endpoint_url`: the URL is returned exactly as you stored it.
</Tip>

<Warning>
  Hidden is not out of reach. A saved header goes with every request the tool makes, wherever its `endpoint_url` points, so anyone who can change a tool can send its saved headers to a server of their choosing. That is any private API key of the workspace, and any member with the developer, admin or owner role. Keep private keys private, give those roles only to people you would trust with the credentials themselves, and rotate a credential if a key or an account that could change its tool is ever exposed.
</Warning>

## The tool object

A custom tool:

```json theme={null}
{
  "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": 30,
  "is_native": false,
  "sms_content_type": null,
  "sms_static_message": null,
  "transfer_call_mode": "static",
  "transfer_call_destination": null,
  "transfer_call_type": "cold",
  "twilio_account_sid": null,
  "twilio_from_number": null,
  "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"
}
```

A native `send_sms` tool with its own Twilio account:

```json theme={null}
{
  "id": "4f1c2d8e-77a1-4a5e-9b31-1f0a2c3d4e5f",
  "agent_id": "550e8400-e29b-41d4-a716-446655440000",
  "tool_name": "send_sms",
  "description": "Text the caller our address when they ask for it.",
  "endpoint_url": "native://send_sms",
  "http_method": "POST",
  "headers": {},
  "parameters": {
    "properties": {
      "message": { "type": "string", "description": "The SMS message content to send to the caller" }
    },
    "required": ["message"]
  },
  "timeout_seconds": 30,
  "is_native": true,
  "sms_content_type": "prompt",
  "sms_static_message": null,
  "transfer_call_mode": "static",
  "transfer_call_destination": null,
  "transfer_call_type": "cold",
  "twilio_account_sid": "********",
  "twilio_from_number": "+14155550123",
  "tool_behavior": null,
  "result_scheduling": null,
  "created_at": "2026-01-15T10:30:00.123456+00:00",
  "updated_at": "2026-01-22T14:00:00.654321+00:00"
}
```

Every tool carries every field. A field that does not apply to a tool's kind holds its default or `null`: a tool that is not a transfer reads `transfer_call_mode` as `"static"` or `null`, for example. Ignore them.

<ResponseField name="id" type="string" required>UUID of the tool.</ResponseField>
<ResponseField name="agent_id" type="string" required>UUID of the assistant the tool belongs to.</ResponseField>
<ResponseField name="tool_name" type="string" required>The name the model calls the tool by: yours for a custom tool, `end_call`, `send_sms` or `transfer_call` for a native one. Unique within the assistant.</ResponseField>
<ResponseField name="description" type="string | null" required>What the model is told about the tool. Say when to use it.</ResponseField>
<ResponseField name="is_native" type="boolean | null" required>`true` for a native tool. A custom tool reads `false`, or `null` if it was created long ago.</ResponseField>
<ResponseField name="endpoint_url" type="string" required>A custom tool's endpoint. A native tool reads `native://` and its name.</ResponseField>
<ResponseField name="http_method" type="string" required>`GET` or `POST`. A native tool reads `POST`.</ResponseField>
<ResponseField name="headers" type="object | null" required>The headers sent with every request to a custom tool, each value `"********"`. A native tool reads `{}`.</ResponseField>
<ResponseField name="parameters" type="object | null" required>The JSON Schema of the arguments the model fills in. `null` or `{}`: the tool takes no arguments. A native tool's is set by Verbeo from its settings.</ResponseField>
<ResponseField name="timeout_seconds" type="number | null" required>How long Verbeo waits for the tool, 5–300 seconds. `30` unless you set it.</ResponseField>
<ResponseField name="tool_behavior" type="string | null" required>Custom tools: `blocking` (the assistant waits for the result) or `non_blocking` (the assistant keeps talking). `null` means `blocking`.</ResponseField>
<ResponseField name="result_scheduling" type="string | null" required>Custom tools with `tool_behavior: "non_blocking"`: `when_idle` (said once the assistant has finished talking) or `silent` (never said). `null` means `when_idle`.</ResponseField>
<ResponseField name="sms_content_type" type="string | null" required>`send_sms`: `prompt` (the assistant writes the message) or `static` (always `sms_static_message`).</ResponseField>
<ResponseField name="sms_static_message" type="string | null" required>`send_sms`: the text a static SMS sends.</ResponseField>
<ResponseField name="twilio_account_sid" type="string | null" required>`send_sms`: `"********"` when the tool has its own Twilio account saved, `null` when it does not.</ResponseField>
<ResponseField name="twilio_from_number" type="string | null" required>`send_sms`: the sender the SMS goes out from. `null`: Verbeo picks the sender, normally the number the call is on.</ResponseField>
<ResponseField name="transfer_call_mode" type="string | null" required>`transfer_call`: `static` (always dials `transfer_call_destination`) or `dynamic` (the assistant chooses the number).</ResponseField>
<ResponseField name="transfer_call_destination" type="string | null" required>`transfer_call`: the number a static transfer dials.</ResponseField>
<ResponseField name="transfer_call_type" type="string | null" required>`transfer_call`: `cold` or `warm`.</ResponseField>
<ResponseField name="created_at" type="string" required>ISO 8601 timestamp with microseconds and a UTC offset, e.g. `2026-01-15T10:30:00.123456+00:00`.</ResponseField>
<ResponseField name="updated_at" type="string" required>The last change, in the same format.</ResponseField>

`tool_behavior` and `result_scheduling` take effect on assistants that run Gemini 3.8 Live; other models ignore them. [Connect your backend with custom tools](/guides/custom-tools#while-the-tool-runs) explains what the caller hears with each.

## Who may do what

An API key acts as the owner of its workspace, so it may read, add, change and delete the tools of every assistant in that workspace. An assistant or a tool in another workspace answers `404 NOT_FOUND`, exactly like one that does not exist.

## Authentication

Every endpoint takes a **private** workspace API key (`vbr_…`) as a Bearer token. Public widget keys (`vbr_pub_…`) are rejected with `403 FORBIDDEN`: they ship inside web pages, where anyone can read them. See [Authentication](/api-reference/authentication).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.