# Agent Approval and Governance Workflow in Paperclip: A Complete Technical Guide

> Explore Paperclip's agent approval and governance workflow. Learn how explicit board approvals and immutable audit trails ensure secure agent management. A complete technical guide.

- Repository: [Paperclip/paperclip](https://github.com/paperclipai/paperclip)
- Tags: deep-dive
- Published: 2026-08-16

---

**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/main/packages/shared/src/types/approval.ts)](https://github.com/paperclipai/paperclip/blob/master/packages/shared/src/types/approval.ts)

- **Database Schema** — The `approvals` and `approval_comments` tables store all approval data with proper indexing. See [[`packages/db/src/schema/approvals.ts`](https://github.com/paperclipai/paperclip/blob/main/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/main/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.

```typescript
// 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:

```typescript
// 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/main/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/main/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:

```typescript
// 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/main/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/main/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/main/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/main/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/main/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/main/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/main/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/main/packages/db/src/schema/approvals.ts)](https://github.com/paperclipai/paperclip/blob/master/packages/db/src/schema/approvals.ts).