> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.runtype.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.runtype.com/_mcp/server.

# Creating external tools

Call an HTTP API from an Agent by configuring an external Tool. Set the endpoint, request method, headers, body, and parameters in the dashboard.

## Before you begin

If the endpoint requires a credential, store it as a managed secret. For instructions, see [Managing secrets](/user-guide/settings/managing-secrets).

## Create an external Tool

Create an external Tool with these steps:

1. Click **Tools** in the sidebar.
2. Click **Create Tool**.
3. Select **External API**.
4. Enter a name and description. Describe what the endpoint does and when to use it.
5. Click **Create Tool**.

The tool editor opens. A new external Tool starts with a `Content-Type: application/json` header and one required parameter named `data` of type **Object**. Configure the request and parameters as described in the following sections, then click **Save**.

## Configure the HTTP request

Choose **GET**, **POST**, **PUT**, **PATCH**, or **DELETE** in **HTTP Method**. The default is **POST**.

### URL

Enter the endpoint in **API Endpoint**. The endpoint must use HTTPS. Runtype blocks requests to private network addresses and `localhost`. Runtype follows up to five redirects and applies the same checks to each redirect target.

Use `{{parameter_name}}` expressions to insert parameter values into the URL:

```plaintext
https://example.com/customers/{{customer_id}}
```

Each expression must match the name of a parameter that you define in **Tool Parameters**. To insert a field of an object parameter, use a dot path such as `{{customer.id}}`.

Runtype inserts URL values as-is, without URL encoding. A value that contains a space, `&`, or `#` changes the meaning of the URL. For free-text values on a GET Tool, leave the parameter out of the URL. Runtype then appends it to the query string with URL encoding.

For GET requests, Runtype appends each parameter that the URL does not use to the query string. It omits parameters that have no value or a `null` value, and it JSON-encodes object and array values. When the URL uses any field of an object parameter, Runtype does not also append that parameter.

### Headers

Add request headers with these steps:

1. In **Request Headers**, click **Add Header**.
2. Enter the name in the **Header name** field, for example `Authorization`.
3. Enter the value in the **Header value** field, for example `Bearer {{secret:CRM_API_KEY}}`.

