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

# Update custom tool

> Change a custom tool's name, endpoint, headers, arguments or timing. Send only the fields you are changing; saved header values are kept when you send them back as the placeholder.

Updates a custom tool in place. Every field is optional and anything you leave out is untouched. The tool stays on the same assistant, and stays a custom tool.

## Path parameters

<ParamField path="id" type="string" required>
  UUID of the custom tool. A native tool's id answers `404 NOT_FOUND`: change those with [Update native tool](/api-reference/tools/update-native).
</ParamField>

## Body

At least one field is required. The rules are those of [Create custom tool](/api-reference/tools/create-custom); what is different on an update is below.

<ParamField body="tool_name" type="string">
  A new name. It is checked against the reserved names and the assistant's other tools only when it changes, so sending the current name back is always fine.
</ParamField>

<ParamField body="description" type="string | null">
  `null` clears it.
</ParamField>

<ParamField body="endpoint_url" type="string">
  A new endpoint. `null` is read as "not sent": a custom tool always has one.
</ParamField>

<ParamField body="http_method" type="string">
  `GET` or `POST`. `null` is read as "not sent".
</ParamField>

<ParamField body="headers" type="object">
  The tool's **whole** set of headers: a header you leave out is removed, and `{}` removes them all. A value of `"********"`, which is what a read returns, keeps the value saved under that same header name (matched exactly, case included); any other value replaces it. `null` is read as "not sent" and keeps the headers as they are.
</ParamField>

<ParamField body="parameters" type="object | null">
  A new argument schema. `null` or `{}` makes it a tool that takes no arguments.
</ParamField>

<ParamField body="timeout_seconds" type="number">
  `5`–`300`. `null` is read as "not sent".
</ParamField>

<ParamField body="tool_behavior" type="string | null">
  `blocking` or `non_blocking`. `null` goes back to the default, `blocking`.
</ParamField>

<ParamField body="result_scheduling" type="string | null">
  `when_idle` or `silent`. `null` goes back to the default, `when_idle`. The rule that it needs `tool_behavior: "non_blocking"` is checked against the tool as it would be saved: the stored values plus your changes.
</ParamField>

`agent_id`, `is_native` and any other field are ignored.

### Keeping a saved header

Read the tool, change what you need, and send `headers` back as you read them. The `"********"` values keep their saved secrets:

```json theme={null}
{
  "endpoint_url": "https://api.example.com/v2/verbeo/orders",
  "headers": { "Authorization": "********", "X-Region": "eu" }
}
```

Here `Authorization` keeps its saved value, `X-Region` is added, and every other header the tool had is removed. `"********"` under a name the tool has no saved value for is refused, because there is nothing for it to stand for. That happens, for example, when you rename `Authorization` to `X-Api-Key` and keep the placeholder: send the real value under the new name.

## Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PUT "https://api.verbeo.ai/v1/tool/custom/8b2f6c1e-3d4a-4f5b-9c7e-1a2b3c4d5e6f" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "endpoint_url": "https://api.example.com/v2/verbeo/orders",
      "headers": { "Authorization": "********", "X-Region": "eu" },
      "tool_behavior": "non_blocking"
    }'
  ```

  ```javascript Node.js theme={null}
  const id = "8b2f6c1e-3d4a-4f5b-9c7e-1a2b3c4d5e6f";
  const res = await fetch(`https://api.verbeo.ai/v1/tool/custom/${id}`, {
    method: "PUT",
    headers: {
      Authorization: `Bearer ${process.env.VERBEO_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      endpoint_url: "https://api.example.com/v2/verbeo/orders",
      headers: { Authorization: "********", "X-Region": "eu" },
      tool_behavior: "non_blocking",
    }),
  });
  const { data: tool } = await res.json();
  ```

  ```python Python theme={null}
  import os, requests

  tool_id = "8b2f6c1e-3d4a-4f5b-9c7e-1a2b3c4d5e6f"
  tool = requests.put(
      f"https://api.verbeo.ai/v1/tool/custom/{tool_id}",
      headers={"Authorization": f"Bearer {os.environ['VERBEO_API_KEY']}"},
      json={
          "endpoint_url": "https://api.example.com/v2/verbeo/orders",
          "headers": {"Authorization": "********", "X-Region": "eu"},
          "tool_behavior": "non_blocking",
      },
  ).json()["data"]
  ```
</CodeGroup>

## Response

`200 OK` with the updated [tool object](/api-reference/tools/overview#the-tool-object), every header value masked.

```json theme={null}
{
  "success": true,
  "data": {
    "id": "8b2f6c1e-3d4a-4f5b-9c7e-1a2b3c4d5e6f",
    "tool_name": "lookup_order",
    "endpoint_url": "https://api.example.com/v2/verbeo/orders",
    "http_method": "POST",
    "headers": { "Authorization": "********", "X-Region": "********" },
    "tool_behavior": "non_blocking",
    "result_scheduling": null,
    "updated_at": "2026-01-23T09:12:00.456789+00:00",
    "…": "the rest of the tool object"
  }
}
```

## Errors

| Code | Status | When |
| - | - | - |
| `VALIDATION_FAILED` | 422 | `id` is not a UUID; an empty body (`No fields to update`); a field that breaks a [create rule](/api-reference/tools/create-custom#errors); a `result_scheduling` on a tool that would not be `non_blocking`; or `"********"` under a header name with no saved value (`headers.X-Api-Key: "********" keeps a saved value, and this tool has no header named "X-Api-Key": send the value itself`). |
| `CONFLICT` | 409 | A new `tool_name` another tool of the assistant already has. |
| `NOT_FOUND` | 404 | No such tool, or it belongs to another workspace (`Tool not found`); or the id is a native tool's (`Custom tool not found`). |


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