Which actions require human sign-off: agent hiring, strategic pivots, agent termination, budget increases. The approvals table schema and how pending approvals block execution.
The four default approval triggers, the approvals table schema, and how to configure custom approval policies for your organization.
Approval gates are moments where autonomous agent action is paused and a human (or higher-authority agent) must sign off before execution continues. They are the safety mechanism that lets you run autonomous agents while keeping critical decisions under human control.
Paperclip ships with four built-in approval triggers that cannot be bypassed:
| Trigger | What requires approval |
|---|---|
hire_agent |
Creating a new agent in the company |
terminate_agent |
Setting an agent status to terminated |
budget_increase |
Raising monthly_budget_usd above current value |
strategy_change |
Modifying core company settings (webhook URLs, heartbeat schedule) |
These protect against runaway agents attempting to self-modify the company configuration.
approvals TableCREATE TABLE public.approvals (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
company_id uuid NOT NULL REFERENCES companies(id),
trigger text NOT NULL,
status text NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending','approved','rejected','expired')),
requesting_agent_id uuid REFERENCES agents(id),
reviewer_agent_id uuid REFERENCES agents(id),
human_reviewer_email text,
details jsonb, -- what is being approved
decision_note text, -- reviewer's comment
created_at timestamptz NOT NULL DEFAULT now(),
expires_at timestamptz,
decided_at timestamptz
);
In Company Settings → Approval Policies → Add Policy:
{
"trigger": "publish_content",
"condition": {
"field": "word_count",
"operator": "gt",
"value": 1000
},
"required_approver": "human_editor",
"expiry_hours": 48,
"on_expire": "reject"
}
When a Writer agent finishes a 1200-word article, Paperclip:
1. Sets the issue to status = 'escalated' (not resolved)
2. Creates an approvals row with trigger = 'publish_content'
3. Notifies human_editor via webhook/email
4. If no decision within 48 hours, auto-rejects
SELECT
ap.id,
ap.trigger,
ap.status,
req.name AS requested_by,
ap.details,
ap.created_at,
ap.expires_at
FROM approvals ap
LEFT JOIN agents req ON req.id = ap.requesting_agent_id
WHERE ap.company_id = 'your-company-id'
AND ap.status = 'pending'
ORDER BY ap.created_at ASC;
# Approve
curl -X POST http://localhost:3100/api/companies/${COMPANY_ID}/approvals/${APPROVAL_ID}/approve \
-d '{"note": "Looks good, content quality is acceptable"}'
# Reject
curl -X POST http://localhost:3100/api/companies/${COMPANY_ID}/approvals/${APPROVAL_ID}/reject \
-d '{"note": "Title is misleading, needs rework"}'
On approval: the original action proceeds (issue resolved, agent hired, budget raised).
On rejection: the action is cancelled and the requesting agent receives the rejection note.