Agent Approval and Governance Workflow in Paperclip: A Complete Technical Guide
Paperclip enforces a board-level governance layer that requires explicit approvals before any agent can be hired, modified, or granted elevated capabilities, with all decisions tracked through immutable audit trails.
The agent approval and governance workflow in paperclipai/paperclip centers on persistent Approval objects that gate critical operations. This architecture ensures that autonomous agents remain under human oversight while providing a clean API for board operators to review and decide on pending actions.
Core Concepts: Approval Objects and State Management
Three foundational components comprise the governance system:
-
Approval Objects — Persistent records capturing the requestor, payload type (e.g.,
hire_agent,approve_ceo_strategy), and status (pending,approved,rejected,revision_requested). Defined in [packages/shared/src/types/approval.ts](https://github.com/paperclipai/paperclip/blob/master/packages/shared/src/types/approval.ts) -
Database Schema — The
approvalsandapproval_commentstables store all approval data with proper indexing. See [packages/db/src/schema/approvals.ts](https://github.com/paperclipai/paperclip/blob/master/packages/db/src/schema/approvals.ts) -
Inbox Aggregation — The board operator's Inbox surfaces pending approvals as the highest-priority items, with inline Approve/Reject actions available across the Dashboard and entity detail pages. Specified in [
doc/spec/ui.md](https://github.com/paperclipai/paperclip/blob/master/doc/spec/ui.md)
Step-by-Step Governance Flow
1. Request Creation
When an agent-related action is initiated, the system creates an approval record before executing any side effects.
// Create a hire-agent approval request
await fetch('/api/approvals', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'hire_agent',
payload: { name: 'DevBot', model: 'claude-3' },
requestedByUserId: currentUser.id,
companyId: currentCompany.id,
})
});
The server stores this with status: "pending" and associates it with the requesting user's ID.
2. Inbox Aggregation
The Inbox query (GET /api/inbox) pulls all pending approvals alongside other alerts (budget warnings, failed heartbeats). Each approval renders with:
- A shield icon indicating governance review required
- A concise title derived from the payload type
- Approve and Reject buttons for immediate action
3. Board Review and Decision
The board operator renders their judgment through the decision endpoint:
// Approve a pending agent hire
await fetch(`/api/approvals/${approvalId}/decide`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
decision: 'approved', // or 'rejected' / 'revision_requested'
decidedByUserId: boardUser.id,
note: 'Looks good to me!',
})
});
The server updates status to approved or rejected and records the deciding user's ID.
4. Side-Effect Execution
| Decision | Outcome |
|---|---|
| Approved | Original request re-executed (agent created, capability granted, etc.) |
| Rejected | No side effects; requestor receives notification with rejection reason |
| Revision Requested | Approval returned to pending with feedback for the original requestor |
5. Audit and Logging
Every state transition emits structured activity logs:
approval.created— Initial request loggedapproval.decided— Final decision recorded with timestamp and user attribution
Logs are strictly scoped by companyId to enforce multi-tenant isolation.
6. Visibility and Monitoring
Approved approvals disappear from the Inbox unless they require follow-up (e.g., revision_requested). The Dashboard always displays the pending approval count via [ui/src/pages/apps/useReviewCount.ts](https://github.com/paperclipai/paperclip/blob/master/ui/src/pages/apps/useReviewCount.ts), serving as a health metric for governance backlog.
7. Plugin Extensibility
Plugins hook into the approval lifecycle through events defined in [doc/plugins/PLUGIN_SPEC.md](https://github.com/paperclipai/paperclip/blob/master/doc/plugins/PLUGIN_SPEC.md):
approval_created— Trigger custom policy checks (budget limits, capability gating)approval_decided— Execute post-decision workflows (notifications, external integrations)
Core Governance Guarantees
| Guarantee | Implementation |
|---|---|
| Company-Scoped Access | All queries filter on companyId; no cross-tenant data leakage |
| Immutable Audit Trail | Approval rows are append-only; status changes are timestamped, never overwritten |
| Single-Assignee Review | Row-level locking ensures only one board operator can decide a given approval |
| Budget Hard-Stop | Budget alerts appear in pending counts; new hires blocked until board resolution |
Fetching Pending Approvals
Client applications query the approval state to render governance UI:
// Query all pending approvals for Inbox rendering
const pending = await fetch('/api/approvals?status=pending')
.then(r => r.json());
// Render each item with inline approve/reject controls
Key Source Files
Summary
- Agent approval and governance in Paperclip requires board-level sign-off through persistent
Approvalobjects before any agent operation executes - The Inbox aggregates pending approvals with inline decision controls, while the Dashboard surfaces approval counts as operational health metrics
- Immutable audit trails capture every state transition with full attribution, ensuring compliance and accountability
- Plugin hooks enable custom policy enforcement without core code modification
Frequently Asked Questions
What happens if a board operator rejects an agent hire?
The approval record updates to status: "rejected" with no side effects executed. The original requestor receives a notification containing the rejection reason provided by the board operator. The agent is never created.
Can multiple board operators approve the same request simultaneously?
No. The system enforces single-assignee review through database-level constraints. Once a board operator loads an approval for review, other operators see it as locked. This prevents split-brain decisions where two operators might render conflicting judgments.
How does Paperclip prevent agent hires from exceeding budget limits?
The budget hard-stop mechanism displays budget alerts within the pending approval count. If a proposed hire would exceed allocated resources, the approval remains blocked until the board explicitly resolves the budget conflict—either by approving an override or requesting revision to the proposal.
Where are approval schemas validated before database insertion?
All approval payloads pass through Zod validators defined in [packages/shared/src/validators/approval.ts](https://github.com/paperclipai/paperclip/blob/master/packages/shared/src/validators/approval.ts). This shared validation layer ensures consistency across API requests, background jobs, and plugin-generated approvals before any data reaches the database schema in [packages/db/src/schema/approvals.ts](https://github.com/paperclipai/paperclip/blob/master/packages/db/src/schema/approvals.ts).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →