Your AI agent generates a quarterly financial report. Before it emails the board, a human needs to review it. Simple requirement.
Here's what you actually have to build.
The DIY Approach
import secrets
import smtplib
from apscheduler.schedulers.background import BackgroundScheduler
def request_approval(reviewer_email, context, agent_id):
# 1. Generate approval token
token = secrets.token_urlsafe(32)
db.insert("approvals", token=token, agent_id=agent_id,
status="pending", created_at=datetime.utcnow())
# 2. Send notification
send_slack_message(reviewer_email,
f"Approval needed: {context}\n"
f"Approve: https://your-app.com/approve/{token}\n"
f"Reject: https://your-app.com/reject/{token}")
# 3. Schedule reminder (5 min)
scheduler.add_job(send_reminder, 'date',
run_date=datetime.utcnow() + timedelta(minutes=5),
args=[token, reviewer_email])
# 4. Schedule escalation (30 min)
scheduler.add_job(escalate_to_backup, 'date',
run_date=datetime.utcnow() + timedelta(minutes=30),
args=[token, get_backup_reviewer(reviewer_email)])
# 5. Schedule timeout (8 hours)
scheduler.add_job(handle_timeout, 'date',
run_date=datetime.utcnow() + timedelta(hours=8),
args=[token])
return token
# Plus you need:
# - Webhook endpoint for approve/reject callbacks
# - Token validation and expiry
# - Polling loop or callback for the agent to resume
# - Audit logging (who approved, when, what context)
# - DB cleanup for expired tokens
# - Error handling for failed notifications
# - Unit tests for all of the above
That's about 200 lines before error handling. You also need a web server for the webhook, a scheduler process that stays alive, and a database for approval state.
All you wanted was "pause and wait for a human."
The 4-Line Version
from axme import AxmeClient, AxmeClientConfig
client = AxmeClient(AxmeClientConfig(api_key=os.environ["AXME_API_KEY"]))
intent_id = client.send_intent({
"intent_type": "intent.report.review_approval.v1",
"to_agent": "agent://myorg/production/report-generator",
"payload": {
"report": "Q1 Financial Summary",
"pii_detected": False,
"reviewer": "cfo@company.com",
},
})
result = client.wait_for(intent_id)
The platform handles:
- Notification - Slack, email, CLI. Reviewer gets notified immediately.
- Reminders - Configurable intervals. Default: 5 min, then 30 min.
- Escalation - Reviewer A does not respond? Escalate to reviewer B, then to the team.
- Timeout - Graceful timeout with configurable fallback action.
- Audit trail - Who approved, when, with what context. Stored durably.
- Durable state - Agent crashes? Restarts? The approval state is in PostgreSQL, not in process memory.
What Matters in Production
The demo version of human approval is always simple. input("Approve? y/n"). The production version is where things break.
| Production concern | DIY | AXME |
|---|---|---|
| Human is on vacation | Build escalation chain | Built-in |
| Agent crashes while waiting | Lost approval state | Durable in DB |
| Two approvals needed | Build chaining logic | Approval chains |
| Audit for compliance | Custom logging | Built-in event log |
| Reminder if no response in 5 min | Scheduler + cron job | Built-in |
| Mobile-friendly approval | Build a UI | Slack/email/CLI |
Works With Any Framework
This is not framework-specific. Your agent can be built with LangGraph, CrewAI, AutoGen, OpenAI Agents SDK, Google ADK, or raw Python. The approval layer sits outside your agent code.
The agent framework handles reasoning. The coordination layer handles waiting for humans.
Try It
Working example - agent generates a report, pauses for human review, resumes after approval:
github.com/AxmeAI/async-human-approval-for-ai-agents
Built with AXME - human approval for AI agent workflows, built in. Alpha - feedback welcome.
Top comments (0)