How Paperclip Board Approval Workflows and Governance Gates Function for Agent Hires
Paperclip enforces a multi-step governance process where every agent hire creates a draft agent in pending_approval status and a linked hire_agent approval record that must pass company policy checks, budget gates, and explicit board approval before activation.
The Paperclip platform treats agent hiring as a privileged operation requiring organizational oversight. Whether triggered via CLI, API, or agent-to-agent calls, the system routes all hire requests through a structured approval workflow designed to enforce budget constraints, board governance, and auditability. This article examines the complete lifecycle—from draft creation through final activation—based on the Paperclip source code implementation.
Overview of the Agent Hire Approval Process
When an agent or board member initiates a hire, Paperclip does not immediately create an active agent. Instead, it establishes two linked records: a draft agent with frozen configuration and an approval object that tracks the governance decision. This separation allows boards to review, modify, or reject proposals without affecting operational agents.
The approval system centralizes around three core concepts:
- Approval types: Categorized by operation (
hire_agent,budget_change, etc.) - Governance gates: Automated policy checks (budget limits, role conflicts)
- Board decisions: Human or delegated authority to approve, reject, or request revisions
Step 1: Draft Agent Creation and Approval Record Generation
Upon receiving a hire request, server/src/services/approvals.ts instantiates a draft agent with status pending_approval. Simultaneously, it writes a record to the approvals table with type: "hire_agent" containing a complete payload snapshot.
The payload schema—defined as HireAgentPayload in the type system—captures:
// Representative payload structure for hire_agent approvals
{
type: "hire_agent",
payload: {
name: "Research Analyst",
role: "Research Analyst",
budget: 3000,
capabilities: ["search", "summarize"],
reporting_to: "manager-agent-id", // optional hierarchy
}
}
This snapshot ensures board members review exactly what was proposed, even if subsequent configuration changes occur elsewhere in the system.
Step 2: Company Policy Evaluation and Board Approval Requirements
Each Paperclip company configures governance through policies stored in the company settings. The require_approval policy (documented in docs/guides/board-operator/approvals.md) determines which operation types demand board review.
When require_approval is enabled for hire_agent types:
- The approval status remains
pendingindefinitely - No lifecycle hooks execute
- The draft agent cannot transition to
active
If the policy is disabled, the system may auto-approve routine hires while still logging the decision for audit purposes.
Step 3: Governance Gates and Budget Enforcement
Before any approval reaches the board, server/src/services/approvals.ts executes automated governance gates. The primary gate is budget validation:
- The system sums the proposed agent's budget against existing committed budgets
- If the total exceeds the company budget cap, the approval status shifts to
revision_requested - The board must either reduce the proposed budget or explicitly authorize a
budget_override
Additional gates may check for:
- Role hierarchy violations (circular reporting chains)
- Capability conflicts with existing agents
- Rate limits on hire frequency
These gates prevent malformed or excessive requests from consuming board attention while maintaining guardrails.
Step 4: Board Review and Decision Lifecycle
Board members interact with pending approvals through the Paperclip UI, specifically via the Approvals navigation tab. The ui/src/components/ApprovalPayload.tsx component renders hire_agent payloads in a structured format showing:
- Proposed agent name and role
- Budget allocation
- Capability grants
- Proposed reporting structure
Board members have three resolution options:
| Action | System Effect |
|---|---|
| Approve | Triggers onHireApproved hook; agent status → active |
| Reject | Draft agent deleted or marked rejected; approval status → rejected |
| Request Revision | Approval status → revision_requested; original requester notified |
Upon approval, the approval service in server/src/services/approvals.ts orchestrates:
- Status transition to
approved - Draft agent activation
onHireApprovedlifecycle hook invocation- Activity log writes for audit trails
Step 5: Lifecycle Hook Execution and Post-Approval Configuration
The onHireApproved hook—defined in packages/adapter-utils/src/types.ts at line 311—allows adapter developers to execute custom logic when hires complete:
// Hook implementation in adapter-managed-agents.ts
export const onHireApproved = async ({
agentId,
companyId,
}: { agentId: string; companyId: string }) => {
// Perform post-hire configuration
await grantPermissions(agentId, { role: "analyst" });
await provisionResources(agentId, { computeTier: "standard" });
};
This hook runs after board approval but before the agent begins accepting tasks, enabling secure initialization of credentials, resources, or external integrations.
Step 6: Rejection Handling and Approval Cleanup
When a board rejects a hire, the system performs cleanup based on configuration:
- Hard deletion: Draft agent removed; approval record retained with
rejectedstatus for audit - Soft retention: Draft agent marked
rejectedwith tombstone timestamp
The system also handles superseded requests. If a newer hire request overlaps with a pending approval for the same role or purpose, the migration logic (referenced in built-in-agent-unique-marker-migration.sql) cancels orphan approvals with the comment: "cancel each duplicate's orphan pending hire_agent approval".
Idempotency and Concurrency Guarantees
The approval service is designed for safe concurrent access. As verified in approval-routes-idempotency.test.ts, repeated approval attempts for the same hire_agent approval ID are deduplicated at the database level. This prevents race conditions where multiple board members simultaneously approve the same request, which could otherwise create duplicate active agents.
CLI and API Integration
Operators can manage the full approval lifecycle programmatically:
Create a hire approval via CLI:
npx paperclipai approval create \
--company-id <company-id> \
--type hire_agent \
--payload '{"name":"New Analyst","role":"Research Analyst","budget":5000}'
Approve via CLI:
npx paperclipai approval update \
--approval-id <approval-id> \
--status approved
API equivalent:
// POST /api/approvals — Create hire approval
await fetch(`${API_URL}/approvals`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
type: "hire_agent",
payload: {
name: "Research Analyst",
role: "Research Analyst",
budget: 3000,
capabilities: ["search", "summarize"],
},
}),
});
The CLI implementation resides in cli/src/commands/client/approval.ts, which wraps the REST endpoints defined in server/src/routes/approvals.ts.
Key Source Files and Their Roles
server/src/services/approvals.ts: Core state machine; implements budget gates, status transitions, and hook orchestrationserver/src/routes/approvals.ts: REST API surface for CRUD operations on approval recordspackages/adapter-utils/src/types.ts: TypeScript definitions foronHireApprovedand related payloadsui/src/components/ApprovalPayload.tsx: Board-facing UI for rendering hire proposalscli/src/commands/client/approval.ts: Operator tooling for approval managementdocs/guides/board-operator/approvals.md: Human documentation for governance configuration
Summary
- Every agent hire creates a
pending_approvaldraft and a linkedhire_agentapproval record, ensuring no agent activates without oversight - Company policies in
require_approvaldetermine whether board review is mandatory for specific operation types - Automated governance gates—primarily budget validation with override capabilities—filter requests before board review
- Board decisions trigger the
onHireApprovedlifecycle hook, enabling adapters to configure credentials and resources - The system guarantees idempotency and proper cleanup of rejected or superseded approvals through database constraints and migration logic
- Full programmatic access via CLI and API allows integration with external HR or procurement systems
Frequently Asked Questions
What happens if a hire request exceeds the company budget?
The approval status automatically transitions to revision_requested. The board must either reduce the proposed budget in the payload or explicitly provide a budget_override authorization. Until resolved, the draft agent remains inactive and no lifecycle hooks execute.
Can agents hire other agents without board approval?
If the company policy require_approval for hire_agent operations is disabled, agents can hire subordinates autonomously. However, governance gates still apply—budget constraints may trigger revision requests even without mandatory board review. The policy granularity allows organizations to balance agility with control.
How does the onHireApproved hook differ from general agent initialization?
The onHireApproved hook runs after board approval but before task acceptance, whereas general initialization occurs at agent process startup. This sequencing allows the hook to provision resources that require governance authorization—such as API keys with elevated permissions or budget-enforced service accounts—that should not be available during pre-approval phases.
What prevents duplicate agents from concurrent approvals?
The approval service implements database-level idempotency constraints verified by approval-routes-idempotency.test.ts. Multiple simultaneous approval attempts for the same approval ID collapse to a single successful transaction. The draft agent record includes unique markers that prevent re-activation of already-processed hires.
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 →