# Call Tools

> Make HTTP API calls as agent tools. Call tools let agents interact with REST APIs directly — the agent sends a request and gets the response back.

**URL:** https://inference.sh/docs/api/agent/call-tools
**Last updated:** 2026-10-18

---

Make HTTP API calls as agent tools. Call tools let agents interact with REST APIs directly — the agent sends a request and gets the response back.

---

## Basic Usage

Call tools are defined in the agent's YAML config:

```yaml
tools:
  - name: get_weather
    type: call
    description: Get current weather for a city
    call:
      method: GET
      url: https://api.weather.com/v1/current?city={{args.city}}
      input_schema:
        type: object
        properties:
          city:
            type: string
            description: City name
```

When the agent calls `get_weather(city="London")`, the runtime makes a GET request to `https://api.weather.com/v1/current?city=London` and returns the response body as the tool result.

---

## How It Works

1. Agent decides to call the tool based on the conversation
2. Runtime substitutes `{{context.X}}` in the call's arguments, then resolves the URL template (`{{context.X}}`, `{{args.X}}`) and `{{context.X}}` in headers
3. Runtime injects auth credentials (headers, bearer token)
4. Request is sent and response is returned to the agent

The agent never sees credentials — auth is handled entirely by the runtime.

---

## URL Templates

URLs support two kinds of template variables:

| Syntax | Source | Resolved when |
|--------|--------|---------------|
| `{{context.X}}` | Per-chat context values (set at chat creation) | Each tool call |
| `{{args.X}}` | Tool call arguments | Each tool call |

`{{args.X}}` values are consumed from the request body — they appear in the URL but are removed from the JSON body sent to the endpoint.

```yaml
url: https://api.example.com/projects/{{context.project_id}}/items/{{args.item_id}}
```

---

## Authentication

Configure auth in the tool's `call.auth` section. Credentials come from your workspace's secrets (`belt secrets set`).

### Bearer Token

```yaml
call:
  auth:
    type: bearer
    secret: my_api_key    # workspace secret name
  method: GET
  url: https://api.example.com/data
```

Sends `Authorization: Bearer <secret_value>` on every request.

### API Key Header

```yaml
call:
  auth:
    type: api_key
    secret: my_api_key
    header: X-API-Key     # custom header name
  method: POST
  url: https://api.example.com/action
```

### Credential (OAuth)

```yaml
call:
  auth:
    type: credential
    provider: google
  method: GET
  url: https://www.googleapis.com/calendar/v3/calendars/primary/events
```

Uses the runner's own connected credential for that provider. With none, the chat pauses and asks them to connect one.

### No Auth

```yaml
call:
  auth:
    type: none
  method: GET
  url: https://api.example.com/public
```

Omitting `call.auth` entirely, or leaving `auth.type` empty, sends no credentials — the same behavior as `type: none`. Generated SDK types expose this as `ToolAuthTypeNone` / `ToolAuthType.NONE`.

---

## Input Schema

Define the tool's parameters with `input_schema`. This becomes the JSON Schema the LLM sees:

```yaml
call:
  method: POST
  url: https://api.example.com/search
  input_schema:
    type: object
    properties:
      query:
        type: string
        description: Search query
      limit:
        type: integer
        description: Max results to return
      filters:
        type: object
        description: Key-value filter pairs
```

For `POST`/`PUT` requests, the input schema fields become the JSON request body (minus any `{{args.X}}` values consumed by the URL).

For `GET`/`DELETE` requests, parameters can be embedded in the URL via `{{args.X}}` templates.

---

## Require Approval

For destructive or expensive operations:

```yaml
tools:
  - name: delete_record
    type: call
    description: Permanently delete a record
    require_approval: true
    call:
      auth:
        type: bearer
        secret: admin_key
      method: DELETE
      url: https://api.example.com/records/{{args.id}}
      input_schema:
        type: object
        properties:
          id:
            type: string
            description: Record ID to delete
```

The agent pauses and waits for user confirmation before executing.

---

## Context

Call tools work with agent context — per-chat variables declared in the agent config and passed when creating a chat.

```yaml
# Agent config
context:
  - name: project_id
    description: Project to operate on
    required: true

tools:
  - name: list_tasks
    type: call
    description: List tasks in the project
    call:
      auth:
        type: bearer
        secret: pm_api_key
      method: GET
      url: https://api.example.com/projects/{{context.project_id}}/tasks
```

Pass context when running the agent:

```bash
belt agent run myteam/pm-agent "list open tasks" \
  --context project_id=proj_123
```

---

## Full Example

A pricing configuration agent with multiple call tools:

```yaml
name: pricing-agent
description: Configure pricing for store apps
core_app:
  ref: openrouter/claude-sonnet-45

context:
  - name: version_id
    description: Store version ID
    required: true

tools:
  - name: get_pricing
    type: call
    description: Returns current pricing state
    call:
      auth:
        type: bearer
        secret: api_key
      method: GET
      url: https://api.example.com/versions/{{context.version_id}}/pricing

  - name: save_draft
    type: call
    description: Save pricing config as draft
    call:
      auth:
        type: bearer
        secret: api_key
      method: PUT
      url: https://api.example.com/versions/{{context.version_id}}/pricing/draft
      input_schema:
        type: object
        properties:
          prices:
            type: object
            description: Price variables (name to value in cents)
          total_expression:
            type: string
            description: CEL expression for total fee

  - name: publish
    type: call
    description: Promote draft to live pricing
    require_approval: true
    call:
      auth:
        type: bearer
        secret: api_key
      method: POST
      url: https://api.example.com/versions/{{context.version_id}}/pricing/publish
```