`CRM_API_KEY` is the name of a managed secret in your account. For more credential formats, see [Configure authentication](#configure-authentication).

A header value can also contain a parameter expression, such as `{{region}}`. Runtype still sends that parameter in the query string for GET requests, or in the default request body. To keep the parameter out of the body, set a body template.

Runtype sends `Content-Type: application/json` by default. Change the **Content-Type** header when the endpoint requires another value.

### Request body

For methods other than GET, enter an optional JSON template in **Body Template (Optional)**. Runtype encodes each value as JSON before it inserts the value, so leave each expression unquoted.

The following body template sends three parameters and one fixed field:

```json
{
  "id": {{customer_id}},
  "name": {{name}},
  "active": {{is_active}},
  "metadata": {
    "source": "runtype-agent"
  }
}
```

The following table shows how the body renders each value type:

| Input type | Example value | Rendered JSON    |
| ---------- | ------------- | ---------------- |
| `string`   | `Dana`        | `"name": "Dana"` |
| `number`   | `42`          | `"count": 42`    |
| `boolean`  | `true`        | `"active": true` |
| `null`     | `null`        | `"value": null`  |
| `array`    | `[1, 2]`      | `"ids": [1,2]`   |

Runtype escapes quotes, backslashes, and control characters inside string values. If you quote an expression, for example `"name": "{{name}}"`, the string receives a second set of quotes. The body is then invalid JSON, and the endpoint usually rejects the request with status `400`.

Don't insert a whole object parameter into a body template. Its nested values are sent as quoted strings, so `{"count": 2}` arrives as `{"count": "2"}`. Insert each field with a dot path instead, for example `"count": {{order.count}}`, or leave the body template empty.

If you leave **Body Template (Optional)** empty, Runtype sends a JSON object that contains each parameter that the URL does not use.

## Define parameters

Define parameters that match the expressions in the URL, headers, and body with these steps:

1. In **Tool Parameters**, delete the default `data` parameter unless the endpoint expects a `data` object.
2. Click **Add Parameter**.
3. Enter a name that matches the expression, without the braces.
4. Select **String**, **Number**, **Boolean**, **Object**, or **Array** as the type.
5. Enter a description that explains the parameter's value.
6. Turn **Required** on when the Agent must provide the parameter.
7. Click **Save**.

The parameter editor also has constraint fields: **Min Length**, **Max Length**, **Pattern (RegEx)**, and **Enum Values (comma-separated)** for strings, and **Minimum** and **Maximum** for numbers. Runtype saves these in the parameter schema.

Runtype does not fill in **Default Value** when the Agent omits the parameter. An omitted parameter in a URL, header, or body is sent as the literal text, for example `{{limit}}`. To guarantee a value, write the fallback in the expression, for example `{{limit || 10}}`. The `??` operator also works.

On a saved Tool, the **Hidden** switch has no effect: the Agent still sees the parameter and provides its value. This does not apply to a parameter named `_endUser` or `_tenant`: Runtype removes those from the model-facing schema and fills them only from the execution's verified identity. See [Use the verified identity in a saved tool](/user-guide/tools/runtime-tools#use-the-verified-identity-in-a-saved-tool). To fill a parameter from an execution variable instead of from the model, define the Tool as a runtime tool with `hiddenParameterNames`. For more information, see [Hide parameters from the model](/user-guide/tools/runtime-tools#hide-parameters-from-the-model).

## Configure authentication

Send each credential as a managed secret reference in the form `{{secret:SECRET_NAME}}`. Runtype resolves the reference when the request runs. It does not expose the secret value to the Agent or return it in tool results. You can use a secret reference in the URL, a header, or the request body.

The following headers send an API key or bearer token:

```plaintext
Authorization: Bearer {{secret:CRM_API_KEY}}
X-API-Key: {{secret:CRM_API_KEY}}
```

For Basic authentication, store the base64-encoded `username:password` pair in a managed secret, then send it in the `Authorization` header:

```plaintext
Authorization: Basic {{secret:CRM_BASIC_AUTH}}
```

For an OAuth access token, store the token in a managed secret and send it as a bearer credential:

```plaintext
Authorization: Bearer {{secret:CRM_OAUTH_TOKEN}}
```

Replace `CRM_API_KEY`, `CRM_BASIC_AUTH`, and `CRM_OAUTH_TOKEN` with the names of managed secrets in your account.

## Handle responses

Runtype returns a JSON response as an object when the response has an `application/json` content type. It returns other response bodies as text. The Agent can then use individual fields from a JSON response, such as `name` and `tier`:

```json
{
  "customerId": "12345",
  "name": "Dana",
  "tier": "premium",
  "orderCount": 47
}
```

## Handle errors

An external Tool fails when one of these conditions occurs:

* The API returns a status outside the 2xx range, such as `400`, `401`, `403`, `429`, or `500`. The error includes the status code and the response body.
* The API does not respond within 30 seconds.
* The API is unreachable.
* The URL uses `http://`, points to a private or local network address, or redirects to one. The error starts with `URL validation failed` or reports the blocked redirect.
* The API redirects more than five times.
* A `{{secret:SECRET_NAME}}` reference names a managed secret that does not exist. The error says that the Tool requires secrets that are not configured.

Inspect the error to identify the failure. Update the request, the parameter values, or your managed secrets, then run the test again.

## Test an external Tool

The test runs the last saved configuration. Test the Tool with these steps:

1. Click **Save**.
2. Click the run button (play icon) next to **Save**, or press Cmd+Enter (macOS) or Ctrl+Enter (Windows and Linux), to open the **Test Tool** panel.
3. On the **Params** tab, enter sample parameter values.
4. Click **Run Test**.
5. Review the returned result on the **Result** tab.

The **History** tab shows up to 10 recent test runs from the current session. It is cleared when you reload the page.

## Create an external Tool from the CLI

You can also create and test external Tools from the `runtype` CLI. A Tool definition has `name`, `description`, `toolType`, `parametersSchema`, and `config` fields. The `config` field holds `url`, `method`, `headers`, and `body`.

The following definition creates a customer lookup Tool:

**`lookup-customer.json`**

```json title="lookup-customer.json"
{
  "name": "lookup_customer",
  "description": "Retrieves customer information by ID",
  "toolType": "external",
  "parametersSchema": {
    "type": "object",
    "properties": {
      "customer_id": { "type": "string", "description": "Customer ID to look up" }
    },
    "required": ["customer_id"]
  },
  "config": {
    "url": "https://example.com/customers/{{customer_id}}",
    "method": "GET",
    "headers": { "Authorization": "Bearer {{secret:CRM_API_KEY}}" }
  }
}
```

Create the Tool from the file:

```bash
runtype tools create --from-file lookup-customer.json
```

The command prints the ID of the Tool that it created. Test the Tool with sample input:

```bash
runtype tools test <tool-id> --input '{"customer_id": "12345"}'
```

Replace `<tool-id>` with the ID that the create command printed. To keep Tool definitions in version control, see [Manage tools as code](/user-guide/tools/manage-tools-as-code).

## Example tools

Use these configurations as starting points for common external Tools. Each secret name, such as `CRM_API_KEY`, is the name of a managed secret in your account.

### Customer lookup

Use a GET Tool to look up a customer by ID:

```plaintext
Name: lookup_customer
Description: Retrieves customer information by ID
Method: GET
URL: https://example.com/customers/{{customer_id}}
Headers:
  Authorization: Bearer {{secret:CRM_API_KEY}}
Parameters:
  - customer_id (string, required): Customer ID to look up
```

### Create a support ticket

Use a POST Tool to create a support ticket:

```plaintext
Name: create_ticket
Description: Creates a support ticket in the ticketing system
Method: POST
URL: https://example.com/tickets
Headers:
  Authorization: Bearer {{secret:TICKETING_TOKEN}}
  Content-Type: application/json
Body:
{
  "subject": {{subject}},
  "description": {{description}},
  "priority": {{priority}},
  "customerId": {{customer_id}}
}
Parameters:
  - subject (string, required): Ticket subject
  - description (string, required): Detailed description
  - priority (string, required): Ticket priority
  - customer_id (string, required): Customer ID
```

### Product search

Use a GET Tool to search a product catalog. The URL has no expressions, so Runtype appends `query` and `limit` to the query string with URL encoding and omits `limit` when the Agent does not send it:

```plaintext
Name: search_products
Description: Searches a product catalog by query
Method: GET
URL: https://example.com/products/search
Headers:
  X-API-Key: {{secret:STORE_KEY}}
Parameters:
  - query (string, required): Search query
  - limit (number): Maximum number of results
```

## Best practices

Apply these practices when you configure an external Tool:

* Write a clear description that explains when to use the Tool.
* Document rate limits in the Tool description.
* Describe side effects when the Tool creates or modifies data.
* Test the Tool with credentials that can call the target API before you attach it to an Agent.
* Use managed secrets instead of hardcoding credentials in the Tool configuration.

## Next steps

Continue with one of these guides:

* [Agent tools](/user-guide/agents/agent-tools): attach Tools to Agents and configure tool use
* [Runtime tools](/user-guide/tools/runtime-tools): define external Tools inline in a request, hide parameters from the model, and ask a person for input before a call
* [Manage tools as code](/user-guide/tools/manage-tools-as-code): keep Tool definitions in version control
* [Managing secrets](/user-guide/settings/managing-secrets): create and manage secret references
* [Creating custom tools](/user-guide/tools/creating-custom-tools): run custom JavaScript or Python tools
* [What are Tools?](/user-guide/tools/what-are-tools): compare built-in, custom, and external Tools
* [Designing tools for agents](/user-guide/tools/designing-tools-for-agents): write tool names, descriptions, and parameters that Agents use well