Audit Trail & Activity Log

What events are automatically written to activity_log, the schema (actor, action, entity_type, entity_id, before/after snapshots, metadata), querying by time range, and CSV export.

The Audit Trail

What you'll learn

Paperclip's complete audit trail — what is logged automatically, how to query it for compliance, and how to export it for external reporting.

What Paperclip Logs Automatically

Event Table Key fields
Every heartbeat run heartbeat_runs status, cost, timing
Every LLM call cost_events model, tokens, cost, agent
Every issue state change issues status, result, timestamps
Every approval approvals trigger, decision, reviewer, note
Every agent status change agents (+ history table) old/new status, changed_at

The Issue Audit Log

The issues table already contains the full story:

SELECT
  id,
  title,
  status,
  attempt_count,
  created_at,
  claimed_at,
  resolved_at,
  EXTRACT(epoch FROM (resolved_at - created_at)) / 60 AS minutes_to_resolve,
  LEFT(result, 100) AS result_preview
FROM issues
WHERE company_id = 'your-company-id'
ORDER BY created_at DESC;

Agent Modification History

Paperclip records changes to agent configuration in a separate audit table:

-- If your schema has an agent_audit table:
SELECT
  aa.changed_at,
  aa.changed_field,
  aa.old_value,
  aa.new_value,
  aa.changed_by   -- 'heartbeat', 'api', or a user email
FROM agent_audit aa
WHERE aa.agent_id = 'your-agent-id'
ORDER BY aa.changed_at DESC;

Compliance Export Query

For SOC2/ISO27001 evidence packages, export a full activity log:

COPY (
  SELECT
    'issue'            AS record_type,
    i.id::text         AS record_id,
    i.title            AS description,
    i.status,
    a.name             AS actor,
    i.created_at       AS occurred_at,
    i.resolved_at      AS completed_at
  FROM issues i
  LEFT JOIN agents a ON a.id = i.assigned_agent_id
  WHERE i.company_id = 'your-company-id'

  UNION ALL

  SELECT
    'approval',
    ap.id::text,
    ap.trigger,
    ap.status,
    COALESCE(ap.human_reviewer_email, req.name),
    ap.created_at,
    ap.decided_at
  FROM approvals ap
  LEFT JOIN agents req ON req.id = ap.requesting_agent_id
  WHERE ap.company_id = 'your-company-id'

  UNION ALL

  SELECT
    'cost_event',
    ce.id::text,
    ce.model || ': ' || ce.input_tokens || '+' || ce.output_tokens || ' tokens',
    'recorded',
    a.name,
    ce.created_at,
    NULL
  FROM cost_events ce
  JOIN agents a ON a.id = ce.agent_id
  WHERE ce.company_id = 'your-company-id'
    AND ce.created_at >= '2024-01-01'

  ORDER BY occurred_at
) TO '/tmp/audit_export.csv' CSV HEADER;

Data Retention Policy

Paperclip never automatically deletes audit records. Implement your own retention policy:

-- Archive records older than 1 year to a cold storage table
INSERT INTO issues_archive SELECT * FROM issues
WHERE created_at < now() - interval '1 year'
  AND company_id = 'your-company-id';

DELETE FROM issues
WHERE created_at < now() - interval '1 year'
  AND company_id = 'your-company-id';

Checkpoint ✓

Sign in for progress tracking & Q&A →