Core Domain Model

The seven database tables: companies, agents, issues, heartbeat_runs, cost_events, approvals, activity_log. ERD walkthrough, primary/foreign key relationships, and the company_id boundary.

Core Domain Model

What you'll learn

The seven database tables that power Paperclip, how they relate to each other, and what every field means.


The seven tables

companies
    │ 1:N
    ├── agents (with self-ref reports_to)
    │       │ 1:N
    │       └── heartbeat_runs
    │               │ 1:N
    │               └── cost_events
    ├── issues (self-ref parent_id)
    ├── approvals
    └── activity_log

Every table except activity_log has a company_id foreign key. This is the multi-tenancy boundary — a query for one company can never accidentally return another company's data.


companies

The root entity. Everything belongs to a company.

Column Type Description
id uuid Primary key
name text Display name
timezone text e.g. America/New_York — drives heartbeat windows
monthly_budget_usd decimal Global ceiling across all agents
metadata jsonb Arbitrary config (webhook URLs, custom fields)
created_at timestamptz —

agents

An agent is a named AI worker with a role, execution config, and budget.

Column Type Description
id uuid Primary key
company_id uuid → companies Data boundary
name text e.g. "Senior Engineer"
role text Role description used in prompts
system_prompt text Full system prompt for this agent
adapter_type enum claude_code \| openai \| http \| shell
adapter_config jsonb Adapter-specific settings (model, url, etc.)
reports_to uuid → agents Self-referential FK for org chart
status enum active \| paused \| terminated
cron_schedule text When heartbeats fire, e.g. 0 9 * * *
monthly_budget_usd decimal Max spend per calendar month
created_at timestamptz —

issues

Tasks. The core unit of work in Paperclip.

Column Type Description
id uuid Primary key
company_id uuid Data boundary
parent_id uuid → issues Self-ref for subtask nesting
assigned_agent_id uuid → agents Who should work on this
title text Short task title
description text Full task details, context, requirements
status enum open \| in_progress \| blocked \| completed \| failed
priority integer Higher = more urgent. Default 0
metadata jsonb Agent-to-agent message passing
created_at timestamptz —
updated_at timestamptz Auto-updated on any change

heartbeat_runs

One record per agent work cycle. The primary observability surface.

Column Type Description
id uuid Primary key
agent_id uuid → agents Which agent ran
company_id uuid Data boundary
issue_id uuid → issues Which task was worked on
started_at timestamptz When the adapter was invoked
completed_at timestamptz When the adapter returned
status enum running \| completed \| failed \| dry_run
tokens_used integer Input + output tokens combined
cost_usd decimal Computed from model pricing
output_summary text Truncated agent output for the dashboard
error_message text Set on failure

cost_events

Immutable spend record. One per heartbeat run (or per API call for granular adapters).

Column Type Description
id uuid Primary key
company_id uuid Data boundary
agent_id uuid → agents Who spent
run_id uuid → heartbeat_runs Which run caused this cost
model text e.g. claude-3-5-sonnet-20241022
input_tokens integer Prompt tokens
output_tokens integer Completion tokens
amount_usd decimal Exact cost in USD
created_at timestamptz —

approvals

Pending human decisions that block agent actions.

Column Type Description
id uuid Primary key
company_id uuid Data boundary
action_type text hire_agent \| terminate_agent \| strategy_change \| budget_increase
payload jsonb The full context for the decision
status enum pending \| approved \| rejected \| expired
created_at timestamptz —
resolved_at timestamptz When a human acted

activity_log

Append-only audit trail. Never updated, only inserted.

Column Type Description
id uuid Primary key
company_id uuid Data boundary
actor_type text agent \| human \| system
actor_id uuid Agent or user who performed the action
action text e.g. issue.status_changed, agent.paused
entity_type text issue \| agent \| company \| approval
entity_id uuid The affected record
before jsonb Snapshot before the change
after jsonb Snapshot after the change
created_at timestamptz —

Checkpoint ✓

Sign in for progress tracking & Q&A →