Running multiple independent companies in one Paperclip instance: company_id isolation guarantees, shared infrastructure costs, per-company billing separation, and UI multi-tenancy.
Run multiple independent companies in one Paperclip installation, implement tenant isolation, and offer Paperclip-as-a-service to multiple customers.
Paperclip is designed for multi-tenancy. A single installation can host:
- Multiple internal departments (Engineering, Marketing, Support)
- Multiple client projects (agency model)
- Multiple end-customers (SaaS product)
As covered in pcl-11, every table has a company_id column and every query filters by it. This is architectural, not configurable — you cannot accidentally leak data between companies.
-- Verify isolation: this should NEVER return rows from another company
EXPLAIN SELECT * FROM issues WHERE company_id = 'company-a-id';
-- → should see "company_id = 'company-a-id'" in the Filter node
// packages/server/src/scripts/create-company.ts
async function createCompanyWithDefaults(opts: {
name: string;
slug: string;
budget: number;
template?: 'content' | 'dev-team' | 'support';
}) {
const company = await db.companies.create({
name: opts.name,
slug: opts.slug,
monthly_budget_usd: opts.budget,
});
if (opts.template === 'content') {
await seedContentTeamAgents(company.id);
} else if (opts.template === 'support') {
await seedSupportTeamAgents(company.id);
}
return company;
}
Each company can have its own heartbeat schedule:
-- High-priority company: every 1 minute
UPDATE companies SET heartbeat_cron = '* * * * *' WHERE slug = 'priority-client';
-- Low-priority: every 30 minutes
UPDATE companies SET heartbeat_cron = '*/30 * * * *' WHERE slug = 'low-tier-client';
Track costs per company for billing:
-- Monthly invoice data per company
SELECT
c.name AS company,
c.slug,
SUM(ce.cost_usd) AS llm_cost_usd,
COUNT(DISTINCT ce.id) AS llm_calls,
COUNT(DISTINCT i.id) AS issues_resolved
FROM companies c
LEFT JOIN cost_events ce
ON ce.company_id = c.id
AND date_trunc('month', ce.created_at) = date_trunc('month', now())
LEFT JOIN issues i
ON i.company_id = c.id
AND date_trunc('month', i.resolved_at) = date_trunc('month', now())
GROUP BY c.id, c.name, c.slug
ORDER BY llm_cost_usd DESC NULLS LAST;
Add your SaaS markup (e.g. 3× LLM cost) in the billing layer.
The Paperclip admin API (requires admin JWT) supports:
GET /api/admin/companies → list all companies
POST /api/admin/companies → create company
PATCH /api/admin/companies/:id → update (budget, pause, etc.)
DELETE /api/admin/companies/:id → soft-delete
GET /api/admin/companies/:id/stats → usage and cost summary