Make policy executable
Express per-payment and rolling-window caps plus payee, network, and asset rules. No matching policy, ambiguous input, or malformed payload means DENY.
Counterroll puts a deny-by-default policy gate between every AI agent and every x402 payment. It evaluates each request against your policy, fails closed on anything it cannot safely evaluate, and returns a signed, hash-chained receipt for every policy-evaluable decision — before settlement. x402 today, running live on Base; a rail-neutral evidence model designed for what comes next.
import { Counterroll, usdc } from '@counterroll/sdk'; // Public sandbox: one published key, bundled demo agents, a live URL. const cr = new Counterroll({ apiKey: 'qm_sandbox_publicdemo_v1' }); const ok = await cr.check({ agent: 'research-bot', to: 'openai', amount: usdc(1) }); const no = await cr.check({ agent: 'research-bot', to: 'openai', amount: usdc(500) }); console.log(ok.approved, no.reasons); // true ['per_tx_cap_exceeded'] console.log(no.receipt!.hash); // signed + hash-chained — verify it yourself
x402.org reported more than 75 million transactions and $24 million in volume over the prior 30 days (observed 2026-07-14). Cloudflare Agents now supports x402 payments directly. The payment rail is scaling; teams need authorization and audit evidence before autonomous spend reaches production.
Market context observed 2026-07-14 — protocol signals, not Counterroll customer or revenue claims.A wallet limit can cap an amount. Counterroll records which agent asked, which policy was applied, why the decision was made, and whether the evidence still verifies later.
Express per-payment and rolling-window caps plus payee, network, and asset rules. No matching policy, ambiguous input, or malformed payload means DENY.
Every policy-evaluable APPROVE and DENY preserves the applied policy snapshot—or its absence—plus decision reasons, hash-chain link, and organization signature.
Policy and audit history live outside any one facilitator. Swap the upstream x402 facilitator without rebuilding your governance layer or surrendering the ledger — x402 today, a rail-neutral evidence model designed for what comes next.
qm.verify({ publicKey }) pulls the raw rows and re-derives the entire chain locally: every hash recomputed, every signature checked against a key you pinned out-of-band. An attacker who rewrote the database and re-signed it with a fresh key passes the server's own check — and fails yours.{
"decision": "APPROVE",
"denyReasons": [],
"attestation": {
"id": "att_29240a122a59b0c63e0749d16008ded9",
"organizationId": "org_sandbox",
"seq": "1",
"agentId": "research-bot",
"policyId": "policy_sandbox_research",
"decision": "APPROVE",
"payTo": "openai",
"amount": "1000000",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"policySnapshot": { "perTxCap": "5000000", "windowCap": null, … },
"requirements": { … },
"eip3009Nonce": "c31d1ab50d697dd6b7a1973632ca2a93d8e247fa442e830fef57d7c1b4bfcac6",
"payloadSha256": "53e3bc4b0d3735baf9d8e1ab3642bfbc4626366b68d23fa543246cbef575c56b",
"prevHash": "GENESIS",
"hash": "e0c9a5964716f7b6a0cb71e6275601e4a00524bc43d40a15e6dbc547d9c15082",
"signature": "23106faca39847ab2b2cec5c3f428760214b42e395754d3ebf576189d258ff83926059aeb7515ea5c18a0d4b85c45c9e0383191f42faf0c3c38b78a9544f0109",
"createdAt": "2026-07-18T22:16:00.214Z"
}
}
{
"decision": "DENY",
"denyReasons": ["per_tx_cap_exceeded"],
"attestation": {
"id": "att_abbfe9ca99df62898d204f1c520ac181",
"organizationId": "org_sandbox",
"seq": "2",
"agentId": "research-bot",
"policyId": "policy_sandbox_research",
"decision": "DENY",
"payTo": "openai",
"amount": "500000000",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"policySnapshot": { "perTxCap": "5000000", "windowCap": null, … },
"requirements": { … },
"eip3009Nonce": "9641901101b3259d4145bd6a594571bd45543051aa2585d1a0fd9755584b43b3",
"payloadSha256": "9954c6456edcc97c62984f3c425de8d7fb50daa495c845553e29484757596e95",
"prevHash": "e0c9a5964716f7b6a0cb71e6275601e4a00524bc43d40a15e6dbc547d9c15082",
"hash": "dbf1b4efee0ff1baf6e76dce561627452f25affecae65d1735fc2ae0eb97c616",
"signature": "c71dc37a66c2b7f933710bf452e41e72b2210d466b69b3eefaabcf84f9830610a07f75b98511feecd0a42545ec86da929c916752d15ffb518755013b2b8e2903",
"createdAt": "2026-07-18T22:16:00.670Z"
}
}
Rows 1 and 2 of Counterroll's live public sandbox chain (org_sandbox), pulled read-only from the deployed API on 2026-07-23. Every hash and signature is real: the DENY's prevHash is the APPROVE's hash, and both signatures verify against the org's published anchor e9d3619e…b6d1. Run qm.verify({ publicKey }) to re-derive the whole chain yourself against that key — 132 signed rows the day this was captured, and append-only since. Note what the deny costs you: nothing moved, and you still got a receipt.
The SDK is public and self-serve — start there. Beyond it, Counterroll supports two focused partner paths: teams putting one governed payment flow into production, and platforms embedding the same independent control across many customers. Both begin with a real integration surface—not a generic AI demo.
Best for enterprises and infrastructure teams with a named technical owner, a production path, and a policy they refuse to ship without. Outcome: enforced decisions plus independently verifiable evidence.
Best for wallets, facilitators, agent runtimes, marketplaces, and cloud platforms that want independent governance without rebuilding policy and signed evidence. Outcome: one integration serving many downstream customers.
We will use it to test the three questions that determine whether Counterroll earns a place in your stack—or inside the product you distribute.
Wallet limits are useful and Counterroll does not replace them. Counterroll adds an organization-scoped decision layer: agent identity, policy assignment, payee and rail restrictions, rolling-window context, explicit deny reasons, and a signed policy snapshot preserved in an independently verifiable chain.
No. Counterroll authorizes, attests, and records. Approved x402 traffic is proxied to the facilitator your organization configures. Settlement remains upstream.
The SDK can pull raw rows and locally re-derive every hash and signature against an organization public key pinned outside the database. A rewritten chain signed with a different key fails that client-side verification.
A DENY is a successful policy evaluation, not an exception. The agent receives approved: false plus machine-readable reasons such as per_tx_cap_exceeded; the decision is retained as evidence before money moves.
We audit Counterroll the way an attacker would, before each layer ships. Two findings from those audits shaped the architecture. Both are in the git history.
The window cap said $200 a day. The first build read the agent's window spend, evaluated the policy, then appended the attestation — three steps, no lock. Two near-cap requests arriving at once would each read the same "spent so far," each pass evaluation, and both get approved: window overspent, with two individually valid receipts to show for it. A textbook time-of-check/time-of-use race, in the one component whose job is making spend limits mean something.
The fix put the replay check, the window read, the evaluation, and the append inside a single transaction holding the org's row lock — and the regression test now races concurrent near-cap requests to prove it. Nothing evaluates outside that transaction.
For drop-in x402 compatibility, the gateway once accepted a fallback: if the agent-signature headers were missing, resolve the agent from the payment's EIP-3009 from wallet. Read that from an attacker's chair. The API key authenticates the tenant, not the agent — so anyone holding the org key could attribute spend to any agent in the org, minting APPROVE attestations and draining that agent's window budget, with no proof of possession of the agent's key. (Never settlements — the EIP-712 signature still fails upstream.)
The gateway now requires the agent's Ed25519 request signature by default and rejects the wallet path with 401 before any ledger write; the fallback survives only as an explicit per-org opt-in, documented as the tradeoff it is.
A tamper-evident ledger is only as good as the code that writes to it. The audits continue; the relevant fixes, tests, and threat-model decisions are available during technical diligence.
Counterroll moves no money: v1 authorizes, attests, and records, and settlement is proxied to whichever x402 facilitator you configure. A governance layer is only credible if it can say no to the rail itself — so it is built to be checked without trusting us.
Counterroll holds no funds and no settlement keys. Approved x402 traffic is proxied to your configured facilitator, and your receipts ride in the standard x402 extensions field — swap facilitators without touching your ledger.
Deny by default, everywhere: no matching policy, an ambiguous rule, or a malformed payload all fail closed. An unavailable policy service cannot quietly become permission to pay, and the wallet-impersonation fallback stays off unless an org explicitly opts in.
No trust required: qm.verify({ publicKey }) re-derives every hash and signature locally against a key you pin out-of-band — e9d3619e…b6d1 for the public sandbox. A rewritten, re-signed database passes our server's own check and fails yours.