Creating pending approvals programmatically, the email/webhook notification on approval creation, the approve/reject REST endpoints, and the timeout-and-escalate pattern.
Design end-to-end approval workflows, chain approvals through multiple reviewers, and implement time-sensitive approval escalation.
The simplest case: agent creates work → human reviews → approved/rejected.
Writer Agent
→ resolves issue with draft
→ Paperclip creates approval (trigger: 'publish_content')
→ Human Editor notified via webhook
→ Human approves in Approvals UI
→ Issue status changes to 'verified'
→ Content published
For sensitive decisions, require two approvers in sequence:
{
"trigger": "large_expenditure",
"condition": { "field": "amount_usd", "operator": "gt", "value": 500 },
"approvers": [
{ "level": 1, "role": "department_manager", "expiry_hours": 24 },
{ "level": 2, "role": "cfo", "expiry_hours": 48 }
]
}
Level 2 approval request is only created after Level 1 is approved. If Level 1 rejects, Level 2 is never created.
For content that any editor can approve:
{
"trigger": "publish_content",
"approvers_any_of": ["editor_alice", "editor_bob", "editor_carol"],
"expiry_hours": 24
}
First approver to click wins; others' pending approvals are cancelled.
-- Summary for the approval dashboard widget
SELECT
COUNT(*) FILTER (WHERE status = 'pending') AS pending,
COUNT(*) FILTER (WHERE status = 'approved') AS approved_today,
COUNT(*) FILTER (WHERE status = 'rejected') AS rejected_today,
COUNT(*) FILTER (WHERE status = 'expired') AS expired_today
FROM approvals
WHERE company_id = 'your-company-id'
AND created_at >= date_trunc('day', now());
Configure what happens when an approval expires without a decision:
on_expire |
Behaviour |
|---|---|
reject |
Issue cancelled, agent notified |
approve |
Issue auto-approved (use carefully!) |
escalate |
Re-create approval request for next-level approver |
notify |
Only send a reminder notification |
Integrate with Slack, email, or any tool:
{
"webhook_url": "https://hooks.slack.com/services/...",
"events": ["approval.created"],
"message_template": "🔔 Approval needed: {{details.title}}\n{{approve_url}}\n{{reject_url}}"
}
The approve_url and reject_url are pre-signed URLs valid for expiry_hours hours.
on_expire behaviours