> ## 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.

# 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.

Adds a custom tool to an assistant and appends its id to 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](/api-reference/webhooks/tool-calls) for the request your endpoint receives and the answer it gives.

## Body

<ParamField body="agent_id" type="string" required>
  UUID of the assistant the tool belongs to.
</ParamField>

<ParamField body="tool_name" type="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.
</ParamField>

<ParamField body="description" type="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.
</ParamField>

<ParamField body="endpoint_url" type="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.
</ParamField>

<ParamField body="http_method" type="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.
</ParamField>

<ParamField body="headers" type="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.
</ParamField>

<ParamField body="parameters" type="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 `properties` object, 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`, `then` and `else` are not supported, in a parameter, a nested object's properties or an array's `items`. For a fixed value, use `enum` with one value.
</ParamField>

<ParamField body="timeout_seconds" type="number" default="30">
  How long Verbeo waits for your endpoint to answer: a whole number of seconds, from `5` to `300`.
</ParamField>

<ParamField body="tool_behavior" type="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.
</ParamField>

<ParamField body="result_scheduling" type="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.
</ParamField>

`tool_behavior` and `result_scheduling` take effect on assistants that run Gemini 3.8 Live; other models ignore them. See [While the tool runs](/guides/custom-tools#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

<CodeGroup>
  ```bash cURL theme={null}
  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
    }'
  ```

  ```javascript Node.js theme={null}
  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();
  ```

  ```python Python theme={null}
  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"]
  ```
</CodeGroup>

## Response

`201 Created` with the new [tool object](/api-reference/tools/overview#the-tool-object). The header you sent comes back as `"********"`.

```json theme={null}
{
  "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`. |

Nothing is left behind when a request fails: if the assistant's `toolIds` cannot be updated, the tool is removed again.


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