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

# Tools

> The fixed registry of actions Autopilot agents can take

## Why a fixed registry?

Every step in a [playbook](/autopilot/playbooks) is a tool call. The set of available tools is fixed and small. New tools don't appear because an agent decided they should. The registry is a closed vocabulary, the same way [triggers](/autopilot/triggers) are.

This matters for three reasons:

* **Auditable.** Every action in the [agent action log](/autopilot/agent-action-log) maps to a known tool with a known signature.
* **Predictable.** No tool can do something it wasn't designed to do.
* **Governable.** Each tool has a [risk tier](/autopilot/risk-tiers). The runtime enforces it.

If you've used MCP-style tool-use with the Anthropic SDK, the model is familiar. The difference is that here the registry is curated for internal-app autonomy, not general-purpose agency.

## The registry

| Tool                 | Reads    | Writes              | Default risk tier                 |
| -------------------- | -------- | ------------------- | --------------------------------- |
| `query_collection`   | App data | No                  | Low                               |
| `update_records`     | App data | App data            | Medium                            |
| `send_email`         | None     | External            | High (draft-and-approve)          |
| `send_slack`         | None     | External            | High (draft-and-approve)          |
| `send_sms`           | None     | External            | High (draft-and-approve)          |
| `notify_user`        | None     | In-app notification | Low                               |
| `call_external`      | None     | External HTTP       | High (draft-and-approve)          |
| `ask_human`          | None     | Pauses playbook     | Low                               |
| `create_task`        | None     | App data            | Low                               |
| `draft_for_approval` | None     | Autopilot inbox     | Low (drafts always land in inbox) |

Each tool has a strict signature. A playbook calls a tool with typed arguments; the runtime validates them before the call runs.

***

### `query_collection`

Read-only, schema-aware queries against your app data.

```yaml theme={null}
- tool: query_collection
  collection: deals
  filter: "stage = 'proposal' AND amount > 10000"
  sort: "amount desc"
  limit: 50
  fields: [id, name, amount, owner, contact_id]
```

**Use when:** You need to fetch records to feed a downstream step.

**Risk tier:** Low. Reads only. Honors agent [scopes](/autopilot/scopes).

***

### `update_records`

Write field values back to a collection.

```yaml theme={null}
- tool: update_records
  collection: deals
  filter: "id = {{ trigger.record.id }}"
  set:
    last_nudged_at: "{{ now }}"
    nudge_count: "{{ record.nudge_count + 1 }}"
```

**Use when:** You need to mark records as processed, advance state, or annotate.

**Risk tier:** Medium. Writes to app data, no external surface. Bounded by guardrails (`max_records_per_call`, scope).

***

### `send_email`

Outbound email through the configured provider.

```yaml theme={null}
- tool: send_email
  to: "{{ contact.email }}"
  from: "sales@{{ org.domain }}"
  subject: "Following up on {{ deal.name }}"
  body: "{{ rendered_template }}"
  template_id: stalled_deal_followup
```

**Use when:** You want the agent to email someone.

**Risk tier:** High. Defaults to `draft_for_approval`. Graduate to `auto_with_undo` only after a clean approval history. See [risk tiers](/autopilot/risk-tiers).

***

### `send_slack`

Outbound Slack message to a user, channel, or thread.

```yaml theme={null}
- tool: send_slack
  channel: "#sales-ops"
  text: "Pipeline summary for {{ today }}"
  blocks: "{{ rendered_blocks }}"
```

**Use when:** You want the agent to post to Slack.

**Risk tier:** High. Defaults to draft-and-approve. Internal channels can be graduated faster than external-customer DMs.

***

### `send_sms`

Outbound SMS through the configured provider.

```yaml theme={null}
- tool: send_sms
  to: "{{ contact.phone }}"
  body: "{{ short_message }}"
```

**Use when:** Time-sensitive notifications where email or in-app isn't enough.

**Risk tier:** High. SMS has the highest cost-of-mistake of any messaging tool. Always draft-and-approve unless the customer has explicitly opted in to automated SMS.

***

### `notify_user`

In-app notification. Renders in the user's notification panel and the [Autopilot](/autopilot/inbox) ambient strip.

```yaml theme={null}
- tool: notify_user
  user_id: "{{ deal.owner }}"
  title: "Deal moved to lost"
  body: "{{ deal.name }} ({{ deal.amount }}) was just marked lost."
  link: "/deals/{{ deal.id }}"
```

**Use when:** The user needs to know something but doesn't need a draft to approve.

**Risk tier:** Low. Internal only. Always live (no draft-and-approve overhead).

***

### `call_external`

Generic outbound HTTP call. The escape hatch for tools the registry doesn't cover yet.

