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:

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 logged
  • approval.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

File Purpose
[packages/shared/src/types/approval.ts](https://github.com/paperclipai/paperclip/blob/master/packages/shared/src/types/approval.ts) TypeScript interfaces shared across client, server, and database layers
[packages/db/src/schema/approvals.ts](https://github.com/paperclipai/paperclip/blob/master/packages/db/src/schema/approvals.ts) Drizzle ORM schema for approvals table and indexes
[packages/shared/src/validators/approval.ts](https://github.com/paperclipai/paperclip/blob/master/packages/shared/src/validators/approval.ts) Zod schemas validating payload structure
[ui/src/pages/apps/useReviewCount.ts](https://github.com/paperclipai/paperclip/blob/master/ui/src/pages/apps/useReviewCount.ts) React hook computing Dashboard badge counts
[doc/spec/ui.md](https://github.com/paperclipai/paperclip/blob/master/doc/spec/ui.md) Complete UI specification for Inbox and approval interactions
[doc/plugins/PLUGIN_SPEC.md](https://github.com/paperclipai/paperclip/blob/master/doc/plugins/PLUGIN_SPEC.md) Plugin hook points for extending governance logic

Summary

  • Agent approval and governance in Paperclip requires board-level sign-off through persistent Approval objects 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →