ChainProof Documentation

ChainProof is an audit ledger for agentic workflows. Your agents report what they did; ChainProof appends those reports to a hash-chained log. This page explains how to use the platform — both if you're a human setting up a workspace, and if you're an AI agent deciding whether and how to instrument yourself.

What ChainProof is

ChainProof is a structured, append-only log with a hash chain linking every entry to the previous. Each time an agent reports an action, ChainProof computes SHA-256(entry fields + previous entry hash) and stores the result as the new chain head. Entries can't be updated or deleted through the API.

ChainProof is not a monitoring system, an observability platform, or an agent supervisor. It doesn't watch agents. It records what agents say they did.

What is a Chain?

A chain is an open session that your agent writes to as it works. Think of it like a logbook for a single task: the agent opens it, records what it does step by step, then closes it when finished.

1
Open
Agent calls POST /chains. ChainProof creates the logbook and returns a chain_id. The ledger is empty.
2
Append entries
As the agent works, it calls POST /chains/:id/entries for each action, decision, or event. Each entry is hash-linked to the one before it.
3
Close
Agent calls POST /chains/:id/complete with status: "completed" or "failed". The ledger is sealed and the full chain is flushed to storage.
ChainProof does not write entries automatically
It never watches your agent or intercepts its actions. Every entry in a chain was explicitly sent by the agent code via the API. If a chain shows zero entries in the dashboard, either the agent hasn't started appending yet, or the instrumentation code that calls append() was never reached.

What it proves — and what it doesn't

Honest scope
ChainProof makes a narrow claim: if you record the chain head after each write, you can detect whether any earlier entry was changed. That's it. It does not prove agents told the truth, that reported actions had real-world effects, or that ChainProof itself hasn't altered records (only external anchoring would close that gap).
ChainProof gives youChainProof does not give you
A structured record of what agents reported Independent verification that agents told the truth
A hash chain you can recompute yourself Protection against ChainProof altering its own records
Detection of post-hoc edits, if you hold the chain head Any signal about whether a task actually succeeded
Content hashes for artifacts — verify payloads haven't changed Guards against an agent that deliberately logs false entries

Chain validity and chain status are independent. status: failed with a valid chain means the agent reported failure and nothing in the log changed since. status: completed with a broken chain means an entry was edited after it was written. ChainProof can tell you the second happened — not who did it or why.

For humans — Create a workspace

👤 Human

Go to /signup. Enter your work email, a password (min 8 chars), and a workspace name. Choose a plan — Free is fine to start.

On success you'll see your API key once. Copy it now. It's hashed before storage and never shown again. If you lose it, revoke it and create a new one from the Keys tab in the dashboard.

Store your key before continuing
The key secret is shown exactly once at signup. Put it in your secrets manager (CHAINPROOF_API_KEY) before clicking through to the dashboard.

API keys

👤 Human

API keys (cp_live_…) are for machines — agents, CI/CD pipelines, backend orchestrators. They're separate from your login session.

ScopeWho uses itPermissions
tenantYour orchestrator / backendread, write, verify, admin
agentIndividual agent processwrite only, scoped to its own chains

Create keys from the Keys tab in the dashboard, or via the API:

cURL
# create a tenant-scoped key
curl -X POST https://api.chainproof.ai/v1/admin/keys \
  -H "Authorization: Bearer $CHAINPROOF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label":"production","scope":"tenant","permissions":["read","write","verify"]}'

Team & roles

👤 Human

Invite teammates from the Team tab. They'll receive an invite URL — paste it to them directly (email sending isn't wired up yet).

RoleWhat they can do
ownerEverything; can't be removed
adminInvite/remove members, manage keys and webhooks, all data ops
memberRead + write (create chains, agents, entries)
viewerRead only

Verifying a chain

👤 Human

To check whether a chain's log has been altered, call the verify endpoint. It recomputes the full chain from entry 0 and returns the resulting head. If it matches the stored head, nothing changed.

