Repository navigation
Dispute evidence for agent payments (ext-disputes) #217
Description
Problem
When an AI agent makes a purchase that doesn't match user intent, no existing chargeback reason code covers it. The payment was authenticated, authorized, and completed — no fraud occurred. But the agent acted outside the scope the user granted.
Traditional chargeback codes (Visa 13.x, Mastercard 48xx) assume a human authorized a transaction. Agent commerce introduces a new failure mode: the agent was legitimately authorized to transact, but the specific purchase fell outside the authorized scope.
Opportunity
The v2 grant model (#179) creates the opening to close this gap. A grant carries the authorization scope (aud, scope, constraints, exp), and the receipt's ack binding ties each payment back to the grant that authorized it. Both legs of the evidence — what was authorized and what happened — are now signed artifacts with a cryptographic binding between them.
What's missing is a machine-readable format for the mismatch between them, and a verification path a third party can walk without callbacks to either side.
Sketch
A dispute+jwt artifact (registered in core's Section 9 table) signed by the disputant (the grant issuer). It embeds the grant and receipt in full, and carries a structured delta — which grant constraint was violated, the authorized value, and the actual value. A resolver verifies mechanically: extract the named field from each embedded artifact and confirm the values don't match.
Four reason codes, all mechanically verifiable from the embedded artifacts:
scope-exceeded— amount, recipient, or constraint violationgrant-expired— grant'sexppreceded the receipt'siataudience-mismatch— payment to a counterparty not in the grant'saudunauthorized-agent— receipt names a different agent than the grant authorized
Codes intentionally excluded (can't be mechanically verified from the artifacts alone): category mismatch, revoked grants, no-grant scenarios.
Full proposal
A complete working draft with spec, diagrams, claim tables, verification checklist, security considerations, and a running jose example:
agent-payments-research/dispute-resolution
This proposal is part of a cross-protocol effort — the same evidence architecture has been adapted for MPP and x402.
Dependencies
This depends on the v2 grant model and receipt ack binding from #179. The spec is written against v2's artifact shapes — it can't land before core does. Happy to restate once the claim shapes settle.
Relationship to open decision #2
The pay core RFC's open decision #2 asks whether buyers should retain offers. This extension strengthens the case for retention: without the embedded payment request, a resolver can't verify amount-based deltas.
This issue was prepared with AI assistance (Claude Code).
Activity
ak68a commentedon Sep 21, 2026
I've started building the runtime enforcement side of this since it's out of scope for the ACK project itself.
The dispute evidence extension proves scope violations after the fact. The missing complement is something that prevents them at runtime imo a policy engine that evaluates each payment against the user's constraints before the agent executes.
Early release is at ack-policy (https://github.com/ak68a/ack-policy). It uses PaymentOption from the published SDK. Thought it might be a useful extension to the ack ecosystem.
Flyingmiata-droid commentedon Oct 15, 2026
Coming at this from the card side. I do gateway support for small merchants, and the disputes they see run through Visa and Mastercard reason codes, not a resolver. Your delta shape (constraint, authorized value, actual value) is right. What card merchants are missing is the mapping: which of your four codes lands under a fraud code and which under not as described today, and what the merchant has to have stored for the artifact to count as evidence. I want to write that mapping as an open doc and link it here. Would that help the ext-disputes work, or is someone already on it?
ak68a commentedon Oct 15, 2026
@Flyingmiata-droid Nobody's working on that mapping and it would fill a gap.
The four reason codes in the proposal (scope-exceeded, grant-expired, audience-mismatch, unauthorized-agent) are all tier 1 (mechanical, verifiable from the artifacts alone). Mapping them to Visa/MC reason codes would validate whether the delta format actually produces what a merchant needs to submit as evidence today.
The part i'd be most curious about is what does the merchant have to have stored for the artifact to count as representment evidence? If the answer is "a signed grant with the constraint that was violated, plus the receipt showing the actual value," that's exactly what the extension produces. If the answer is "you also need X and we don't have X," that's a gap.
If you want to write it as a standalone doc and link it here, that works. Or if it's easier as a PR against the research repo (ak68a/agent-payments-research), i can point you to the right directory.
Flyingmiata-droid commentedon Oct 15, 2026
Wrote it up against the April 2026 Visa rulebook, rule IDs included. It's the "you also need X" case, three times:
Also: dispute+jwt is the cardholder's exhibit. The merchant's exhibit is the same check with zero deltas, and Visa already makes the issuer pay for an agent purchase made per the cardholder's instructions (ID 0006558).
Would you take a PR that makes zero-delta a named resolver output?
https://gist.github.com/Flyingmiata-droid/354dcf989cdfbf786408c217236eef4e
ak68a commentedon Oct 17, 2026
@Flyingmiata-droid The three gaps land with rule IDs, which is what was missing from the extension's card-network.
The grant doesn't carry the cardholder info Visa wants (name, address, email per ID 0031170). Even in
fulldisclosure mode (#3693), the grant has authorization scope (constraints, expiry, audience), not PII. The merchant sees the constraint boundaries but still doesn't have the checkout details Visa requires. That data probably needs to come from whatever checkout flow the agent uses, separate from the grant.CE 3.0 device fingerprinting assumes the cardholder is the one making the request, but agent commerce breaks that by design. The agent's IP and device are all that exist for the transaction. I think this is worth documenting as a known incompatibility rather than trying to route around it. An agent-mediated transaction can't produce cardholder device signals because there is no cardholder device in the loop.
The 90-day ceiling is a spec fix. Section 4.2 says evidence
expSHOULD be no more than 90 days afteriat. If Visa's fraud dispute window runs 120 days, evidence can expire before the window closes. Either bump the SHOULD to 120 or drop the ceiling and let the issuer set it based on the applicable dispute window.Zero-delta as a named output but Section 4.3 requires one or more delta entries. A zero-delta artifact would be the merchant's representment evidence: same structure, same verification path, but proving compliance instead of violation. So maybe a separate type (
compliance+jwt) makes more sense than relaxing the delta requirement, so resolvers can tell "this is a dispute" from "this is a defense" by type alone.Would take a PR on the research repo (ak68a/agent-payments-research,
dispute-resolution/directory) for the Visa/MC mapping doc. For the zero-delta output, probably best to start it as a design question on this thread since it changes the extension's output model.ID 0006558 (issuer liability for authorized agent purchases) connects to an open point on #179. If Visa already assigns liability based on whether the agent was authorized, the grant is the artifact that proves it. One more reason the
ackbinding in receipts should be MUST within the ACK-Pay profile.Flyingmiata-droid commentedon Oct 17, 2026
Will do. I'll open the PR on agent-payments-research with the Visa mapping under dispute-resolution/.
Agree on the 90 day ceiling. Drop it and let the issuer's window set expiry, since a fixed number will be wrong for some network the day it ships.
On zero-delta, a separate compliance+jwt type is the cleaner road. A resolver should know defense from claim by type alone, without opening the payload.
One file for the mapping, or one per reason code?