~ $ cat ./guides/agents

How an agent proves
its own output existed.

A loop for autonomous timestamping: hash the bytes, pay the 402, keep the proof, and find out when Bitcoin confirms it. No account, no API key, no human in the loop — and nothing about your output leaves the agent except its fingerprint.

What the agent needs

The loop, in six steps

  1. Hash the output before anything else touches it. Store the hash with the artifact, not in a comment inside it.
  2. Ask the price. POST /v1/stamps without a payment returns 402 with the amount for that exact request in the body and in the PAYMENT-REQUIRED header. Asking is free.
  3. Pay. Sign an EIP-3009 TransferWithAuthorization for that amount and repeat the request with PAYMENT-SIGNATURE. Any x402 client does this in a few lines.
  4. Stamp. The 200 carries one .ots proof per hash (base64), a status URL per hash, and a signed receipt for the batch. Write them down before doing anything else.
  5. Keep the proof next to the artifact. A pending proof is already valid; it names the calendars that will commit it to Bitcoin.
  6. Complete it. When Bitcoin confirms, the status turns complete, the webhook fires, and downloading ots_url again returns the proof with the Bitcoin attestation inside. A POST /v1/verify will also finish a pending proof for you.

The requests

The whole protocol is one endpoint, one price, one proof. A probe, without payment:

$ curl -s -i -X POST https://stampthisfile.com/v1/stamps \
    -H 'content-type: application/json' \
    -d '{"hashes":["9f2c…"]}'

HTTP/2 402
payment-required: eyJ4NDAyVmVyc2lvbiI6MiwicmVzb3VyY2Ui…

{"x402Version":2,
 "error":"Payment required",
 "accepts":[{"scheme":"exact","network":"eip155:8453",
             "asset":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
             "amount":"20000","payTo":"0x…","maxTimeoutSeconds":300,
             "extra":{"name":"USD Coin","version":"2"}}],
 "extensions":{"bazaar":{…}}}

amount is in atomic units: 20000 is 0.02 USDC at six decimals. The same request with PAYMENT-SIGNATURE: <base64 PaymentPayload> returns 200, the proofs, and a PAYMENT-RESPONSE header with the transaction. Retrying with the same payment and the same body returns the same proofs and charges nothing more; the same payment with a different body is refused.

Paying from code

Use an x402 client rather than hand-rolling the signature. wrapFetchWithPayment from x402-fetch (or the official @x402/fetch) wraps fetch and handles the 402, the EIP-712 signature and the retry:

import { createHash } from 'node:crypto'
import { writeFile } from 'node:fs/promises'
import { privateKeyToAccount } from 'viem/accounts'
import { wrapFetchWithPayment } from 'x402-fetch'

const BASE = 'https://stampthisfile.com'
const account = privateKeyToAccount(process.env.AGENT_WALLET_KEY as `0x${string}`)
const pay = wrapFetchWithPayment(fetch, account)
const sha256 = (b: Uint8Array) => createHash('sha256').update(b).digest('hex')

const output = new TextEncoder().encode(JSON.stringify(report))
const res = await pay(`${BASE}/v1/stamps`, {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ hashes: [sha256(output)] }),
})
if (!res.ok) throw new Error(`stamp failed: ${res.status} ${await res.text()}`)

const { batch_id, stamps, receipt } = await res.json()
await writeFile(`runs/${batch_id}.ots`, Buffer.from(stamps[0].ots, 'base64'))
await writeFile(`runs/${batch_id}.json`, JSON.stringify({ hash: stamps[0].hash, receipt }, null, 2))

A batch is the same call with more hashes: up to 100 per request, one payment, one receipt, one proof per hash. That is the cheapest shape for an agent that writes many outputs: hash them all, send them together, keep the batch id.

MCP, if your agent speaks it

POST /mcp is a stateless MCP server (Streamable HTTP, no key) with four tools: stamp_hashes (paid), get_stamp, get_proof, verify_proof. It follows the x402 MCP transport: without a payment, stamp_hashes returns a tool error whose structuredContent is the PaymentRequired; retry with the payment payload in _meta["x402/payment"], and the settlement comes back in _meta["x402/payment-response"].

$ claude mcp add --transport http stampthisfile https://stampthisfile.com/mcp

That is the shortest path for Claude Code and anything else that can attach an HTTP MCP server: the agent gets the tools, reads the price from the error, and pays with whatever wallet its x402 transport is configured with.

Or install the skill

The stampthisfile/skills repository is an agent skill with a script that hashes, pays, stamps, upgrades and verifies in one go, plus the instructions an agent needs to use it:

$ npx skills add stampthisfile/skills

Read llms.txt for the full request and response shapes, the webhook format and the error codes. It is written to be read by a model, not skimmed by a person.

What to store, and why

KeepBecause
The artifact, unchangedThe proof is about these bytes. Keep them read-only; a re-save changes the hash.
The .ots proofThe proof is the evidence. The receipt is not a substitute and does not contain it.
The stamp id and status URLLets you re-download the proof, or fetch the Bitcoin height and block time later.
The signed receiptShows the service received that hash at that time, paid by that address. Signature verifiable against /.well-known/stampthisfile-keys.
The batch id and manifestTies a set of outputs to one commitment; makes a whole run provable with one proof.
The canonicalisation recipeIf you hash a normalised form (sorted keys, stripped metadata), record how, or nobody can reproduce the hash.

Six ways agents get this wrong

Verify on read

The cheapest integrity check is the one you run when you use the artifact. Hash it again, send the hash and the stored proof, and let the answer decide whether the pipeline is allowed to proceed:

const hash = sha256(await readFile(artifact))
const out = await fetch(`${BASE}/v1/verify`, {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ hash, ots: (await readFile(proof)).toString('base64') }),
}).then((r) => r.json())

if (out.status === 'invalid') throw new Error(`integrity check failed: ${out.reason}`)
if (out.status === 'complete') {
  if (out.ots) await writeFile(proof, Buffer.from(out.ots, 'base64')) // it upgraded a pending proof
  console.log(`existed by block ${out.bitcoin.height} (${out.bitcoin.block_time})`)
}

The call is free and stores nothing. It returns complete with the block height and time, pending with the calendars still working, or invalid with a reason: the three answers a pipeline actually needs.

Cost control

At $0.05 a hash, 100 outputs cost $5.00 and 10,000 cost $500.00. Two habits keep it small: stamp a manifest of many outputs rather than each file separately when the set is what matters, and cap the price the agent will accept in its x402 client so a misconfigured endpoint cannot drain the wallet. A batch of 100 hashes settles in one payment, so there is no per-call overhead beyond the hashes themselves.

Questions

Can an agent pay without a human?

Yes. There is no account and no API key: the agent holds a wallet with USDC and signs an EIP-3009 authorization per request. Gas is paid by the facilitator, so the wallet needs no ETH. If your policy forbids hot wallets, use a subscription API with a key a human provisions, and accept that the key is now the agent’s to leak.

What happens if the calendars are down?

If fewer than two of the four calendars answer, the request returns 503 and nothing is charged. There is no half-stamped state to clean up: either the batch was submitted and you paid, or you did not.

Does the agent need to keep the output?

Yes. The proof is about the hash, and the hash is only meaningful next to the bytes. Keep the artifact and the .ots together, and keep the artifact read-only: editing it, or even re-saving it, breaks the pair.

How does the agent know the proof completed?

Pass a webhook with the stamp request and you get one signed POST per batch when it completes or fails. Otherwise poll GET /v1/stamps/{id}, or call /v1/verify at read time, which completes a pending proof and returns the upgraded .ots when the calendars have caught up.

Is the signed receipt enough on its own?

No. The receipt is our statement that we received the hash at a time, signed with our key; it depends on trusting us. The .ots proof is the part that stands alone, because it is checked against Bitcoin rather than against our signature.

Checked October 2026. Something out of date? Tell us and we will fix it. This page is also served as markdown at /guides/agents.md.