Answers · agent payments
How do I record the inputs an agent used for a payment or identity decision?
Last updated
TL;DR: Hash the exact inputs the agent saw when it decided to pay or to trust an identity, and store that receipt where the agent cannot edit it. For an x402 payment, that means the Payment Required fields (resource, network, asset, amount/max amount, payTo, and any identity extension), the counterparty identity handle you resolved (for example an ERC-8004 agentRegistry + agentId and the registration file at a stated block), and the policy or human approval that allowed the spend. ERC-8004 treats payments as orthogonal to its identity/reputation registries but shows how an off-chain feedback file can carry a proofOfPayment (from, to, chainId, txHash) and how its Validation Registry takes a requestURI plus keccak256 requestHash of the payload a validator needs — including inputs. Chat history and the agent's scratch folder are the wrong place: a later turn can rewrite them.
What should I check, step by step?
1. Decide which decision you are proving: "pay this x402 requirement" or "accept this agent identity / reputation signal," and list the input fields that would change the yes.
- Capture the raw Payment Required (or identity registration) bytes the agent actually received — not a paraphrased summary — and store sha256 (or keccak256 when you follow an on-chain scheme) of that byte string.
- For identity: record agentRegistry, agentId, the agentURI/registration file hash, the block or fetch time you read it at, and whether endpoint-domain verification succeeded. ERC-8004's optional domain check is a separate fact from "the NFT exists."
- For payment: record network, asset, amount (or upto max), payTo, resource/origin, scheme, any facilitator, and the idempotency or payment-identifier the client will reuse on retry.
- Bind the spend authorization or human approval to those hashes (amount, payTo, resource, identity ids). Re-check the hashes immediately before signing; if the 402 or registration file changed, ask again.
- Write the receipt outside the agent's writable session: server DB, append-only ledger, or a checkpointer the agent's tools cannot UPDATE. Give the agent only a receipt id.
- After settlement, attach the chain tx hash (ERC-8004's proofOfPayment shape) to the same decision id so payment outcome and decision inputs stay one record.
- Log denials and mismatches (payTo ≠ registered agentWallet, endpoint unlisted, policy deny) the same way you log allows — a missing deny is how silent retries rewrite history.
What "inputs" means for payment vs identity
A payment decision's inputs are the price quote and destination the agent accepted: x402's Payment Required body or header fields, plus any client-side caps (maxAmountPerPayment, cumulative spend) and the approval or spend policy that said yes.
- An identity decision's inputs are what you read about the counterparty before trusting or paying it: ERC-8004's globally unique (agentRegistry, agentId), the registration file (endpoints, x402Support, supportedTrust), optional endpoint-domain verification, and any reputation or validation summary you filtered by reviewer set (the ERC warns unfiltered summaries are open to Sybil/spam).
- ERC-8004 explicitly leaves payments to other protocols such as x402, and shows proofOfPayment inside off-chain feedback and a Validation Registry that commits to requestURI contents with requestHash — that is the same pattern as "hash the inputs, store the receipt."
Keep the record outside the agent's editable session
Anything in chat memory, AGENTS.md, or a workspace JSON file can be overwritten by the next turn or a prompt injection that claims "already approved."
- OpenAI's Agents SDK human-in-the-loop guidance keeps RunState snapshots in application-controlled server storage and never accepts replacement tool calls or serialized state from the browser client as authorization.
- LangGraph's persistence docs treat InMemorySaver as non-durable; production approvals belong in PostgresSaver/SqliteSaver (or an external ledger) on a thread id your payment runner owns.
- Practical pattern: payment runner loads receipt by id, verifies input hashes still match the 402/registration it is about to use, then signs; on mismatch it refuses and opens a new approval.
What can go wrong?
Only the tx hash is logged after the fact, with no hash of the Payment Required the agent signed for — you cannot tell whether payTo or amount was swapped mid-flight.
- Identity was copied from a directory UI into the prompt; the agent paid a different payTo than the registered agentWallet with no recorded mismatch check.
- The approval flag lives as approved=true in the agent's session and gets reused for a larger amount or a new resource.
- Retries mint a new payment authorization without consulting the original decision receipt (use one idempotency / payment-identifier per decision).
How do I ask Rightshold about this (free, no key)?
Add the MCP server https://www.rightshold.com/mcp to Claude, Cursor, ChatGPT, Grok or VS Code, then ask: “Record a signed receipt for this agent payment decision: hashes of the x402 Payment Required body and of the counterparty identity registration I checked, the amount and payTo I am allowing, and when the decision was made.”
{
"tool": "record_ai_run",
"arguments": {
"model": "<model or agent id that proposed the payment>",
"provider": "<runtime or framework>",
"prompt_sha256": "<sha256 of the Payment Required bytes + identity registration file>",
"output_sha256": "<sha256 of the allow/deny decision object>",
"params": {
"network": "eip155:8453",
"asset": "USDC",
"amount_minor": 30,
"pay_to": "0x…",
"resource": "https://api.example.com/v1/data",
"agent_registry": "eip155:8453:0x…",
"agent_id": "20030",
"decision": "allow"
},
"ran_at": "2026-11-06T18:00:00Z"
}
}- Needs an API key. You can send hashes only; if you send text as prompt/output, Rightshold stores only SHA-256. The receipt is Ed25519-signed, timestamped, and written to the append-only ledger.
- authorize_spend (with set_spend_policy) records a signed allow/deny against per-purchase and daily caps for the same purchase; put the decision id or idempotency key in reference.
- request_human_approval stores the yes/no outside the agent session and binds it to a hash of the action; verify_human_approval fails if amount or payTo changed.
- How to keep an agent risk/approval record outside the agent's editable session and How to stop an AI agent from paying twice when it retries cover the storage and idempotency pieces.
Rightshold records signed receipts and spend/approval decisions for agents that call it. It does not submit x402 payments, read ERC-8004 contracts for you, or stop an agent that never asks. Keep framework state and checkpointers in storage the agent cannot edit. Not legal advice.
What are the sources?
- ERC-8004: Trustless Agents (draft) — Identity, Reputation, Validation; payments orthogonal; proofOfPayment example
- x402: HTTP-native payments for agents and APIs
- x402 Foundation issue: Support ERC-8004 for payment-based reputation
- OpenAI Agents SDK (JS): Human-in-the-loop (keep approval state on the server)
- LangGraph: Persistence (checkpointers)
- How to read an x402 Payment Required response as a price (Rightshold)
Related
- How to keep an agent risk/approval record outside the agent's editable session
- How to stop an AI agent from paying twice when it retries
- How to require human approval before an AI agent spends money
- How to read an x402 Payment Required response as a price
Not legal advice. JSON · Markdown · All answers
Get a free API key in one call
No card and no email needed. 100 free credits; the license and policy checks stay free without a key.
curl -s -X POST https://www.rightshold.com/v1/accounts/signup -H "Content-Type: application/json" -d '{"accept_terms":true}'
export RIGHTSHOLD_API_KEY=rs_... # the api_key from the reply (shown once)
curl -s https://www.rightshold.com/v1/licenses/check -H "Authorization: Bearer $RIGHTSHOLD_API_KEY" \
-H "Content-Type: application/json" -d '{"license":"MIT","intended_use":{"commercial_use":true}}'
