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

# Approval Backends

> Choose how tool approvals reach a human — console prompt, plan mode, accept-edits, or a chat channel

Pick who (or what) approves a tool call — the terminal, a coding-mode fast path, or a chat channel that fans out to Slack/Telegram/Discord.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Approval Backends"
        Request[📋 User Request] --> Process[⚙️ Approval Backends]
        Process --> Result[✅ Result]
    end

    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff

    class Request input
    class Process process
    class Result output
```

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

agent = Agent(
    name="coder",
    instructions="Edit files carefully.",
    tools=["write_file"],
    approval="console",
)
agent.start("Refactor utils.py")
```

<Note>
  **Using the TypeScript SDK?** The same four Python-parity backends — `AutoApproveBackend`, `ConsoleBackend`, `AgentApproval`, `CallbackBackend` — plus `setApprovalBackend` wiring are documented at [Approval Backends (TypeScript)](/docs/js/approval#approval-backends-python-parity).
</Note>

The user triggers a risky tool; the chosen approval backend prompts or routes the decision to a human.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Approval Backends"
        In[📝 Risky Tool Call] --> Backend[⚙️ Approval Backend]
        Backend --> Human[🧑 Human Decision]
        Human --> Agent[🤖 Agent]
        Agent --> Out[✅ Result]
    end

    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef process fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff

    class In input
    class Backend,Human process
    class Agent agent
    class Out output
```

### Available Backends

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    subgraph "pip install praisonai-code"
        C[console]
        P[plan]
        AE[accept-edits]
        BY[bypass]
        AU[auto]
        AG[agent]
        N[none]
    end

    subgraph "pip install praisonai"
        SL[slack]
        TG[telegram]
        DC[discord]
        WH[webhook]
        HT[http]
        SC[secure]
        PR[presentation]
    end

    classDef standalone fill:#10B981,stroke:#7C90A0,color:#fff
    classDef wrapper fill:#189AB4,stroke:#7C90A0,color:#fff

    class C,P,AE,BY,AU,AG,N standalone
    class SL,TG,DC,WH,HT,SC,PR wrapper
```

<Note>
  **Which class runs the prompt?**

  * `Agent(approval=True)` in Python → `praisonaiagents.approval.ConsoleBackend` (Rich terminal prompt, no permission-mode features).
  * `praisonai --approval console` (also `true`, `Console`, `plan`, `accept-edits`, `bypass`) → `praisonai_code.cli.approval_backend.InteractiveCLIApprovalBackend`, which layers `PermissionMode` (plan / accept-edits / bypass) and declarative permission rules on top.

  Both render the same unified-diff preview for file-mutating tools. The extra CLI-only features (plan mode, accept-edits, bypass, `praisonai permissions` rules) come from `InteractiveCLIApprovalBackend`.

  * On any of these backends, standing registry grants (`PRAISONAI_AUTO_APPROVE`, YAML `approve:`, session grants recorded via `[s]`) are now checked **before** the backend is invoked — no more re-prompting for calls the user already blessed. Fixed in [PR #4878](https://github.com/MervinPraison/PraisonAI/pull/4878). See [Standing grants and attached backends](/docs/features/approval#standing-grants-and-attached-backends).
</Note>

## How It Works

A risky tool call pauses until the chosen backend collects a human decision.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Agent
    participant ApprovalBackend

    User->>Agent: Task with risky tool
    Agent->>ApprovalBackend: Request approval
    ApprovalBackend-->>Agent: Approve / deny
    Agent-->>User: Result or blocked
```

### Diff preview

The `console` backend renders a coloured unified diff for file-mutating tools — `edit_file`, `acp_edit_file`, `write_file`, `acp_create_file`, and `apply_patch` — so the reviewer sees the concrete change, not truncated arguments.

The diff rides on `ApprovalRequest.context["diff"]`, a stable public field. Wrapper backends (`slack`, `telegram`, `discord`, `webhook`, `http`) can read it and render or attach it in their channel — this is where custom-backend authors should look.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Inside a custom backend
def request_approval_sync(self, request, timeout=None):
    diff = request.context.get("diff")
    if diff:
        ...  # render as a code block, attachment, or on-request expand
```

<Note>
  `request_approval_sync` now takes an optional `timeout` kwarg (PR [#5357](https://github.com/MervinPraison/PraisonAI/pull/5357)): `None` waits indefinitely, `> 0` is an explicit per-call limit. Accept it as `timeout=None` so legacy call sites still work, and never mutate a shared `self._timeout`.
</Note>

See [Approval › Diff preview in approval prompts](/docs/features/approval#diff-preview-in-approval-prompts) for the full tool contract and safety rules.

## Backend Matrix

| Backend | Where it runs | One-line meaning |
| - | - | - |
| `console` (also `true`/`yes`/`1`) | Standalone | Ask on the terminal, y/N — shows a rendered diff for file-mutating tools |
| `none` (also `false`/`no`/`0`) | Standalone | Auto-approve everything (unsafe) |
| `auto` | Standalone | Auto-approve safe read-only tools |
| `plan` | Standalone | Coding-mode plan approval |
| `accept-edits` | Standalone | Auto-accept file-edit tools |
| `bypass` | Standalone | Approve without prompting (dev) |
| `agent` | Standalone | Delegate to a reviewer agent (see below) |
| `slack` | Wrapper | Approvals routed to Slack |
| `telegram` | Wrapper | Approvals routed to Telegram |
| `discord` | Wrapper | Approvals routed to Discord |
| `webhook` | Wrapper | Custom outbound HTTP webhook |
| `http` | Wrapper | Inbound HTTP approval endpoint |
| `secure` | Wrapper | Secure-mode policy (audit-logged) |
| `presentation` | Wrapper | Presentation/demo-safe policy |

<Note>
  Wrapper backends (`slack`, `telegram`, `discord`, `webhook`, `http`, `secure`, `presentation`) require `pip install praisonai`.
</Note>

### CLI opt-outs register a registry backend

`praisonai code --no-safe` and `--dangerously-skip-approval` do two things, not one:

* **Set env vars** — `PRAISON_APPROVAL_MODE=auto`, `PRAISONAI_TOOL_SAFETY=off` — consumed by the CLI's own tool wiring.
* **Register `AutoApproveBackend`** on the approval registry — consumed by the core `@require_approval` decorator that gates critical tools like `acp_execute_command`. Without this the flag was a no-op for any registry-decorated tool.

A safe-mode run *removes* the `AutoApproveBackend` a prior `--no-safe` installed in the same process, and removes **only** that backend — a caller-supplied backend or one installed by `--plan` is preserved. A per-agent backend still wins, since the core consults `Agent(approval=…)` before the global registry. See [Approval → Bypassing safety](/docs/features/approval#bypassing-safety) for the full resolution order.

***

## Quick Start

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

agent = Agent(
    name="Code assistant",
    instructions="Help refactor code files.",
)
agent.start("Refactor utils.py")
```

<Steps>
  <Step title="Choose your approval mode">
    <Tabs>
      <Tab title="Terminal">
        Ask the user on the terminal before each risky tool call:

        ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
        praisonai-code run --approval console "Refactor utils.py"
        ```
      </Tab>

      <Tab title="Coding fast path">
        Use plan/accept-edits mode (Claude Code-style flow) for unattended coding runs:

        ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
        praisonai-code run --approval plan "Refactor utils.py"
        ```

        `accept-edits` auto-accepts file edits; `plan` requires explicit plan approval before execution.
      </Tab>

      <Tab title="Chat channel (wrapper)">
        Route approvals to a Slack channel:

        ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
        praisonai run --approval slack --approval-timeout 300 "Refactor utils.py"
        ```

        <Note>
          Channel backends (`slack`, `telegram`, `discord`, `webhook`, `http`, `secure`, `presentation`) require `pip install praisonai`.
        </Note>
      </Tab>
    </Tabs>
  </Step>
</Steps>

***

## `--approval-timeout`

`--approval-timeout` takes seconds. Pass `none` to wait indefinitely.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai-code run --approval console --approval-timeout 60 "..."
praisonai-code run --approval slack --approval-timeout none "..."
```

<Note>
  The CLI timeout is passed **per call** as the `timeout` kwarg on `request_approval_sync` (PR [#5357](https://github.com/MervinPraison/PraisonAI/pull/5357)) rather than mutated onto a shared backend, so one backend instance can be shared across agents safely.
</Note>

***

## Reviewer-Agent Mode (`--approval agent`)

When you pass `--approval agent`, a built-in LLM reviewer gates every tool call. The default reviewer instructions frame the tool name and arguments as UNTRUSTED input from a possibly-compromised agent, forbid obeying any directive that appears inside, and offer three verdicts.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai-code run --approval agent "List all files and summarise the project"
```

The reviewer replies with one of `APPROVE`, `DENY`, or `ESCALATE` for each pending tool call. Parsing is fail-closed: empty, ambiguous, mixed, or negated ("DO NOT APPROVE") responses deny; `ESCALATE` defers the call to a human. You can override the default instruction by passing a custom reviewer prompt via the API:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent
from praisonaiagents.approval import AgentApproval

reviewer = Agent(
    name="strict-reviewer",
    instructions="Only approve file reads. Deny everything else. Reply APPROVE, DENY, or ESCALATE if uncertain.",
)

agent = Agent(
    name="Assistant",
    instructions="Help with coding tasks.",
    approval=AgentApproval(approver_agent=reviewer),
)
agent.start("Read and summarise main.py")
```

<Note>
  Supplying your own `approver_agent` opts you out of the hardened defaults — you are unaffected unless you adopt the new instructions and tri-state verdict yourself. The default reviewer (auto-created when none is passed) gets the hardened instructions. See [AgentApproval Hardening](/docs/features/approval-agent-hardening).
</Note>

***

## Unknown-Backend Error

If you pass an unrecognised backend name, the CLI raises:

```
Unknown approval backend: '<value>'. Valid options: console, plan, accept-edits, bypass, auto, agent, none, discord, http, presentation, secure, slack, telegram, webhook
```

Use this to trap typos — the valid list is alphabetically sorted within the wrapper group.

***

## Best Practices

<AccordionGroup>
  <Accordion title="Use console in interactive dev, agent for unattended runs">
    Use `console` in interactive dev, `agent` for unattended runs where a reviewer LLM can gate tools.
  </Accordion>

  <Accordion title="Pair accept-edits and plan with praisonai-code code">
    `accept-edits` and `plan` are the coding-mode fast paths — pair them with `praisonai-code code`.
  </Accordion>

  <Accordion title="Never use none outside throwaway sandboxes">
    `none` disables approval entirely; only use it in throwaway sandboxes.
  </Accordion>
</AccordionGroup>

***

## Related

<CardGroup cols={2}>
  <Card title="TypeScript Approval Backends" icon="js" href="/docs/js/approval#approval-backends-python-parity">
    The same backends for the TypeScript SDK.
  </Card>

  <Card title="Approval Protocol" icon="shield-check" href="/docs/features/approval-protocol">
    Cross-SDK backend matrix and request/decision shapes.
  </Card>

  <Card title="Local Tools Loading" icon="wrench" href="/docs/features/local-tools-loading">
    Approval decides who says yes to your local tools.
  </Card>

  <Card title="Approval" icon="shield" href="/docs/features/approval">
    The full approval system — dangerous tool gating, TTY detection, and safe defaults.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.