Skip to main content
Approval enables a “User Triggers, Admin Authorizes” workflow. When an agent hits a protected tool during a workflow run, the run pauses and persists a pending record to your database. Execution only resumes once an admin approves or rejects the request. Approvals are built on HITL primitives (requires_confirmation, requires_user_input, or external_execution). Your tool must implement at least one.

Quick start

from agno.approval import approval
from agno.tools import tool
from agno.db.sqlite import SqliteDb
from agno.agent import Agent

@approval
@tool(requires_confirmation=True)
def delete_user_data(user_id: str) -> str:
    """Permanently delete all data for a user. Requires admin approval."""
    return f"All data for user {user_id} has been deleted."

db = SqliteDb(db_file="app.db", approvals_table="approvals")
agent = Agent(model=..., tools=[delete_user_data], db=db)
When the user asks for something that uses this tool, the run pauses and a pending approval is written to the database. An admin resolves it; then you continue the run.

Approval Types

TypeBehaviorUse Case
@approval(type="required") or @approval“Blocking: Run pauses until an admin reviews and resolves the database record.Critical actions such as deletion, payments, bulk emails.
@approval(type="audit")Non-blocking: Run continues immediately after the HITL interaction is resolved and an audit log is created.Compliance and activity auditing purposes.

Blocking

By default, @approval needs HITL approval and requires_confirmation=True is set.

Non-blocking

To enable an audit-style (non-blocking) Human-in-the-Loop flow for persistent audit trails, use @approval(type="audit"). This will create an audit log after the HITL interaction is resolved. You can use the @tool decorator with log_approval=True to explicitly signal that this tool’s execution should be logged in the HITL audit system. See User Confirmation for details.

Execution Flow

There are three distinct phases in the approval flow:
  • The Pause: When a user triggers an @approval tool, the SDK automatically pauses the run and inserts a pending record into your database.
  • Admin Approval: Admin views the list of pending requests. Then update the record status via the DB provider. Use expected_status="pending" to prevent race conditions.
    db.update_approval(
        approval_id,
        expected_status="pending",
        status="approved",   # or "rejected"
        resolved_by="admin_user_id",
        resolved_at=int(time.time()),
        # For requires_user_input or external_execution: pass resolution_data
        # (e.g. values for user input, result for external execution); SDK applies it on continue_run.
    )
    
  • Resuming the Run: Continue the run using the run_id. The SDK verifies the resolution before proceeding. If the record is missing or still pending, continue_run raises a RuntimeError.
    run = agent.continue_run(run_id=run.run_id, requirements=run.requirements)
    

Examples

Developer Resources