---
title: "Approvals: tool calls that wait for a person"
url: "https://conecto.chat/docs/approvals"
markdown: "https://conecto.chat/docs/approvals.md"
description: "Approval rules hold risky tool calls until a person approves them, and notify_request_approval asks a person before an agent acts."
section: "Concepts"
published: 2026-10-19
updated: 2026-10-19
---

# Approvals

Some calls should wait for a person, like a big refund or a change to the company's HubSpot. An approval rule holds them, and the agent goes on once someone says yes.

## How a held call goes

1. The agent calls a tool that a rule holds. The call answers `approval_required` with status 409 and costs nothing.
2. The request goes to the approver as a one-time page, by email or through a saved channel like Slack.
3. The approver picks Approve or Decline on that page, with a comment if they like.
4. The agent gets the event `notify.answered` with the answer. `notify_get_approval` shows it too.
5. On Approve, the agent calls the tool again with the same arguments and the approval. That one call runs.

The held call's reply, 409:

```json
{
  "error": "approval_required",
  "approval_id": "3e9a6f12-5c7b-4d81-9f0e-2a6b8c4d1e57",
  "approver": "dana@halversoncycle.example",
  "message": "A person must approve this first: the request went to dana@halversoncycle.example. When notify.answered arrives (or notify_get_approval says Approve), call again with the same arguments and the approval (header X-Conecto-Approval, or _meta.approvalId over MCP). Nothing was charged.",
  "cost_usd": "$0.00"
}
```

The approval goes in the `X-Conecto-Approval` header over REST, or in `_meta.approvalId` over MCP. It fits the exact call it was made for: the same tool with the same arguments. Calling again with the same arguments while the request waits reuses that request, so the approver gets one.

## Approval rules

`account_add_approval_rule` makes a tool wait for a person, for $0.001.

| Field | What it does |
| --- | --- |
| `tool` | The tool to hold. |
| `argument` and `over` | Hold only the calls whose argument is over a number, like `invoices_create_credit_note` when `amount` is over 500. |
| `argument` and `values` | Hold only the calls whose argument is one of some values, like `connect_call` when `method` is POST, PUT, PATCH or DELETE. |
| `approver_email` | A person who approves. |
| `approver_channel` | A saved notify channel that gets the request. |
| `approver_team` and `approvers` | A team that approves, through its `managers` or `everyone` in it. |
| `two_people_over` | Calls whose argument is over this need two different people. |

With no `argument`, every call of the tool waits. `account_list_approval_rules` lists the rules. Bots add rules and tighten them. On an account with an owner email, removing a rule is the owner's, on the account page.

## Who approves

- The person a call is for never approves it: the person an assistant acts as, or the person a bot answers to, its key's `accountable`. In a one-person business, with nobody else on the account, the owner approves their own.
- Calls over `two_people_over` need two different people to approve. One Decline declines it.
- Someone away passes their approvals to their delegate.
- When too few people are left, the owner steps in, then the admins and the billing people. With nobody at all, the call answers `no_approver`.
- Each approver gets a link of their own, so the page knows who answered. A channel's link is shared, so a call with a known requester goes to people by email.

## Holds with no rule

Three holds need no rule:

- A supplier's new or changed bank details, in `purchasing_save_supplier`, always wait for someone else.
- Paying a person's expense claims back to an IBAN never paid before, in `books_repay_claims`, waits the same way.
- Every change from a key or a team set to drafts only waits for its team's managers, or the owner. Reads go through.

## Holds made when a system is connected

On an account with an owner email, connecting some of the company's systems makes rules of its own, with the owner as the approver, unless the tool has a rule already. The request shows what the call would change.

| Connecting with | Waits for the owner | Which calls |
| --- | --- | --- |
| `connect_add` | `connect_set_rules` | Every call |
| `connect_add` | `connect_call` | When `method` is `POST`, `PUT`, `PATCH` or `DELETE` |
| `google_connect` | `google_calendar_cancel` | Every call |
| `google_connect` | `google_drive_upload` | When `if_exists` is `REPLACE` |
| `hubspot_connect` | `hubspot_update` | Every call |
| `m365_connect` | `m365_calendar_cancel` | Every call |
| `m365_connect` | `m365_files_upload` | When `if_exists` is `REPLACE` |
| `salesforce_connect` | `salesforce_update` | Every call |
| `directory_connect` | `directory_suspend_user` | Every call |

[Your own systems](https://conecto.chat/docs/your-systems.md) covers each connection.

## Asking a person yourself

`notify_request_approval` asks a person before the agent acts, with no rule needed: approve or decline, or one of up to 5 options, with an optional comment. It goes through a saved channel or by email as a one-time page, and costs $0.01 a request, $0.02 more by email, up to $0.03. The answer is free.

notify_request_approval, the arguments:

```json
{
  "question": "Refund order 1042 in full ($849)?",
  "details": "The buyer says the Tabor arrived with a bent derailleur hanger and sent two photos.",
  "email": "dana@halversoncycle.example",
  "reference": "order-1042"
}
```

- The first answer reaches the agent as the event `notify.answered`, with the option picked and the comment.
- A request lapses after `expires_in_hours`: 72 hours unless set, 720 at most.
- Anyone holding the link can answer, so send it to the person who decides and nobody else.
- Email follows the account's rules: on free credit, it reaches only the owner.

## More in the docs

- Previous: [Calling tools](https://conecto.chat/docs/calling-tools.md)
- Next: [Events and webhooks](https://conecto.chat/docs/events.md)
- Every page in one file: https://conecto.chat/docs/all.md
- Every page's title and address: https://conecto.chat/docs/index.json