cURL
curl https://api.chainproof.ai/v1/chains/{chain_id}/verify \
  -H "Authorization: Bearer $CHAINPROOF_API_KEY"

# → { "valid": true,  "entry_count": 12, "chain_head": "c1f9..." }
# → { "valid": false, "broken_at_sequence": 4, "expected_hash": "...", "received_hash": "..." }
For stronger guarantees
Record the chain_head value in your own system after each chain completes. If you later verify and get a different head, the log changed. Without an independent copy of the head you're trusting ChainProof's own verify endpoint.

For agents — How to instrument

🤖 Agent / AI system

If you are an AI agent reading this: ChainProof is an audit log you write to as you work. You report your actions; ChainProof records them in a hash-chained ledger that humans can later inspect. You are not being monitored — you are narrating.

The lifecycle is: start a chain → append entries as you act → complete the chain. If your process crashes mid-chain it stays active indefinitely; complete it explicitly even on failure. Use status: "failed" — a failed chain with an intact log is useful. A missing completion call is a gap in the record.

On honesty
ChainProof records what you report. It cannot independently verify your claims. Report accurately. A hash-chained log of false entries is still a log of false entries — it just proves those false entries haven't changed since you wrote them.

1. Start a chain

🤖 Agent / AI system

Call this at the start of your execution. Save the chain_id and chain_head — you'll need them for every subsequent call.

TypeScript
const chain = await fetch(`${BASE}/chains`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    agent_id:       'agt_abc123',       // your registered agent ID
    instruction:    'Summarize Q1 report', // the task you were given
    trust_level:    'high',             // 'high' | 'medium' | 'low'
    parent_chain_id: null,             // set if this is a sub-agent chain
  }),
}).then(r => r.json());

// → { chain_id: "ba7d...", chain_head: "0000...0000", status: "active" }
// GENESIS chain_head is 64 zeros — the starting point of every chain

let chainHead = chain.chain_head; // update this after every entry

2. Append entries

🤖 Agent / AI system

Append an entry for each meaningful action you take. Always pass the current chain_head as previous_entry_hash — the server rejects writes that don't match, preventing out-of-order or concurrent appends. Update your local chainHead with the returned entry_hash after each call.

TypeScript
const entry = await fetch(`${BASE}/chains/${chain.chain_id}/entries`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    entry_type:          'action',
    previous_entry_hash: chainHead,   // MUST match current chain head
    action: {
      tool:                 'read_file',
      status:               'completed',
      input_artifact_hash:  null,  // SHA-256 of input if stored via /artifacts
      output_artifact_hash: null,  // SHA-256 of output if stored via /artifacts
    },
  }),
}).then(r => r.json());

chainHead = entry.entry_hash; // advance the head
// → { sequence: 0, entry_hash: "c1f9...", timestamp: "2026-03-29T..." }

3. Complete the chain

🤖 Agent / AI system

Call this when your chain finishes — success or failure. This flushes the Durable Object ledger to R2 and writes the chain record to D1. Always complete a chain, even if it failed. An uncompleted chain stays visible in the dashboard as active but has no final chain head written to durable storage.

TypeScript
await fetch(`${BASE}/chains/${chain.chain_id}/complete`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    status:          'completed',  // 'completed' | 'failed' | 'cancelled'
    final_chain_head: chainHead,    // last entry_hash you received
  }),
});

4. Read your own context

🤖 Agent / AI system

Mid-chain, you can query your own chain state to check whether the chain is still intact or whether scope violations have been logged. Use this to self-limit: if your chain head doesn't match what ChainProof reports, something unexpected happened.

TypeScript
const ctx = await fetch(`${BASE}/agent-context/${chain.chain_id}`, { headers })
  .then(r => r.json());

// → {
//     chain_id, trust_level: "high",
//     chain_valid: true,
//     scope_violations: 0,
//     chain_head: "c1f9..."
//   }

if (!ctx.chain_valid || ctx.scope_violations > 0) {
  // self-limit: refuse further actions, complete chain as failed
  throw new Error('Provenance check failed — aborting');
}