```yaml theme={null}
- tool: call_external
  method: POST
  url: "https://api.example.com/webhook"
  headers:
    Authorization: "Bearer {{ secrets.example_api_key }}"
  body:
    deal_id: "{{ deal.id }}"
    amount: "{{ deal.amount }}"
```

**Use when:** You need to integrate with a system that isn't covered by a dedicated tool.

**Risk tier:** High. The runtime can't know what an external endpoint does. Always draft-and-approve until you've validated the call's effect.

***

### `ask_human`

Pause the playbook and ask a question. The question lands in the configured user's [Autopilot inbox](/autopilot/inbox) or [Copilot](/copilots/overview). The playbook resumes when they answer.

```yaml theme={null}
- tool: ask_human
  user_id: "{{ deal.owner }}"
  question: "Should I escalate {{ deal.name }} to procurement?"
  options: [yes, no, skip]
  timeout: "24h"
```

**Use when:** The agent needs a decision it can't make on its own.

**Risk tier:** Low. The action is "ask a question." The follow-up tool calls inherit their own tiers.

***

### `create_task`

Surface a TODO item in a tasks collection. Use this when the right action is "a human should do something" rather than "the agent should do something."

```yaml theme={null}
- tool: create_task
  title: "Review {{ deal.name }} terms"
  description: "Deal moved to legal review. Standard MSA in place?"
  assignee: "{{ deal.owner }}"
  due_date: "{{ now + 2_days }}"
```

**Use when:** The right next action is human work, not agent work.

**Risk tier:** Low. Writes a record, doesn't reach outside the app.

***

### `draft_for_approval`

The default tool for outbound work. Composes a draft (email, Slack, SMS, external call) and lands it in the configured user's [Autopilot inbox](/autopilot/inbox). The user approves, edits, or skips. Only on approval does the underlying outbound tool run.

```yaml theme={null}
- tool: draft_for_approval
  underlying: send_email
  owner: "{{ deal.owner }}"
  payload:
    to: "{{ contact.email }}"
    subject: "Following up on {{ deal.name }}"
    body: "{{ rendered_template }}"
  reasoning: "{{ agent.reasoning }}"
```

**Use when:** Almost always, for any outbound tool. This is the default in every Gainable app and the reason agents are safe by default.

**Risk tier:** Low. Drafts never reach a recipient on their own.

***

## Tool-tier interaction

The risk tier on a tool is the *default*. A playbook's [risk tier](/autopilot/risk-tiers) can constrain it further, but never relax it. A tool that defaults to high stays high until the org explicitly graduates it.

A playbook configured at `auto_with_undo` doesn't make `send_email` skip the draft step unless `send_email` has graduated for that org or that recipient class. The runtime is the final word.

## Adding a tool the registry doesn't cover

When you need behavior the registry doesn't have, the right question is usually "is this an outbound integration?"

* **Yes.** Use `call_external`. If it becomes a recurring pattern, it's a candidate for promotion to a first-class tool.
* **No, it's pure data work.** It probably belongs in a derived field or a [build agent](/concepts/agents) step, not in a runtime tool.

We deliberately keep the registry small. Every tool we add is one more thing the action log, the risk tiers, and the Autopilot UI have to know about.

## Best practices

<AccordionGroup>
  <Accordion title="Default to draft_for_approval">
    Outbound tools should almost always be wrapped in `draft_for_approval`. The exceptions are graduated tools that have earned auto status with a clean approval history.
  </Accordion>

  <Accordion title="Use ask_human instead of guessing">
    If the agent doesn't have enough information, `ask_human` is cheaper than a wrong action. The user is in the loop anyway.
  </Accordion>

  <Accordion title="Log enough to debug">
    Every tool call writes to the [action log](/autopilot/agent-action-log) automatically. Don't add side effects that don't show up there.
  </Accordion>

  <Accordion title="Use create_task when the next step isn't agent work">
    A surfaced task in the user's queue is often more useful than a draft message. Don't force every objective into an outbound message.
  </Accordion>
</AccordionGroup>

## Learn more

<CardGroup cols={2}>
  <Card title="Risk tiers" icon="shield-check" href="/autopilot/risk-tiers">
    Draft-and-approve, auto-with-undo, auto
  </Card>

  <Card title="Agent action log" icon="list" href="/autopilot/agent-action-log">
    Every tool call recorded
  </Card>

  <Card title="Playbooks" icon="book-open" href="/autopilot/playbooks">
    How tools fit into a playbook
  </Card>

  <Card title="Connect outbound" icon="plug" href="/autopilot/connect-outbound">
    Slack, SMS, calendar, DocuSign, Stripe, generic webhooks
  </Card>
</CardGroup>
