Skip to main content

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.

Why a fixed registry?

Every step in a playbook 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 are. This matters for three reasons:
  • Auditable. Every action in the 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. 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

ToolReadsWritesDefault risk tier
query_collectionApp dataNoLow
update_recordsApp dataApp dataMedium
send_emailNoneExternalHigh (draft-and-approve)
send_slackNoneExternalHigh (draft-and-approve)
send_smsNoneExternalHigh (draft-and-approve)
notify_userNoneIn-app notificationLow
call_externalNoneExternal HTTPHigh (draft-and-approve)
ask_humanNonePauses playbookLow
create_taskNoneApp dataLow
draft_for_approvalNoneAutopilot inboxLow (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.
- 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.

update_records

Write field values back to a collection.
- 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.
- 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.

send_slack

Outbound Slack message to a user, channel, or thread.
- 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.
- 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 ambient strip.
- 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.
- 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 or Copilot. The playbook resumes when they answer.
- 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.”
- 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. The user approves, edits, or skips. Only on approval does the underlying outbound tool run.
- 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 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 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

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.
If the agent doesn’t have enough information, ask_human is cheaper than a wrong action. The user is in the loop anyway.
Every tool call writes to the action log automatically. Don’t add side effects that don’t show up there.
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.

Learn more

Risk tiers

Draft-and-approve, auto-with-undo, auto

Agent action log

Every tool call recorded

Playbooks

How tools fit into a playbook

Connect outbound

Slack, SMS, calendar, DocuSign, Stripe, generic webhooks