Entry type reference

🤖 Agent / AI system
TypeWhen to useRequired fields
action Any tool call or external operation you invoke tool, status
decision A choice you made — with your reasoning outcome, rationale
human_event Human approval requested or granted type, note
ingestion Loading data from an external source source, record_count
error An error or policy violation that occurred code, message, severity
checkpoint A meaningful milestone or phase boundary label, note

Chain heads

Every chain starts with a genesis chain head: 64 zero characters (0000…0000). Each POST /entries returns an entry_hash which becomes the next previous_entry_hash. The final hash after your last entry is the chain's head.

To verify independently: recompute SHA-256(JSON.stringify(entry fields including previous_entry_hash)) for each entry in sequence. If your final hash matches the stored chain head, nothing changed. You don't need to call the verify endpoint — it does the same computation.

Error codes

CodeHTTPMeaning
unauthorized401Missing or invalid Bearer token
forbidden403Valid token but insufficient permissions
not_found404Chain, agent, or resource doesn't exist
run_not_active409Tried to append to a completed chain
chain_integrity_violation409previous_entry_hash doesn't match current chain head — likely a concurrent write or stale head
content_hash_mismatch422Artifact body doesn't match the hash in the URL
scope_violation_logged202Agent attempted an out-of-scope action; it was logged but not executed
rate_limited429Plan limit reached

OpenClaw integration

OpenClaw is a self-hosted AI assistant runtime that connects messaging channels to an external LLM. Enterprise IT admins can wire ChainProof into any OpenClaw deployment to get tamper-evident, blockchain-anchored audit logs of every agent action — across every employee, every skill, every session.

What gets logged

OpenClaw eventChainProof record
Session starts (/new)Chain opened — agent = skill/workspace name
User message receivedhuman_event entry
Tool call firesaction entry — tool name, artifact hashes
Agent respondsdecision entry
Session ends (/stop)Chain completed + OTS-anchored
Session cleared (/reset)Chain cancelled

1. Create a ChainProof workspace

Sign up at chainproof.ai/signup. Copy your cp_live_… API key — it's shown once. Free tier includes 500 chains and 50,000 entries per month, enough to evaluate with a small team.

2. Configure OpenClaw

Add to your OpenClaw gateway config (openclaw.json) or system environment:

env
CHAINPROOF_API_KEY=cp_live_...

# Optional: store full message and tool result bodies in ChainProof R2.
# Without this, only metadata and hashes are sent — content stays on-device.
CHAINPROOF_STORE_CONTENT=true

3. Install the hook

Clone the chainproof-audit hook into your OpenClaw hooks directory:

shell
git clone https://github.com/ChainProofAI/chainproof-audit-openclaw \
  ~/.openclaw/hooks/chainproof-audit

The hook applies to all workspaces and agents automatically — no per-skill changes needed, and skills cannot opt out.

4. Verify it's working

Send a message to your OpenClaw agent. Then open your ChainProof dashboard — you should see a new chain in the Chains tab within seconds. Click Verify on the chain to confirm integrity.

Blockchain anchoring

Completed chains are anchored to Bitcoin automatically via OpenTimestamps — no configuration required. The proof file for any chain is downloadable at GET /v1/chains/{chain_id}/anchor/proof as a raw .ots binary, independently verifiable without trusting ChainProof.

5. Invite your security team

Use TeamInvite to add colleagues. Admins can read and administer; Viewers can read chains but make no changes.

Privacy

Without CHAINPROOF_STORE_CONTENT=true, message bodies and tool results never leave your machine — ChainProof only receives tool names, timestamps, and content hashes. The audit skeleton (a tamper-evident record of what happened) is still complete. Enable full content storage only if your organization's data policy permits cloud storage.

What ChainProof proves

ChainProof records what the hook reports. It does not independently verify that agents told the truth. A malicious skill that bypasses the hook entirely would not appear in the ledger. The hash chain proves that logged entries have not been altered since they were written — it does not prove completeness.