| Internet-Draft | Agent Decision Commitments | October 2026 |
| Milan | Expires 18 April 2027 | [Page] |
- Workgroup:
- Network Working Group
- Internet-Draft:
- draft-milan-agent-decision-commitment-00
- Published:
- Intended Status:
- Experimental
- Expires:
Agent Decision Commitments for Payments
Abstract
Software agents increasingly select and initiate payments on behalf of people and organizations. Existing agent payment protocols record what a principal authorized and that value moved, but not what information the agent relied on when it chose. This document defines the Agent Decision Commitment (ADC): a salted hash commitment to a decision record that identifies the content the agent provided to the model interface (after any agent-side transformation), the model calls made, and the resulting answer and action. The commitment is carried as a single selectively disclosable claim in an existing payment mandate or payment payload and is, by default, registered with a transparency service before the payment is authorized. The decision record and its content remain with the holder and are disclosed only to dispute participants, who can verify them against the mandate and the transparency service and can inspect the content the agent provided to the model. This document defines the data model, the commitment and anchoring procedures, a disclosure and verification procedure that reports attestation levels rather than verdicts, and bindings to three agent payment protocols and to account-to-account credit transfers. It also defines a witnessed reciprocal exchange by which two parties' agents evidence what passed between them: the receiver signs over the committed bytes, independent witnesses fix the time of each step, a failure to answer by a deadline is recorded, and arbiters resolve stalled exchanges.¶
About This Document
This note is to be removed before publishing as an RFC.¶
This document is an individual submission. Discussion is invited on the DISPATCH mailing list (dispatch@ietf.org), and on the SCITT (scitt@ietf.org) and OAuth (oauth@ietf.org) working group lists, since this document uses the SCITT architecture [RFC9943] and SD-JWT [RFC9901].¶
Status of This Memo
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 18 April 2027.¶
Copyright Notice
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
1. Introduction
Payment agents increasingly choose rather than merely execute: which bill to pay, which merchant to buy from, whether a request is legitimate. Mandate formats such as Verifiable Intent [VI] and the Agent Payments Protocol [AP2] bind a principal's authorization to an agent key. Merchant-signed checkout objects record what was bought, and payment receipts and settlement record that value moved. None of these records what the agent relied on when it chose.¶
That question matters in two recurring situations. The first is the authorized but wrong purchase: a payment that satisfies the principal's signed constraints but results from a misread invoice or a fraudulent payee that fits the budget. The second is the manipulated agent: content the agent read, including text embedded in images, has been shown to redirect deployed agents [TOB] [BRAVE]. Media is a particular problem because model providers may resize images or sample video frames before the model sees them, and an attack can be constructed to appear only after such resampling [TOB]. A log of original files is therefore not a record of what the model received. This document records the bytes the agent sent to the model interface; transformations applied by a provider after receipt are visible only through provider attestation (Section 6.5).¶
This document defines a minimal mechanism. The holder of the agent's records computes a decision record that identifies, by salted digests, every item provided to the deciding model, together with the model calls, the answer and the action. The holder commits to the record with a salted hash, places the commitment in the payment mandate or payload, and, by default, registers it with a transparency service [RFC9943] before the payment is authorized. In a dispute, the holder discloses the record, the salts and the content to authorized verifiers, who check them against the mandate and the transparency service. Where the agent's decision depends on what a counterparty sent, or a counterparty is to acknowledge what the agent sent, the two parties use the witnessed reciprocal exchange (Section 8), so that what passed between them is evidenced as well.¶
1.1. Scope
This document specifies the decision record, the context receipt, the commitment, the holder chain and anchoring, outcome and erasure entries, disclosure and verification, bindings, and the witnessed reciprocal exchange between parties. It does not specify how an agent decides, how content is selected for a model, how dispute participants are credentialed, or what consequence follows from a verification result.¶
1.2. Liability Neutrality
ADC produces evidence. It does not allocate liability among principals, holders, merchants, issuers, acquirers, networks, or model providers, and it does not define the consequence of a missing, withheld, or failed record. Those consequences are matters for the applicable scheme rules, contracts, and law. Verifiers report the attestation level and step results defined in Section 6.5, not a verdict.¶
1.3. What ADC Does Not Establish
ADC establishes the integrity and timing of evidence, not its truth. It does not establish that recorded inputs were authentic or accurate, that the model's answer was correct, that the action reflected the principal's intent beyond what the mandate states, which item caused an outcome, that the context receipt is complete, or that the holder recorded honestly at decision time. ADC imports no facts about the world and therefore relies on no external oracle; facts enter only as recorded content whose stated origin a verifier can assess. Every verification step is a finite computation over released data (hash recomputation, signature verification, and comparison) and terminates. ADC does not evaluate properties of the agent's program or behavior.¶
1.4. Requirements Language
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
2. Terminology
- Agent Decision Commitment (ADC):
- The mechanism defined in this document, comprising the decision record, context receipt, commitment, anchoring, and disclosure procedures.¶
- Agent:
- Software that selects and initiates a payment on behalf of a principal.¶
- Principal:
- The person or organization on whose behalf the agent acts.¶
- Holder:
- The party that stores decision records and context and responds to disclosure requests; typically the operator of the agent.¶
- Verifier:
- A dispute participant (for example an issuer, acquirer, merchant, network, or arbiter) that checks a disclosed record.¶
- Context item:
- One byte sequence provided to a model for a decision, after all agent-side transformation (rendering, resizing, frame sampling, optical character recognition, transcription), or the original from which such an item was derived.¶
- Item descriptor:
- A JSON object that identifies a context item by content digest and describes its media type, role, origin, and any transformation.¶
- Context receipt:
- A JSON object listing the salted digests of all item descriptors for one decision. Not to be confused with a mandate receipt or a transparency receipt.¶
- Decision record:
- A JSON object binding a context receipt, the question asked, the model calls, the answer, the action, and the transaction.¶
- Commitment:
- The salted hash of a decision record defined in Section 5.4.¶
- Decision context claim:
- The JSON object carrying a commitment inside a mandate or payment payload, registered as the JWT claim "decision_context".¶
- Mandate receipt:
- A receipt defined by a payment protocol, such as the AP2 Checkout Receipt.¶
- Transparency service, signed statement:
- As defined in [RFC9943].¶
- Transparency receipt:
- A Receipt as defined in [RFC9943], called "transparency receipt" here to distinguish it from mandate receipts and context receipts.¶
- Exchange:
- The witnessed reciprocal exchange of a payload between a sender and a recipient defined in Section 8.¶
- Witness:
- A service, independent of the parties, that countersigns the hash of an exchange object with its own time (Section 8.3).¶
- Arbiter:
- One of the parties named in an offer that hold shares of the exchange key and resolve stalled exchanges (Section 8.5). Not to be confused with a dispute arbiter acting as verifier.¶
- Rail:
- The payment protocol or scheme that carries the payment.¶
- Covered decision:
- A decision of a type that the holder's profile designates as subject to this document.¶
- Profile:
- A set of choices made by a community of holders and verifiers (for example a payment scheme or a dispute program): the decision types covered, the transparency services and their trust anchors, the maximum anchoring interval, the credentials accepted for disclosure requests, the request and release transport, retention periods, and the attestation levels accepted for each purpose. A claim or record identifies its profile by URI in the "profile" member.¶
Abbreviations: JCS, JSON Canonicalization Scheme [RFC8785]; NFC, Unicode Normalization Form C [UAX15]; SD-JWT, Selective Disclosure for JWTs [RFC9901]; CDDL, Concise Data Definition Language [RFC8610].¶
3. Roles and Responsibilities
| Role | Responsibilities in this document |
|---|---|
| Holder | Records context items, computes receipts, records and commitments, maintains the holder chain, registers anchor and entry statements, stores content for the retention period, authenticates disclosure requests, releases evidence under the principal's authorization policy. |
| Agent | Places the decision context claim in the mandate or payload it signs or submits. |
| Principal | Sets the authorization policy that governs disclosure; may access its own records. |
| Transparency service | Registers signed statements and issues transparency receipts that bound when a commitment existed. |
| Model provider | Optionally attests to model calls (Section 6.5, level A3). |
| Verifier | Requests disclosure under a credential accepted by the holder's profile, runs the procedure in Section 6.4, and reports results. |
| Counterparty | In an exchange, signs held, key_received, received, mismatch, accepted, or refused over exactly the committed bytes (Section 8). |
| Witness | Countersigns exchange object hashes with its own time; sees hashes only. |
| Arbiter | Holds an escrowed share of an exchange key and delivers the key when an exchange stalls; never acknowledges or accepts on the recipient's behalf. |
| Merchant, issuer, acquirer, network | Carry and present the mandate according to the rail's own rules; no new obligations are defined for them. |
4. Overview
Holder/Agent Transparency Rail / Merchant
| Service |
1. record items, compute | |
receipt, record, | |
commitment C | |
2. register anchor(C) ------>| |
|<-- transparency receipt |
3. mandate/payload carrying decision_context(C) -->|
| | |
... later, a dispute ... | |
| | Verifier |
|<-------- 4. disclosure request (credential) |
5. release record, salts, | |
receipt, items ---------------------------------> 6. verify:
| | mandate, C,
|<---------------------- inclusion/time check - receipt,
| | items, level
5. Data Model
5.1. Conventions
- All objects are JSON [RFC8259]. Before hashing, every string member name and value MUST be normalized to NFC [UAX15], and the object MUST be serialized with JCS [RFC8785]. The result is denoted CANON(object).¶
- Digests are SHA-256 [SHS] unless the object's "alg" member states otherwise, and are encoded as base64url without padding [RFC4648]. DIGEST(x) denotes BASE64URL(SHA-256(x)), where x is CANON(x) when x is a JSON value and the octets themselves otherwise. Verifiers MUST reject objects whose "alg" value they do not support.¶
- Every salt defined by this document is exactly 32 octets from a cryptographically secure random number generator, fresh for each use. (SD-JWT disclosure salts are governed by [RFC9901].) The fixed length makes the concatenation salt || CANON(object) unambiguous.¶
- Timestamps are strings in the format of [RFC3339] with a "Z" offset.¶
- Each top-level object (receipt, record, statement payload, release) carries "type" and "v"; the decision context claim carries "v"; nested objects carry neither. This document defines version 1. A verifier that does not support a version MUST report the object as unsupported.¶
- Unknown members MUST be included when an object is canonicalized and hashed, and MUST be ignored when interpreting it. An object that contains duplicate member names, before or after NFC normalization, MUST be rejected.¶
- Amounts are strings containing a non-negative decimal integer in the smallest unit of the asset on the rail (minor units for fiat currencies, base units for tokens), so that large values survive JCS.¶
- Numbers are canonicalized by JCS, which can change their textual form. Where the exact bytes returned by a model provider matter, they are recorded as a context item with role "response" (Section 5.2).¶
5.2. Context Items and the Context Receipt
For each context item i, in the order in which items were provided to the model, the holder constructs an item descriptor D_i and a fresh salt s_i, and computes the item digest:¶
item_digest_i = BASE64URL(SHA-256(s_i || CANON(D_i)))¶
An item descriptor has the following members:¶
- content_digest (REQUIRED):
- BASE64URL(SHA-256(item bytes)).¶
- media_type (REQUIRED):
- The media type of the item bytes.¶
- role (REQUIRED):
- One of "instructions", "tool_definition", "prior_turn", "as_sent", "original", "request", "response", "exchange", "witness", or an extension value in the form of a URI. Role "request" denotes the complete request body as sent to the provider. Roles "exchange" and "witness" denote an exchange object body and a witness statement (Section 8.6).¶
- calls (REQUIRED):
- The array of zero-based indexes, into the record's "models" array, of the model calls to which the item was input or, for role "response", from which it was returned; empty for an item with role "original".¶
- derived_from (OPTIONAL):
- The zero-based index of the item from which this item was derived.¶
- transform (OPTIONAL):
- A string identifying the transformation and the implementation and version that performed it.¶
- source (REQUIRED):
- An object with "kind" and, where applicable, "uri" and "fetched_at".¶
The holder MUST list every input to every model call covered by the decision, including instructions, tool definitions, prior turns, retrieved content, and media, and MUST include the raw provider response for each call with role "response". Where the agent transformed an item, the item as provided to the model has role "as_sent" and a "derived_from" member giving the index of its source item. The source item SHOULD also be listed; it has role "original" if it was not itself provided to the model, and role "as_sent" otherwise. Agents SHOULD perform media transformations themselves and stay within provider limits, so that the bytes recorded are the bytes the model received.¶
The context receipt is:¶
- type, v, alg (REQUIRED):
- "adc-receipt", 1, and the digest algorithm.¶
- items (REQUIRED):
- The non-empty array of item digests, in model-input order.¶
- item_count (REQUIRED):
- The number of items, at least 1.¶
- retention_until (REQUIRED):
- The time until which the holder commits to retain the descriptors, salts, and items (Section 12).¶
- selector (OPTIONAL):
- An object naming the component that selected the items and a digest of its configuration.¶
receipt_digest = BASE64URL(SHA-256(CANON(receipt))). Because each descriptor is hidden behind its own salt, the receipt reveals neither the content digests nor the origins of the items, and a descriptor and its salt can later be deleted without changing receipt_digest (Section 5.6).¶
5.3. Decision Record
- type, v, alg (REQUIRED):
- "adc-record", 1, and the digest algorithm.¶
- chain (REQUIRED):
- An object with "id", an opaque identifier of the holder chain that is independent of any signing key; "seq", this record's sequence number in the chain; and "prev", the commitment of the record with sequence number seq - 1, or 32 zero octets encoded in base64url for the first record of a chain (Section 5.5).¶
- receipt_digest (REQUIRED):
- As defined in Section 5.2.¶
- question (REQUIRED):
- An object with "name", "kind" (for example "choice", "score", "probability", or "freeform"), and "schema_digest", DIGEST of the question definition (a JSON object).¶
- profile (OPTIONAL):
- The URI of the profile under which the record was made.¶
- models (REQUIRED):
- An array with one entry per model call, each with "provider", "id" (a resolved model version, not an alias), "request_id", "request_digest" (DIGEST of the request body as sent to the provider), and optionally "params_digest" (digest of the decoding parameters, if the interface exposes them) and "attestation", a provider-issued attestation over the request and response, for example an Attested Inference Receipt [I-D.tsyrulnikov-rats-attested-inference-receipt], encoded as the base64url encoding of the attestation token (Section 6.5).¶
- answer (REQUIRED):
- The model output, as a JSON object.¶
- action (REQUIRED):
- An object with "taken", the action decided: "pay", "decline", or an extension value in the form of a URI. It MUST NOT reference the mandate, which does not yet exist when the record is committed.¶
- transaction (REQUIRED when the action initiates a payment):
- An object with "rail", "payee", "amount", "currency", and, where the rail defines one, "checkout_digest", each encoded and compared as defined for the rail in Section 7. "currency" is an ISO 4217 [ISO4217] code for fiat currencies and a CAIP-19 asset identifier [CAIP19] for tokens.¶
- issued_at (REQUIRED):
- The time the record was completed.¶
One decision record MUST correspond to at most one transaction. Outcomes and erasures are not part of the record; they are separate entries (Section 5.6).¶
5.4. Commitment and Decision Context Claim
C = BASE64URL(SHA-256(salt || CANON(record)))¶
The decision context claim is:¶
{"v":1,"alg":"sha-256","commitment":"<C>","receipt_uri":"<uri>",
"profile":"<uri>"}
¶
"receipt_uri" is an https URI at which disclosure requests for this record are made; its path is an opaque value chosen by the holder. It MUST NOT embed content, digests, or identifiers that link the principal across transactions. "profile" (OPTIONAL) identifies the profile; if it is absent, the verifier obtains the applicable profile out of band, and without one reports no level above A1. Rails that cannot carry the claim object carry only C, as defined in Section 7. In JWT-based mandates, the claim is carried as an ordinary SD-JWT disclosure [RFC9901]: it is not canonicalized, its member order is whatever the issuer serialized, and its digest is computed over the base64url-encoded disclosure string as RFC 9901 specifies. Verifiers MUST use the received disclosure string and MUST NOT re-serialize the claim. On rails that are not JWT-based, the claim is carried as a plain JSON object or reduced to C, as defined in Section 7. The commitment salt is distinct from any SD-JWT disclosure salt and is released only under Section 6.¶
5.5. Holder Chain and Anchoring
The holder MUST create a decision record for every covered decision, including decisions whose action is "decline", and MUST assign consecutive sequence numbers per chain. The "prev" member links each record to its predecessor, so that a missing record appears as a gap. Hash chains are common in audit-log designs (Section 9); the chain here is kept per chain id, independent of signing keys, and covers declined decisions, so that a verifier can detect a withheld decision, not only a withheld payment.¶
A chain is not tied to a signing key. The key that signs a chain's first anchor is the chain's initial key. To rotate, the holder registers a rotation statement of type "adc-rotation", signed with the current key, that names the chain and the successor key; statements signed with the successor key then continue the same chain id and sequence. Verifiers MUST consider only statements whose signing key is the chain's initial key or a successor established by such rotation statements; statements by any other issuer that use the same "sub" are ignored. The holder MUST NOT start a new chain to replace an existing one except under the profile's rules, and a new chain's first anchor MUST be reported to verifiers on request. A deletion at the tail of a chain, after the last anchor a verifier sees, is not detectable by the chain alone.¶
Unless the profile permits late registration, the holder MUST register an anchor statement with at least one transparency service [RFC9943] before the mandate or payment payload that carries the claim is signed. The anchor is a Signed Statement as defined in [RFC9943]: a COSE_Sign1 message [RFC9052] whose protected header carries a "kid" identifying the signing key (unless "x5t" or "x5chain" is present) and CWT claims [RFC8392] with "iss" identifying the holder and "sub" set to the chain id, and whose payload, of content type application/adc-anchor+json, is:¶
{"type":"adc-anchor","v":1,"commitment":"<C>","chain":"<id>",
"seq":<n>,"prev":"<C(n-1)>","issued_at":"<time>"}
¶
The holder MUST retain the transparency receipt. Level A2 requires that the registration time can be established from the receipt or from the transparency service under the profile; [RFC9943] does not itself require receipts to carry a registration time. Profiles MAY require registration with two transparency services operated by different organizations, SHOULD prefer services that provide consistency proofs, and MAY set the maximum interval between issued_at and registration (300 seconds if unspecified). Where the profile permits late registration, a holder that cannot reach a transparency service registers as soon as it can, and the anchor is reported as late (Section 6.5). Outcome, erasure, and rotation statements (Section 5.6) use the same envelope and "sub" but carry no sequence number. A batch format for anchors is out of scope.¶
Failure handling:¶
- Before registering an anchor, the holder MUST durably store the record, the salts, the items, and the sequence number it consumes. A sequence number MUST NOT be reused, including after recovery from backup; the holder MUST determine the highest sequence number already anchored before resuming a chain.¶
- If registration times out, the holder retries with the identical signed statement bytes. Verifiers treat byte-identical statements as one. Re-signing a statement for the same chain and seq produces different bytes and is reported as equivocation.¶
- If the agent does not complete an anchored action whose "taken" is "pay", for example because the process failed before the mandate was signed, the holder MUST register an outcome statement with status "abandoned".¶
- A chain is a total order. A holder that makes concurrent decisions SHOULD operate several chains; each covered decision belongs to exactly one chain.¶
- Holders keep issued_at on a synchronized clock. Verifiers allow the clock tolerance set by the profile (60 seconds if unspecified) when comparing issued_at, registration times, and mandate issuance times.¶
5.6. Outcome and Erasure Entries
After the decision, the holder can register further signed statements that reference the commitment:¶
- "adc-outcome" (OPTIONAL), with "status" (for example "settled", "disputed", "refunded", "reversed", or "abandoned"; see Section 5.5) and, when known, "reference_digest", a digest of the mandate or payment identifier. Rails and settlement-record formats may already carry this information; profiles decide whether outcome entries are used.¶
- "adc-erasure", which the holder MUST register when it deletes evidence before retention_until, listing in "items" the zero-based indexes of items whose descriptors, salts, and content were deleted, in "erased_at" the time of deletion, and in "reason" one of "principal_request", "legal_obligation", "lost", or an extension value in the form of a URI. Loss of evidence before retention_until is registered with reason "lost". Deleting a descriptor and its salt leaves receipt_digest and the commitment valid and makes the item digest unlinkable to the content, without the per-record key management of cryptographic erasure. An erasure entry records that evidence is no longer available; this document attaches no consequence to it (Section 1.2).¶
5.7. CDDL
The following CDDL [RFC8610] describes the objects. JSON encodings apply; "digest" is a base64url string, "timestamp" is an RFC 3339 string, and salts, content, nonce_random, and statements are base64url strings. Each entry of "statements" is a Transparent Statement [RFC9943]: an anchor, entry, or rotation statement with its transparency receipts. The exchange objects of Section 8 are described by adc-exchange, adc-witness, and exchange-receipt.¶
adc-receipt = {
type: "adc-receipt", v: 1, alg: hash-alg,
items: [+ digest], item_count: uint .ge 1,
retention_until: timestamp,
? selector: { name: tstr, version: tstr, ? config_digest: digest },
* tstr => any
}
item-descriptor = {
content_digest: digest, media_type: tstr, role: item-role,
calls: [* uint], ? derived_from: uint, ? transform: tstr,
source: { kind: tstr, ? "uri": tstr, ? fetched_at: timestamp,
* tstr => any },
* tstr => any
}
item-role = "instructions" / "tool_definition" / "prior_turn" /
"as_sent" / "original" / "request" / "response" /
"exchange" / "witness" / tstr
adc-record = {
type: "adc-record", v: 1, alg: hash-alg,
chain: { id: tstr, seq: uint, prev: digest, * tstr => any },
? profile: tstr,
receipt_digest: digest,
question: { name: tstr, kind: tstr, schema_digest: digest,
* tstr => any },
models: [+ model-call],
answer: { * tstr => any },
action: { taken: tstr, * tstr => any },
? transaction: transaction,
issued_at: timestamp,
* tstr => any
}
model-call = {
provider: tstr, id: tstr, request_id: tstr, request_digest: digest,
? params_digest: digest, ? attestation: tstr,
* tstr => any
}
transaction = {
rail: "vi" / "ap2" / "x402" / "iso20022" / tstr,
payee: tstr, amount: tstr, currency: tstr,
? checkout_digest: digest,
* tstr => any
}
decision-context-claim = {
v: 1, alg: hash-alg, commitment: digest, receipt_uri: tstr,
? profile: tstr,
* tstr => any
}
adc-anchor = {
type: "adc-anchor", v: 1, commitment: digest, chain: tstr,
seq: uint, prev: digest, issued_at: timestamp,
* tstr => any
}
adc-outcome = {
type: "adc-outcome", v: 1, commitment: digest, status: tstr,
? reference_digest: digest, issued_at: timestamp,
* tstr => any
}
adc-erasure = {
type: "adc-erasure", v: 1, commitment: digest,
items: [+ uint], erased_at: timestamp,
reason: "principal_request" / "legal_obligation" / "lost" / tstr,
* tstr => any
}
adc-rotation = {
type: "adc-rotation", v: 1, chain: tstr, successor_kid: tstr,
issued_at: timestamp,
* tstr => any
}
adc-request = {
type: "adc-request", v: 1, request_id: tstr, verifier: tstr,
presentation_digest: digest, issued_at: timestamp,
* tstr => any
}
adc-release = {
type: "adc-release", v: 1, request_id: tstr, verifier: tstr,
issued_at: timestamp, record: adc-record,
commitment_salt: tstr, receipt: adc-receipt,
statements: [* tstr],
items: [+ released-item / withheld-item],
? nonce_random: tstr,
* tstr => any
}
released-item = { index: uint, salt: tstr,
descriptor: item-descriptor, content: tstr }
withheld-item = { index: uint, status: "withheld" / "erased",
? erasure_reference: tstr }
adc-error = {
type: "adc-error", v: 1, ? request_id: tstr,
code: "invalid_request" / "invalid_credential" /
"replayed_request" / "not_found" / "policy_denied" /
"too_large" / "temporarily_unavailable" / tstr,
? retry_after: uint, ? description: tstr,
* tstr => any
}
adc-report = {
type: "adc-report", v: 1, verifier: tstr, verified_at: timestamp,
? profile: tstr, level: "A0" / "A1" / "A2" / "A3" / "none",
signer: "agent" / "principal" / "payer" / "none",
steps: [8*8 step-result],
inputs: { presentation: digest, release: digest,
statements: [* digest], attestations: [* digest],
trust_anchors: [* digest], revocation: [* digest] },
? late_anchor_seconds: uint, ? equivocation: bool,
* tstr => any
}
adc-exchange = {
exchange-common, exchange-step,
* tstr => any
}
exchange-common = (
type: "adc-exchange", v: 1, exchange_id: tstr, seq: uint,
prev: digest, issued_at: timestamp
)
exchange-step = (
x-offer // x-held // x-key-release // x-key-received //
x-received // x-mismatch // x-answer // x-arbiter-delivery //
x-non-ack // x-close
)
x-offer = (
step: "offer", intent: "deliver" / "offer",
sender_key: tstr, recipient_key: tstr,
payload_commitment: digest, aead: tstr,
ciphertext_hash: digest, key_hash: digest,
? escrow: adc-escrow, response_window: uint,
expires_at: timestamp
)
x-held = (step: "held", envelope_hash: digest,
? arbiters_accepted: digest)
x-key-release = (
step: "key_release",
hpke: { kem_id: uint, kdf_id: uint, aead_id: uint },
wrapped_key: tstr
)
x-key-received = (step: "key_received", key_release_hash: digest)
x-received = (step: "received", envelope_hash: digest)
x-mismatch = (
step: "mismatch", envelope_hash: digest,
check: "ciphertext_hash" / "unwrap" / "key_hash" / "decrypt" /
"payload_commitment"
)
x-answer = (step: "accepted" / "refused", envelope_hash: digest)
x-arbiter-delivery = (
step: "arbiter_delivery", envelope_hash: digest,
key_matched: bool, commitment_matched: bool,
? delivered_at: timestamp
)
x-non-ack = (
step: "non_ack", envelope_hash: digest, awaited: tstr,
attempts: [* delivery-attempt]
)
x-close = (step: "close", state: exchange-state, reason: tstr)
delivery-attempt = {
transport_digest: digest, witnesses: [+ witness-statement],
* tstr => any
}
adc-escrow = {
scheme: tstr, arbiters: [3* tstr], threshold: uint .ge 2,
shares: [3* tstr], proof: tstr,
* tstr => any
}
exchange-state = "NOT_ACKNOWLEDGED" / "VOID" /
"KEY_UNACKNOWLEDGED" / "DELIVERED_VIA_ARBITER" /
"CONTENT_UNACKNOWLEDGED" / "DELIVERED" /
"ACCEPTED" / "REFUSED" / "NOT_ACCEPTED" /
"FAULT_SENDER" / "FAULT_RECIPIENT" / "CLOSED"
exchange-receipt = {
object: tstr / { payload: tstr, signatures: [+ any] },
witnesses: [+ witness-statement],
* tstr => any
}
witness-statement = {
format: "rfc3161" / "scitt" / tstr, token: tstr,
* tstr => any
}
adc-witness = {
type: "adc-witness", v: 1, object_hash: digest,
* tstr => any
}
step-result = ("pass" / "fail" / "not_applicable" /
"not_checked" / "unsupported")
hash-alg = "sha-256" / tstr
digest = tstr
timestamp = tstr
¶
6. Disclosure and Verification
6.1. Disclosure Request
A verifier sends the holder, at "receipt_uri", the mandate presentation that carries the claim and a credential issued by the party administering the dispute. The credential types, their trust anchors, and the request transport are defined by the profile; this document defines the properties they need. The credential MUST be audience-restricted to the holder, sender-constrained (for example with DPoP [RFC9449]), short-lived, and bound to the transaction, for example by including the "sd_hash" of the presentation, so that a holder or an attacker controlling "receipt_uri" cannot replay it elsewhere. The verifier MUST dereference only https URIs, MUST apply request-forgery protections to "receipt_uri" (for example refusing private and link-local addresses and redirects to other hosts), and MUST NOT dereference "source.uri" values found in released descriptors. The holder MUST authenticate the credential, MUST log the request, and MUST limit release to the records bound to the transaction in the request.¶
The request is an object of type "adc-request" (Section 5.7) carrying "request_id", at least 16 octets from a cryptographically secure random number generator encoded in base64url; "verifier", an identifier of the verifier; "presentation_digest", the digest of the mandate presentation; and "issued_at". The holder MUST reject a request whose request_id it has already accepted within the lifetime of the credential, and a request whose issued_at lies outside the profile's tolerance (60 seconds if unspecified).¶
6.2. Release
Release is governed by an authorization policy that the principal sets in advance. When the policy permits, the holder returns a release object of type "adc-release" (Section 5.7) containing the decision record, the commitment salt, the context receipt, the anchor statement and any rotation statements needed to authorize its key, each with its transparency receipts, and, for each item, either the descriptor, its salt, and the item bytes, or a statement that the item is withheld or was erased (with a reference to the erasure entry). The release echoes the request's "request_id" and "verifier" and carries its own "issued_at", so that it cannot be presented as the answer to a different request. For the x402 binding, the release also includes "nonce_random" (Section 7.3). The holder MUST sign the release as a JWS [RFC7515] with a key that the profile associates with the holder, so that what was released, and what was withheld, is attributable. Withheld items remain listed by digest, so the omission is visible. Because the holder is typically the party whose conduct is in question, profiles MAY require the holder to deposit an encrypted copy of the release material with the principal or a custodian at decision time.¶
6.3. Transfer Confidentiality
A release contains personal data and the salts that protect it. It MUST be transferred over TLS 1.3 [RFC8446] or later, following [RFC9325], with the holder authenticated for the host named in "receipt_uri" and the verifier's credential presented within that session. If any party other than the holder and the verifier relays, queues, or stores the release, the release SHOULD additionally be encrypted to the verifier as a JWE [RFC7516]; HPKE-based JWE [I-D.ietf-jose-hpke-encrypt] and post-quantum or hybrid key encapsulation [I-D.ietf-hpke-pq] are suitable as they become available. Salts MUST NOT be sent in any message that is not confidentiality-protected. A verifier MUST NOT retain released content beyond the dispute and any retention obligation that applies to it.¶
6.4. Verification Procedure
- Verify the mandate or payload according to its own specification and extract the decision context claim.¶
- Verify the holder's signature on the release. Recompute C from the record and salt; it MUST equal the claim's commitment.¶
- Check that record.transaction matches the transaction in the mandate or payload, field by field, using the mapping and comparison rules for the rail (Section 7). For x402, check that the signed nonce equals SHA-256(BASE64URL-DECODE(C) || nonce_random); if "nonce_random" is not released, the x402 binding is level A0.¶
- Recompute receipt_digest; it MUST equal record.receipt_digest, and item_count MUST equal the length of items.¶
- For each released item, recompute its item digest from descriptor and salt and check that it appears at the stated index, then check content_digest against the bytes. For derived items, the verifier MAY re-apply the transformation; a mismatch is reported, and its significance depends on whether the transformation is deterministic.¶
- Verify each transparency receipt against trust anchors for the transparency service defined by the profile, and check that every receipt covers the same anchor statement. Check that the anchored commitment, chain, seq, and prev match the record, that the registration time is not more than the profile's interval after issued_at, and that it is not later than the issuance time of the mandate or payload (for example its "iat") where the rail provides one. Only statements signed by keys authorized for the chain (Section 5.5) are considered. The verifier MAY request from the holder the anchors for neighboring sequence numbers; [RFC9943] defines no query interface for this, so the means are set by the profile. Two different anchor statements for the same chain and seq MUST be reported as equivocation.¶
- For each model call with an attestation, verify the attestation and check that it binds the call's request_digest and the content_digest of the call's "response" item. If an item with role "request" is released for the call, check that its content_digest equals the call's request_digest.¶
- Report the attestation level and the result of each step.¶
6.5. Attestation Levels
| Level | Conditions |
|---|---|
| A0 | The record matches a commitment carried in a field not signed by the agent, the principal, or the payer. |
| A1 | The commitment is covered by the signature of the agent, the principal, or the payer (including the x402 nonce construction), and steps 2 to 5 succeed. The verifier reports which party signed. |
| A2 | A1, and step 6 succeeds: the anchor was registered in time, and the anchored chain values are consistent with the record and with any neighboring anchors obtained. |
| A3 | A2, every model call carries an attestation, and step 7 succeeds for every model call. |
A late or missing anchor caps the level at A1, and the verifier reports the delay; a timely anchor for an A0 commitment is reported alongside A0. No level asserts that the receipt lists every input to the model. At A3, a provider attestation binds what the provider received and returned; if the holder also releases the item with role "request", the verifier can check that the listed items are the parts of that request. The strength of A3 depends on the attestation format; some formats bind hashes of a request and response without proving that the response was produced from that request. Profiles define which levels they accept for which purposes.¶
6.6. Errors
A holder that does not release returns an error object of type "adc-error" (Section 5.7) with one of the following codes; the profile maps them to its transport.¶
- invalid_request:
- The request is malformed or names an unsupported version or algorithm.¶
- invalid_credential:
- The credential is missing, expired, not sender-bound, or not issued under a trust anchor of the profile.¶
- replayed_request:
- The request_id was already used, or issued_at is outside the tolerance.¶
- not_found:
- No record is bound to the presented transaction.¶
- policy_denied:
- The principal's authorization policy does not permit release to this verifier.¶
- too_large:
- The release would exceed the limits set by the profile; the profile may define partial release.¶
- temporarily_unavailable:
- The holder cannot serve the request now; the error carries "retry_after" in seconds.¶
Where distinguishing not_found from policy_denied would reveal that a record exists, the profile MAY require holders to return not_found for both. An error is not evidence about the decision; verifiers report it as received.¶
Verifiers fail closed. Each step in Section 6.4 yields one of "pass", "fail", "not_applicable", "not_checked", or "unsupported". If any of steps 1 to 5 yields "fail" or "unsupported", no level is reported. A step that is not checked never counts as passed, and partial results never raise the level.¶
6.7. Verification Reports and Their Audit
The verifier records its result as a verification report of type "adc-report" (Section 5.7). The report states the level or its absence, the result of each step, the profile, the verifier, the time of verification, and the digests of every input: the mandate presentation, the signed release, each transparent statement, each attestation, and the trust anchors and revocation information used. The verifier MUST sign the report as a JWS [RFC7515].¶
Given the same inputs, every step except the evaluation of revocation status is a deterministic computation, so any other party holding those inputs can repeat the procedure and must obtain the same result. Revocation status is evaluated as of the recorded time against the recorded sources. A verifier is therefore checked by repetition, not by another authority. Profiles MAY require verification by more than one independent verifier for a given purpose.¶
7. Bindings
The bindings below are based on the cited versions of each protocol and will need revision as those protocols change. Verifiable Intent and the Agent Payments Protocol have been contributed to the FIDO Alliance, whose Payments Technical Working Group is developing them further [FIDO-AGENTIC]. AP2 states that other properties may be included in a mandate, based on the mandate type. Verifiable Intent defines no general rule for additional claims in its Layer 3 payloads; it defines an "agent_attestation" extension container whose attestation types are not yet specified and whose unrecognized types are ignored, and a future attestation type for decision evidence could carry the commitment instead of a separate claim. The bindings rely on the JWT rule that claims not understood are ignored unless the application requires otherwise (Section 4 of [RFC7519]), and on verifying parties that choose to process the claim. Selective disclosure does not prevent a presenter from omitting the claim's disclosure; it prevents only alteration of a disclosure that is presented.¶
7.1. Verifiable Intent
In Autonomous mode, the agent includes "decision_context" as an additional selectively disclosable claim in both the Layer 3a payment payload and the Layer 3b checkout payload [VI], with the same claim value, so that its disclosure digest appears under the agent's signature in what the network and the merchant each receive. Payee and amount are compared against Layer 3a and checkout_digest against Layer 3b. Verifiable Intent does not profile additional claims of this kind; a holder that presents the payload to a party that does not process the claim can omit its disclosure. The claim MUST NOT be expressed as a constraint, because unknown constraint types in open mandates are rejected. In Immediate mode no agent-signed layer exists and the principal confirms the final values; the claim MAY be placed in the principal-signed Layer 2 mandate only if the signing interface shows the principal that a decision commitment is included, and the verifier reports the principal as signer. checkout_digest is the Verifiable Intent "checkout_hash" value.¶
7.2. Agent Payments Protocol
The agent includes "decision_context" as a selectively disclosable claim of the closed Checkout Mandate ("mandate.checkout.1") [AP2]. The merchant-signed Checkout Receipt's "reference" covers the commitment only if the claim's disclosure is included in the presentation the merchant receives. The claim MUST NOT appear in open mandates or constraints. In human-present mode the principal signs the closed mandate, and the claim SHOULD be included only if the trusted surface shows the principal that a decision commitment is included. The claim MAY also be included in the Payment Mandate, which carries the payee, payment instrument, and amount to the payment processor and issuer; payee, amount, and currency are compared against the Payment Mandate where present and otherwise against the checkout object bound by "checkout_hash". Implementations MUST compute digests over the raw disclosure strings, since some libraries discard unknown members when parsing. checkout_digest is the closed mandate's "checkout_hash".¶
7.3. x402
The server advertises the extension "adc" in its payment-required response with an empty "info" object and a schema; the client echoes the extension in its payment payload and appends the claim as "info.decision_context", which the x402 extension rules permit (clients may append to, but not alter, the server's info) [X402]. The payer's EIP-3009 or Permit2 signature does not cover extensions. To bring the commitment under the payer's signature, the client SHOULD set the 32-octet nonce to SHA-256(BASE64URL-DECODE(C) || r), where r is 16 random octets that the holder retains with the record and returns, as "nonce_random", in the release (Section 6.2); the 32-octet value is encoded as the payment scheme requires (for example as a decimal string for Permit2). Without this construction, the binding is level A0. checkout_digest is BASE64URL(SHA-256(CANON(accepted payment requirements))). Payee is the "payTo" value and amount the authorized value in base units; currency is the CAIP-19 identifier [CAIP19] of the asset, lowercased before use. Hexadecimal addresses are compared case-insensitively. Because payment requirements can repeat, reuse of a claim is detected by the nonce construction and the holder chain, not by step 3 alone.¶
7.4. Account-to-Account Credit Transfers
For ISO 20022 credit transfers [ISO20022], including instant schemes, the holder places "ADC1:" followed by the lowercase hexadecimal encoding of the 32 octets of the commitment in the unstructured remittance information (69 characters), where the scheme and the payer's provider permit payer-supplied remittance text. Hexadecimal is used because some schemes restrict remittance text to a limited character set. No receipt_uri is carried; the verifier obtains it from the payer's provider or the principal under the profile. Payee is the creditor account identifier, amount the instructed amount in minor units, and currency its ISO 4217 code. checkout_digest is BASE64URL(SHA-256(end-to-end identifier)) when that identifier is known before submission and is otherwise absent. This field is not signed by the agent, so the binding is level A0, anchoring (Section 5.5) provides the time bound, and reuse is detected only through the holder chain.¶
7.5. Card Authorization Messages
Card authorization and authentication messages do not carry the claim. An issuer can obtain it only through a mandate presented by the rail's agent program. This document defines no binding for authorization messages.¶
8. Witnessed Reciprocal Exchange
A decision record proves what one party's agent relied on. This section defines how two parties, for example an agent and a merchant, two agents, or an agent and a person, evidence what passed between them. The receiver signs over exactly the committed bytes. Independent witnesses, procured and carried by each party's agent, fix the time of every step with their own clocks. A failure to answer by a deadline becomes evidence in its own right, and arbiters resolve stalled exchanges.¶
The result is tamper-evident, not tamper-proof. Fair exchange between two parties without a trusted third party is impossible [PAGNIA], so this section follows the optimistic model [ASOKAN]: witnesses take part in every exchange, and arbiters take part only in stalled ones. The exchange is OPTIONAL; a holder that does not exchange payloads with counterparties under this document need not implement it.¶
8.1. Common Rules
- Each exchange object has a body: a JSON object with "type" set to "adc-exchange", "v" set to 1, and the members defined below. The body is signed as a JWS [RFC7515] whose payload is CANON(body) and whose protected header carries "alg", "kid", and "typ" set to "adc-exchange+json". The object hash is object_hash = DIGEST(CANON(body)); it does not depend on the signature.¶
- "alg" is "Ed25519" [RFC9864] or "ES256" [RFC7518]. ES256 signatures MUST be in low-S form, with s no greater than half the order of the group; verifiers MUST reject ES256 signatures whose s is greater. Verifiers MUST reject other algorithms unless the profile permits them.¶
- Every body carries "exchange_id", an opaque string chosen by the sender and unique for the pair of sender and recipient keys; "seq", 0 for the offer and one more than the previous object otherwise; "prev", the object_hash of the previous object, or 32 zero octets encoded in base64url for the offer; "step", the kind of object; and "issued_at". One exchange is one linear chain.¶
- The offer identifies the parties by "sender_key" and "recipient_key", the JWK Thumbprints [RFC7638] of their signing keys. Objects signed by any other key are not part of the exchange, except witness statements (Section 8.3) and arbiter_delivery (Section 8.5).¶
- Equivocation is two different bodies signed by the same key with the same exchange_id and seq. Different signatures over the same body are not equivocation. A verifier that observes equivocation reports it and attributes it to the holder of the signing key.¶
- Deadlines and ordering are judged by witness time (Section 8.3), not by issued_at or by either party's clock. Where objects signed by different parties carry the same seq, the one with the earlier witness time is part of the chain and the other is void.¶
8.2. Objects
| step | Signed by | Members in addition to the common members |
|---|---|---|
| offer | Sender | intent, sender_key, recipient_key, payload_commitment, aead, ciphertext_hash, key_hash, escrow (conditional), response_window, expires_at |
| held | Recipient | envelope_hash, arbiters_accepted (when escrow is present) |
| key_release | Sender | hpke, wrapped_key |
| key_received | Recipient | key_release_hash |
| received | Recipient | envelope_hash |
| mismatch | Recipient | envelope_hash, check |
| accepted, refused | Recipient | envelope_hash; valid only after received for the same envelope, and only when intent is "offer" |
| arbiter_delivery | M arbiters | envelope_hash, key_matched, commitment_matched, delivered_at |
| non_ack | Sender | envelope_hash, awaited, attempts |
| close | Either party | state, reason |
- intent:
- "deliver", when the sender seeks proof of receipt, or "offer", when the sender also seeks acceptance.¶
- Payload and payload_commitment:
- The payload is a JSON value. The sender chooses a fresh 32-octet salt s and computes payload_commitment = BASE64URL(SHA-256(s || CANON(payload))), the construction of Section 5.4. The plaintext is CANON({"payload": payload, "salt": BASE64URL(s)}). A payload that is not JSON is carried as an object with "media_type" and "content", the base64url encoding of its octets.¶
- Ciphertext, aead, ciphertext_hash, and key_hash:
- The sender chooses a fresh 256-bit key K, used for one exchange only, and encrypts the plaintext with the AEAD algorithm named in "aead" ("A256GCM" [RFC7518] unless the profile permits others), a fresh random 96-bit nonce, and, as associated data, the UTF-8 octets of exchange_id. The ciphertext is the nonce followed by the AEAD output. ciphertext_hash = DIGEST(ciphertext) and key_hash = DIGEST(K).¶
- Envelope and envelope_hash:
- The envelope is the signed offer together with the ciphertext. envelope_hash is the object_hash of the offer.¶
- hpke and wrapped_key:
- K encrypted to the recipient with HPKE [RFC9180] in base mode, with info set to the UTF-8 octets of "adc-exchange" followed by the 32 octets of envelope_hash, and empty associated data. wrapped_key is the base64url encoding of the encapsulated key followed by the HPKE ciphertext; "hpke" carries "kem_id", "kdf_id", and "aead_id". How the sender obtains the recipient's HPKE public key is a matter for the profile.¶
- key_release_hash:
- The object_hash of the key_release. key_received acknowledges that the key_release arrived; it does not attest that K is correct.¶
- escrow:
- An object with "scheme", a URI identifying a publicly verifiable secret sharing scheme and its encoding, for example one based on [SCHOENMAKERS]; "arbiters", the array of N arbiter identifiers; "threshold", M; "shares", the N encrypted shares in base64url, each encrypted to one arbiter; and "proof", the base64url encoding of the proof that the shares are consistent, which any party can verify. Where the scheme shares a group element rather than an octet string, K is derived from the shared secret with HKDF [RFC5869] as the scheme specifies. escrow MUST be present when intent is "offer" or when the exchange carries a payment obligation, and then MUST have M at least 2 and N at least 3, with arbiters operated by different organizations.¶
- arbiters_accepted:
- DIGEST(CANON(escrow)), sent only after the recipient has verified the proof.¶
- response_window and expires_at:
- response_window is a number of seconds. The deadline for each expected object is the witness time of the preceding object plus response_window, and is never later than expires_at.¶
- check:
- The check that failed: "ciphertext_hash", "unwrap", "key_hash", "decrypt", or "payload_commitment".¶
- awaited and attempts:
- awaited is the step that did not arrive by its deadline. attempts lists the delivery attempts, each with "transport_digest", a digest of the transport receipt for the attempt, and the witness statements obtained at the time of the attempt.¶
- key_matched, commitment_matched, and delivered_at:
- The results of the arbiters' checks (Section 8.5) and, when K was delivered, the time of delivery.¶
- state and reason:
- The terminal state that the closing party asserts (Section 8.4) and a human-readable reason.¶
8.3. Witnesses
A witness is an independent service that countersigns an object_hash with its own time. Witnesses see hashes only, never content. A witness statement takes one of two forms:¶
- "rfc3161":
- A TimeStampToken [RFC3161] whose messageImprint uses SHA-256 and whose hashedMessage is the 32 octets of object_hash, that is, SHA-256(CANON(body)). Qualified electronic time stamps issued by qualified trust service providers [EIDAS] take this form.¶
- "scitt":
- A Transparent Statement [RFC9943]: a Signed Statement, in the envelope of Section 5.5, whose payload, of content type application/adc-anchor+json, is the witness payload shown below, with its transparency receipt. The witness time is the registration time, established as for level A2 (Section 5.5). The "sub" of such a statement MUST NOT link different exchanges.¶
{"type":"adc-witness","v":1,"object_hash":"<object_hash>"}
¶
Each party's agent procures witness statements for every object it signs and for every object it receives, and carries them with the object as an exchange receipt: {"object": the signed object, "witnesses": [{"format", "token"}, ...]}. A receipt for a step is therefore three things together: the object, the counterparty's signature on the object that answers it, and the witness statements. Neither party needs to trust the other's clock or storage.¶
At least one witness MUST be independent of both parties. Exchanges that carry a payment or other obligation SHOULD use two witnesses operated by different organizations. The witness time of an object is the earliest time among its valid witness statements from witnesses accepted by the profile. Witness time is authoritative for deadlines and for ordering across parties. The anchor of Section 5.5 is itself a witness statement over the decision commitment.¶
A witness statement proves that an object existed at a time. It does not prove delivery. Delivery is proven by the counterparty's acknowledgement. Without one, a non_ack stands on delivery attempts whose transport receipts were witnessed at the time of each attempt.¶
8.4. State Machine
A verifier determines the state of an exchange by verifying each object's signature, its exchange_id, seq, and prev, and its witness statements, and then applying the transitions below in order of witness time.¶
| State | Expected next | If the deadline passes |
|---|---|---|
| SENT (offer witnessed) | held | NOT_ACKNOWLEDGED. The sender issues non_ack carrying the witnessed delivery attempts. The recipient is bound to nothing, and a later held is void. |
| HELD | key_release | VOID. K was withheld and the offer is void. The recipient MAY close. |
| KEY_RELEASED | key_received | Without escrow: KEY_UNACKNOWLEDGED, and the sender issues non_ack. With escrow: the sender MAY request arbiter delivery (Section 8.5); on arbiter_delivery, DELIVERED_VIA_ARBITER, and any later received, accepted, or refused is void. |
| KEY_RECEIVED | received, or mismatch | CONTENT_UNACKNOWLEDGED. The sender MAY issue non_ack. |
| RECEIVED (intent "offer") | accepted, or refused | NOT_ACCEPTED. The silence is recorded with non_ack. |
| MISMATCH | Recomputation | FAULT_SENDER if the failure is confirmed; FAULT_RECIPIENT if it is not. |
For intent "deliver", received moves the exchange directly to the terminal state DELIVERED. For intent "offer", accepted and refused move it to ACCEPTED and REFUSED. Either party MAY close an exchange in a non-terminal state with a stated reason, which moves it to CLOSED. The terminal states are NOT_ACKNOWLEDGED, VOID, KEY_UNACKNOWLEDGED, DELIVERED_VIA_ARBITER, CONTENT_UNACKNOWLEDGED, DELIVERED, ACCEPTED, REFUSED, NOT_ACCEPTED, FAULT_SENDER, FAULT_RECIPIENT, and CLOSED.¶
An object whose earliest witness time is after the deadline or expiry that governs it, or after the exchange reached a terminal state, is void. A state that results from a passed deadline is established against the objects presented; a party rebuts it by presenting an object of the awaited step whose witness time precedes the deadline.¶
Recomputation: because the sender signed both ciphertext_hash and key_hash, any party given K and the ciphertext can repeat the recipient's checks. If DIGEST(ciphertext) equals ciphertext_hash and DIGEST(K) equals key_hash, but decryption fails or the decrypted payload does not match payload_commitment, the failure is confirmed and the state is FAULT_SENDER. If every check passes, the state is FAULT_RECIPIENT. Where escrow is present, arbiters perform the recomputation with the reconstructed K. A recipient that relies on recomputation discloses K and the ciphertext to the party that performs it, which can then read the payload. If the K or the ciphertext presented does not match the hash the sender signed, recomputation cannot attribute the failure, because HPKE decryption and transport are not publicly verifiable. This covers the checks "ciphertext_hash", "unwrap", and "key_hash"; where escrow is present, the arbiters instead check the reconstructed K as described in Section 8.5. Otherwise the verifier reports the cause as not determinable, and the exchange remains in MISMATCH until it is closed.¶
FAULT_SENDER and FAULT_RECIPIENT identify the party whose signed values are inconsistent with the recomputation. Like every other result of this document, they are evidence and not a verdict (Section 1.2).¶
Implementations MUST present held, received, delivered via arbiter, and accepted to users as distinct states. Treating delivery as agreement is the most likely misuse of this mechanism.¶
8.5. Arbiters
Arbiters are named in the offer and bind only once the recipient accepts them through arbiters_accepted in held. Publicly verifiable secret sharing lets the recipient check the encrypted shares before it sends held. A recipient MUST NOT send held with arbiters_accepted unless the proof verifies; where escrow is required and the proof does not verify, the recipient closes the exchange.¶
An arbiter MUST open its share only for an exchange whose chain and witness statements show KEY_RELEASED past its deadline, or MISMATCH, and MUST verify the chain before doing so. Every opening is itself witnessed and registered with a transparency service.¶
When M arbiters have opened their shares, they reconstruct K, check it against key_hash, check that it decrypts the ciphertext to a payload matching payload_commitment, deliver K to the recipient in the manner of key_release, and each sign the same arbiter_delivery body; the signatures are carried in the JWS JSON General Serialization (Section 7.2.1 of [RFC7515]). arbiter_delivery never implies received, acceptance, or refusal. Arbiters never issue received, accepted, or refused on the recipient's behalf.¶
If the reconstructed K does not match key_hash, or does not decrypt the ciphertext to a payload matching payload_commitment, the arbiters sign arbiter_delivery with "key_matched" or "commitment_matched" false and deliver nothing, and the inconsistency is attributed to the sender, who signed both values (FAULT_SENDER).¶
8.6. Binding to Decision Records
When an agent decides on the basis of a received payload, its context receipt (Section 5.2) lists, as items with role "exchange", the bodies of the exchange objects up to and including received, each with media type application/adc-exchange+json, and, as items with role "witness", their witness statements, in addition to the payload itself. The decision record therefore proves both what arrived and what the agent did with it.¶
A counterparty MAY countersign a decision commitment: the holder sends an exchange with intent "deliver" whose payload is the decision context claim, and the counterparty's received object is the countersignature. In the Agent Payments Protocol, the merchant-signed Checkout Receipt has the same effect when the claim's disclosure is included in the presentation the merchant receives (Section 7.2).¶
8.7. Security and Privacy of the Exchange
- Fairness:
- Neither party can obtain the other's acknowledgement without leaving evidence, but a party can still stop at any step. The state machine turns stopping into a recorded state; it does not prevent it [PAGNIA].¶
- Metadata:
- Witnesses see hashes only. Pairwise keys limit linking across exchanges. Arbiters see the metadata of exchanges that name them and SHOULD be rotated.¶
- Low-entropy payloads:
- The 32-octet salt in the plaintext prevents a party that sees payload_commitment from confirming a guessed payload.¶
- Replay:
- exchange_id, seq, prev, and expires_at together prevent a replayed object from being accepted as new.¶
- Key compromise:
- Witness time bounds when a signature could have been made.¶
- Witness collusion:
- A party colluding with every witness it uses can misstate time. The requirement for a witness independent of both parties, and two witnesses for obligations, mitigates this.¶
- Arbiter collusion:
- M colluding arbiters can read the payload. Fewer than M learn nothing about K.¶
- Griefing:
- A non_ack carries weight only through its witnessed delivery attempts, not through the sender's assertion. A recipient MAY answer with a signed close stating a reason.¶
- Key and nonce reuse:
- K is fresh for each exchange, so a random AEAD nonce is safe. Reusing K across exchanges is prohibited.¶
- Legal effect:
- This section produces evidence. The legal presumption that Article 43 of [EIDAS] gives to qualified electronic registered delivery services requires a qualified provider. A qualified trust service provider acting as both witness and delivery service is the route to that effect and is outside the scope of this document.¶
10. Security Considerations
10.1. Trust Assumptions
- The holder's signing key and the agent's signing key are not compromised at decision time.¶
- At least one transparency service used is append-only, presents a consistent view, and records registration times honestly. Collusion between a holder and every transparency service it uses defeats the time bound.¶
- SHA-256 is collision-resistant and second-preimage-resistant.¶
- For level A3, the attesting model providers are honest.¶
- For exchanges, at least one witness used is independent of both parties and records time honestly, and fewer than M arbiters collude (Section 8.7).¶
10.2. Threats and Mitigations
- Fabrication after the fact:
- A record created after a dispute does not match the commitment in the signed mandate or the anchored statement.¶
- Fabrication at decision time:
- Not prevented. A holder can commit to a misleading record before signing. Level A3 narrows this by having providers attest to what each model call received and returned.¶
- Omission of records:
- The holder chain and anchoring make a missing interior sequence number detectable, and a verifier that obtains neighboring anchors can detect equivocation. Deletion after the last anchor a verifier sees, and abandonment of a chain, are not detectable from the chain alone (Section 5.5).¶
- Omission of items within a record:
- A verifier cannot prove that the receipt lists every input; it can prove only that the receipt has not changed since anchoring. At A3 with a released "request" item, it can check that the listed items are the parts of the attested request. Verifiers MUST NOT report completeness.¶
- Back-dating:
- Bounded by the registration time, where it can be established (Section 5.5).¶
- Split view and service collusion:
- A transparency service that presents different logs to different parties can mislead a verifier that checks only the receipts the holder presents. Profiles mitigate this with services that provide consistency proofs, with registration at independent services, and by distributing service trust anchors out of band.¶
- Malicious receipt_uri:
- A holder can direct verifiers to a host it controls. The credential properties in Section 6.1 prevent replay of the verifier's credential, and the restriction on dereferencing limits request forgery.¶
- Reuse of a claim in another transaction:
- Detected by step 3 where the record's transaction fields are unique to the transaction (for example through checkout_digest); on rails where they can repeat, detected through the x402 nonce construction or the holder chain.¶
- Stripping or substitution of the claim:
- Substitution is prevented where the claim is covered by a signature. Omission of the claim's disclosure from a presentation is not prevented; a verifier that sees an undisclosed digest can request the disclosure from the holder. Where no signature covers the claim, the binding is level A0.¶
- Guessing record contents:
- Prevented by the 32-octet commitment salt and per-item salts.¶
- Model substitution:
- The record names a resolved model version; only level A3 proves which model ran.¶
- Canonicalization divergence:
- Addressed by mandatory NFC normalization and JCS; numbers are canonicalized by JCS, and raw provider output is a separate item.¶
- Key compromise:
- Anchoring bounds when a commitment existed, not when a signature over it was made. Holders publish key validity periods and revocations under their profile.¶
- Replay of a disclosure request:
- Prevented by the single-use request_id, the issued_at tolerance, and the sender-constrained, transaction-bound credential (Section 6.1).¶
- Replay of a release:
- A release echoes the request_id and verifier and is signed by the holder, so it cannot be passed off as the answer to another request. Anyone who holds a release can still show it to others; verifiers MUST NOT pass releases to parties outside the dispute.¶
- Collusion:
- A holder colluding with every transparency service it uses defeats the time bound; independent services and consistency proofs mitigate this. A holder colluding with a model provider defeats level A3. A principal and holder acting together can still commit to a misleading record before a payment, though not alter it afterwards. A holder colluding with a verifier is exposed when another party repeats the verification (Section 6.7). This document does not address collusion among dispute participants beyond that.¶
- Hash collisions:
- Binding depends on the collision resistance of SHA-256. A party able to find collisions could open one commitment to two records. Fixed-length salt prefixes and canonical serialization remove ambiguity in how inputs are formed. Length extension does not apply, because ADC uses no secret-prefix authentication and verifiers recompute every digest over its complete input. The "alg" member allows migration.¶
- Canonicalization attacks:
- Objects containing invalid Unicode (such as unpaired surrogates), non-finite numbers, numbers outside the range JCS represents exactly, or duplicate member names MUST be rejected before hashing. Amounts are strings for this reason.¶
- Weak randomness and salt reuse:
- Predictable or reused salts allow guessing of hidden content and linking of equal descriptors. Salts MUST come from a cryptographically secure generator and MUST NOT be reused.¶
- Malicious released content:
- Released items are untrusted input and may contain malware, prompt injection aimed at review tools, or decompression bombs. Verifiers MUST treat items as data, MUST enforce the profile's size and count limits before processing, and SHOULD isolate any replay or automated review of item content.¶
- Denial of service:
- Holders rate-limit disclosure requests, which require credentials. Unavailability of a transparency service is handled by alternate services or late registration; unavailability of the holder is reported as temporarily_unavailable and does not by itself change any level.¶
- State rollback:
- A holder restored from backup could reuse sequence numbers, which verifiers report as equivocation. The failure-handling rules in Section 5.5 prevent this for honest holders.¶
10.3. Replay and Attribution
Re-submitting recorded items to the named model shows what the model received and how it may respond. It does not establish what caused the original output, whether that output was correct, or that a given item was responsible. Model outputs may vary between calls, and models may be withdrawn. The committed answer is the record of what happened.¶
10.4. Cryptographic Considerations
Salts have a fixed length so that concatenation is unambiguous. The "alg" member allows other digest algorithms in future versions; verifiers reject algorithms they do not support. Item digests and commitments are salted with fixed-length salts, and receipts, records, and statement payloads carry distinct "type" members; an item descriptor carries no "type" but is always hashed with a salt, whereas receipts are not. SD-JWT disclosure digests are computed as specified in [RFC9901].¶
This document does not depend on the quantum resistance of any signature. SHA-256 commitments retain their binding property against known quantum attacks, and the 32-octet salts keep generic quantum search for the hidden content impractical. The signatures on mandates, signed statements, and transparency receipts are another matter: a party able to forge them could fabricate evidence of when a commitment existed. Holders SHOULD support ML-DSA for anchor statements [RFC9964] as transparency services deploy it, MAY use composite signatures [I-D.ietf-jose-pq-composite-sigs], and MAY re-register earlier commitments under a stronger algorithm while retaining the original receipts. Verifiers MUST report the signature algorithm used at each step.¶
11. Privacy Considerations
This section follows the threat categories of [RFC6973].¶
- Surveillance and correlation:
- Mandates and transparency services see only commitments, chain ids, and sequence numbers, but anchors are public and timestamped. Because declined decisions are anchored too, the anchoring pattern of a chain reveals how often its principal considers payments, and the timing of an anchor can be joined with the time of an on-chain payment to link a chain to a wallet. Holders SHOULD operate chains and signing keys that serve many principals rather than one, MAY add a random delay within the profile's interval, and MAY batch anchors where the profile allows. "receipt_uri" MUST NOT contain stable principal identifiers or values derived from the record.¶
- Stored data compromise:
- Stored context routinely contains personal data of the principal and of third parties, such as the senders of emails. Holders MUST encrypt stored items at rest and SHOULD use per-principal keys.¶
- Identification:
- Anchor and entry statements MUST NOT contain principal identifiers.¶
- Secondary use:
- Context recorded under this document SHOULD NOT be used for purposes other than dispute resolution, audit, and the principal's own access. Replay by a verifier is itself processing and a transfer to the model provider; it SHOULD occur only under an agreement covering that processing or on infrastructure the principal approves.¶
- Disclosure:
- Release is limited to the transaction in the request and governed by the principal's policy. Withholding is visible.¶
- Exclusion:
- Principals SHOULD be able to obtain their own records and receipts.¶
Data-protection law can apply to holders and verifiers, for example [GDPR] and [LGPD]. Holders will typically need to determine their role as controller or processor, a lawful basis, whether an impact assessment is required, how notice is given to third parties whose data appears in context, how access requests are met, and how cross-border transfers are handled. Under such regimes, digests of personal data can remain personal data. Deleting a descriptor's salt prevents the digest from being linked to the content, but whether that satisfies an erasure obligation is a legal question. Requirements for logging by high-risk AI systems [EUAIA] apply only where a system is classified as high-risk; this document does not by itself satisfy them.¶
12. Operational Considerations
- Latency:
- Anchoring before authorization adds a round trip to a transparency service. Profiles that cannot accept this latency can allow late anchoring, which caps the level at A1.¶
- Availability:
- Holders SHOULD be able to register with more than one transparency service, and SHOULD replicate stored evidence so that a single storage failure does not cause loss before retention_until.¶
- Limits:
- Profiles set maximum item counts, item sizes, and release sizes; holders and verifiers enforce them.¶
- Retention:
- retention_until SHOULD cover the longest dispute window applicable to the rail and any regulatory minimum. Profiles set the value.¶
- Model deprecation:
- Providers withdraw model versions, after which replay is impossible. Level A3 attestations remain verifiable without the model.¶
- Storage:
- As-sent media can be large. Holders can store each distinct item once, keyed by content digest.¶
- Keys:
- Holder signing keys carry identifiers, validity periods, and revocation status published under the profile. Sequence numbers are per chain, not per key.¶
- Registration cost:
- Some transparency services may charge for or otherwise gate registration. Because every covered decision, including a decline, is anchored, holders need to account for the per-decision cost when choosing services.¶
13. IANA Considerations
13.1. JSON Web Token Claims Registration
IANA is requested to register the following claim in the "JSON Web Token Claims" registry established by [RFC7519]:¶
- Claim Name: decision_context¶
- Claim Description: Commitment to the decision context of an agent-initiated payment¶
- Change Controller: Author (to be changed to IETF if this document is adopted in the IETF stream)¶
- Specification Document(s): Section 5.4 of this document¶
13.2. Media Type Registrations
IANA is requested to register the following media types [RFC6838], all using the "+json" structured syntax suffix [RFC6839]. application/adc-anchor+json is the payload content type of anchor, outcome, erasure, and witness statements (Section 5.5, Section 8.3); application/adc-release+json is the payload of a signed release (Section 6.2); application/adc-exchange+json is the body of an exchange object (Section 8).¶
- Type name:
- application¶
- Subtype names:
- adc-anchor+json; adc-release+json; adc-exchange+json¶
- Required parameters:
- N/A¶
- Optional parameters:
- N/A¶
- Encoding considerations:
- binary; as for application/json¶
- Security considerations:
- See Section 10 and Section 11 of this document.¶
- Interoperability considerations:
- Objects are canonicalized as described in Section 5.1 before hashing.¶
- Published specification:
- This document¶
- Applications that use this media type:
- Agent platforms, payment dispute systems, and agents exchanging payloads with counterparties¶
- Fragment identifier considerations:
- As for application/json¶
- Additional information:
- Deprecated alias names for this type: N/A; Magic number(s): N/A; File extension(s): N/A; Macintosh file type code(s): N/A¶
- Person and email address to contact for further information:
- Fernando Henrique Damasceno Milan, milan@nordqubit.com¶
- Intended usage:
- COMMON¶
- Restrictions on usage:
- None¶
- Author:
- Fernando Henrique Damasceno Milan¶
- Change controller:
- Author (to be changed to IETF if this document is adopted in the IETF stream)¶
14. Implementation Status
This section is to be removed before publishing as an RFC.¶
This section records the status of known implementations at the time of writing, following [RFC7942]. Description: the author is developing a prototype holder implementation. Maturity: prototype; not publicly available. Coverage: data model, commitment, and test vectors; the exchange in Section 8 is not yet implemented, and its state table has been checked mechanically for completeness and for the absence of transitions out of terminal states. Contact: the author. No interoperability testing has taken place. The test vectors in Appendix A were reproduced by an independent implementation written from the text of this document alone, and the CDDL in Section 5.7 has been checked with a CDDL validator against the objects in that appendix.¶
15. References
15.1. Normative References
- [RFC2119]
- Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/info/rfc2119>.
- [RFC3161]
- Adams, C., Cain, P., Pinkas, D., and R. Zuccherato, "Internet X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)", RFC 3161, DOI 10.17487/RFC3161, , <https://www.rfc-editor.org/info/rfc3161>.
- [RFC3339]
- Klyne, G. and C. Newman, "Date and Time on the Internet: Timestamps", RFC 3339, DOI 10.17487/RFC3339, , <https://www.rfc-editor.org/info/rfc3339>.
- [RFC4648]
- Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, DOI 10.17487/RFC4648, , <https://www.rfc-editor.org/info/rfc4648>.
- [RFC6838]
- Freed, N., Klensin, J., and T. Hansen, "Media Type Specifications and Registration Procedures", BCP 13, RFC 6838, DOI 10.17487/RFC6838, , <https://www.rfc-editor.org/info/rfc6838>.
- [RFC6839]
- Hansen, T. and A. Melnikov, "Additional Media Type Structured Syntax Suffixes", RFC 6839, DOI 10.17487/RFC6839, , <https://www.rfc-editor.org/info/rfc6839>.
- [RFC7515]
- Jones, M., Bradley, J., and N. Sakimura, "JSON Web Signature (JWS)", RFC 7515, DOI 10.17487/RFC7515, , <https://www.rfc-editor.org/info/rfc7515>.
- [RFC7516]
- Jones, M. and J. Hildebrand, "JSON Web Encryption (JWE)", RFC 7516, DOI 10.17487/RFC7516, , <https://www.rfc-editor.org/info/rfc7516>.
- [RFC7518]
- Jones, M., "JSON Web Algorithms (JWA)", RFC 7518, DOI 10.17487/RFC7518, , <https://www.rfc-editor.org/info/rfc7518>.
- [RFC7519]
- Jones, M., Bradley, J., and N. Sakimura, "JSON Web Token (JWT)", RFC 7519, DOI 10.17487/RFC7519, , <https://www.rfc-editor.org/info/rfc7519>.
- [RFC7638]
- Jones, M. and N. Sakimura, "JSON Web Key (JWK) Thumbprint", RFC 7638, DOI 10.17487/RFC7638, , <https://www.rfc-editor.org/info/rfc7638>.
- [RFC8174]
- Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/info/rfc8174>.
- [RFC8259]
- Bray, T., Ed., "The JavaScript Object Notation (JSON) Data Interchange Format", STD 90, RFC 8259, DOI 10.17487/RFC8259, , <https://www.rfc-editor.org/info/rfc8259>.
- [RFC8446]
- Rescorla, E., "The Transport Layer Security (TLS) Protocol Version 1.3", RFC 8446, DOI 10.17487/RFC8446, , <https://www.rfc-editor.org/info/rfc8446>.
- [RFC8610]
- Birkholz, H., Vigano, C., and C. Bormann, "Concise Data Definition Language (CDDL): A Notational Convention to Express Concise Binary Object Representation (CBOR) and JSON Data Structures", RFC 8610, DOI 10.17487/RFC8610, , <https://www.rfc-editor.org/info/rfc8610>.
- [RFC8785]
- Rundgren, A., Jordan, B., and S. Erdtman, "JSON Canonicalization Scheme (JCS)", RFC 8785, DOI 10.17487/RFC8785, , <https://www.rfc-editor.org/info/rfc8785>.
- [RFC9180]
- Barnes, R., Bhargavan, K., Lipp, B., and C. Wood, "Hybrid Public Key Encryption", RFC 9180, DOI 10.17487/RFC9180, , <https://www.rfc-editor.org/info/rfc9180>.
- [RFC9325]
- Sheffer, Y., Saint-Andre, P., and T. Fossati, "Recommendations for Secure Use of Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS)", BCP 195, RFC 9325, DOI 10.17487/RFC9325, , <https://www.rfc-editor.org/info/rfc9325>.
- [RFC9864]
- Jones, M. and O. Steele, "Fully-Specified Algorithms for JSON Object Signing and Encryption (JOSE) and CBOR Object Signing and Encryption (COSE)", RFC 9864, DOI 10.17487/RFC9864, , <https://www.rfc-editor.org/info/rfc9864>.
- [RFC9901]
- Fett, D., Yasuda, K., and B. Campbell, "Selective Disclosure for JSON Web Tokens", RFC 9901, DOI 10.17487/RFC9901, , <https://www.rfc-editor.org/info/rfc9901>.
- [RFC9943]
- Birkholz, H., Delignat-Lavaud, A., Fournet, C., Deshpande, Y., and S. Lasker, "An Architecture for Trustworthy and Transparent Digital Supply Chains", RFC 9943, DOI 10.17487/RFC9943, , <https://www.rfc-editor.org/info/rfc9943>.
- [SHS]
- National Institute of Standards and Technology, "Secure Hash Standard (SHS)", FIPS PUB 180-4, , <https://doi.org/10.6028/NIST.FIPS.180-4>.
- [UAX15]
- The Unicode Consortium, "Unicode Normalization Forms", Unicode Standard Annex #15, , <https://www.unicode.org/reports/tr15/>.
15.2. Informative References
- [AP2]
- Google, "Agent Payments Protocol, version 0.2.0", , <https://github.com/google-agentic-commerce/AP2>.
- [ASOKAN]
- Asokan, N., Shoup, V., and M. Waidner, "Optimistic Fair Exchange of Digital Signatures", Advances in Cryptology - EUROCRYPT '98, LNCS 1403, pp. 591-606, DOI 10.1007/BFb0054156, , <https://doi.org/10.1007/BFb0054156>.
- [BRAVE]
- Brave, "Unseeable prompt injections in screenshots", , <https://brave.com/blog/unseeable-prompt-injections/>.
- [CAIP19]
- Chain Agnostic Standards Alliance, "CAIP-19: Asset Type and Asset ID Specification", <https://chainagnostic.org/CAIPs/caip-19>.
- [EIDAS]
- European Parliament and Council of the European Union, "Regulation (EU) No 910/2014 on electronic identification and trust services for electronic transactions in the internal market, as amended by Regulation (EU) 2024/1183", , <https://eur-lex.europa.eu/eli/reg/2014/910/oj>.
- [EUAIA]
- European Parliament and Council, "Regulation (EU) 2024/1689 (Artificial Intelligence Act), as amended by Regulation (EU) 2026/1744", , <https://eur-lex.europa.eu/eli/reg/2024/1689/oj>.
- [FIDO-AGENTIC]
- FIDO Alliance, "FIDO Alliance to Develop Standards for Trusted AI Agent Interactions", , <https://fidoalliance.org/fido-alliance-to-develop-standards-for-trusted-ai-agent-interactions/>.
- [GDPR]
- European Parliament and Council, "Regulation (EU) 2016/679 (General Data Protection Regulation)", , <https://eur-lex.europa.eu/eli/reg/2016/679/oj>.
- [I-D.ailex-vap-legal-ai-provenance]
- Yamakawa, S., "Verifiable AI Provenance (VAP) Framework and Legal AI Profile (LAP)", Work in Progress, Internet-Draft, draft-ailex-vap-legal-ai-provenance-04, , <https://datatracker.ietf.org/doc/draft-ailex-vap-legal-ai-provenance/04/>.
- [I-D.birkholz-verifiable-agent-conversations]
- Birkholz, H., Heldt, T., and O. Steele, "Verifiable Agent Conversation Records", Work in Progress, Internet-Draft, draft-birkholz-verifiable-agent-conversations-01, , <https://datatracker.ietf.org/doc/draft-birkholz-verifiable-agent-conversations/01/>.
- [I-D.chang-delta1-settlement]
- Chang, Y. C., "Delta-1: A SCITT Profile for Binary Settlement Receipts in Agentic AI Accountability", Work in Progress, Internet-Draft, draft-chang-delta1-settlement-00, , <https://datatracker.ietf.org/doc/draft-chang-delta1-settlement/00/>.
- [I-D.dias-agent-payment-verdicts]
- Dias, I., "Signed Authorization Verdicts for Agent-Initiated Payments", Work in Progress, Internet-Draft, draft-dias-agent-payment-verdicts-00, , <https://datatracker.ietf.org/doc/draft-dias-agent-payment-verdicts/00/>.
- [I-D.dogru-cedulon-core]
- Doğru, E. C., "Spend Receipts and Payment Rail Reconciliation for AI Agents", Work in Progress, Internet-Draft, draft-dogru-cedulon-core-04, , <https://datatracker.ietf.org/doc/draft-dogru-cedulon-core/04/>.
- [I-D.dogru-cedulon-decision-profile]
- Doğru, E. C., "Cedulon Decision Profile: Reconciling an Agent's Decisions Against Its Effects", Work in Progress, Internet-Draft, draft-dogru-cedulon-decision-profile-04, , <https://datatracker.ietf.org/doc/draft-dogru-cedulon-decision-profile/04/>.
- [I-D.emirdag-scitt-ai-agent-execution]
- Emirdag, P., "AI Agent Execution Profile of SCITT", Work in Progress, Internet-Draft, draft-emirdag-scitt-ai-agent-execution-00, , <https://datatracker.ietf.org/doc/draft-emirdag-scitt-ai-agent-execution/00/>.
- [I-D.ietf-hpke-pq]
- Barnes, R. and D. Connolly, "Post-Quantum and Post-Quantum/Traditional Hybrid Algorithms for HPKE", Work in Progress, Internet-Draft, draft-ietf-hpke-pq-05, , <https://datatracker.ietf.org/doc/draft-ietf-hpke-pq/05/>.
- [I-D.ietf-jose-hpke-encrypt]
- Reddy.K, T., Tschofenig, H., Banerjee, A., Steele, O., and M. B. Jones, "Use of Hybrid Public Key Encryption (HPKE) with JSON Web Encryption (JWE)", Work in Progress, Internet-Draft, draft-ietf-jose-hpke-encrypt-22, , <https://datatracker.ietf.org/doc/draft-ietf-jose-hpke-encrypt/22/>.
- [I-D.ietf-jose-pq-composite-sigs]
- Prabel, L., Shuzhou, S., Gray, J., and T. Reddy.K, "PQ/T Hybrid Composite Signatures for JOSE and COSE", Work in Progress, Internet-Draft, draft-ietf-jose-pq-composite-sigs-05, , <https://datatracker.ietf.org/doc/draft-ietf-jose-pq-composite-sigs/05/>.
- [I-D.kamimura-scitt-vcp]
- Kamimura, T., "A SCITT Profile for Verifiable Audit Trails in Algorithmic Trading: The VeritasChain Protocol (VCP)", Work in Progress, Internet-Draft, draft-kamimura-scitt-vcp-03, , <https://datatracker.ietf.org/doc/draft-kamimura-scitt-vcp/03/>.
- [I-D.kuehlewind-audit-architecture]
- Kühlewind, M. and H. Birkholz, "An Architecture for Auditing Agent Delegation and Interactions", Work in Progress, Internet-Draft, draft-kuehlewind-audit-architecture-01, , <https://datatracker.ietf.org/doc/draft-kuehlewind-audit-architecture/01/>.
- [I-D.mih-agent-bilateral-attestation]
- Mih, S., "Bilateral Attestation of Cross-Organization Agent Actions", Work in Progress, Internet-Draft, draft-mih-agent-bilateral-attestation-02, , <https://datatracker.ietf.org/doc/draft-mih-agent-bilateral-attestation/02/>.
- [I-D.mih-agent-settlement-records]
- Mih, S., "Two-Party Settlement Records for Agent Payments", Work in Progress, Internet-Draft, draft-mih-agent-settlement-records-00, , <https://datatracker.ietf.org/doc/draft-mih-agent-settlement-records/00/>.
- [I-D.mih-scitt-agent-action-capsule]
- Mih, S., "An Agent Action Capsule Profile for SCITT", Work in Progress, Internet-Draft, draft-mih-scitt-agent-action-capsule-05, , <https://datatracker.ietf.org/doc/draft-mih-scitt-agent-action-capsule/05/>.
- [I-D.mih-scitt-agent-action-capsule-sel-disc]
- Mih, S., "Selective Disclosure Profile for Agent Action Capsules", Work in Progress, Internet-Draft, draft-mih-scitt-agent-action-capsule-sel-disc-00, , <https://datatracker.ietf.org/doc/draft-mih-scitt-agent-action-capsule-sel-disc/00/>.
- [I-D.munoz-scitt-permit-profile]
- Munoz, C., "A SCITT Profile for Pre-Execution AI Action Authorization Records", Work in Progress, Internet-Draft, draft-munoz-scitt-permit-profile-01, , <https://datatracker.ietf.org/doc/draft-munoz-scitt-permit-profile/01/>.
- [I-D.noa-scitt-ai-agent-receipt]
- Toraman, T., "A SCITT Profile for AI-Agent Action Receipts", Work in Progress, Internet-Draft, draft-noa-scitt-ai-agent-receipt-01, , <https://datatracker.ietf.org/doc/draft-noa-scitt-ai-agent-receipt/01/>.
- [I-D.sharif-agent-audit-trail]
- Sharif, R., "Agent Audit Trail: A Standard Logging Format for Autonomous AI Systems", Work in Progress, Internet-Draft, draft-sharif-agent-audit-trail-06, , <https://datatracker.ietf.org/doc/draft-sharif-agent-audit-trail/06/>.
- [I-D.tsyrulnikov-rats-attested-inference-receipt]
- Tsyrulnikov, B., "Attested Inference Receipt (AIR): A COSE/CWT Profile for Confidential AI Inference", Work in Progress, Internet-Draft, draft-tsyrulnikov-rats-attested-inference-receipt-02, , <https://datatracker.ietf.org/doc/draft-tsyrulnikov-rats-attested-inference-receipt/02/>.
- [ISO4217]
- International Organization for Standardization, "Codes for the representation of currencies", ISO 4217:2015, , <https://www.iso.org/iso-4217-currency-codes.html>.
- [ISO20022]
- International Organization for Standardization, "Financial services - Universal financial industry message scheme - Part 1: Metamodel", ISO 20022-1:2013, , <https://www.iso20022.org/>.
- [LGPD]
- Brazil, "Lei Geral de Proteção de Dados Pessoais (Lei 13.709/2018)", , <https://www.planalto.gov.br/ccivil_03/_ato2015-2018/2018/lei/l13709.htm>.
- [PAGNIA]
- Pagnia, H. and F. C. Gaertner, "On the Impossibility of Fair Exchange without a Trusted Third Party", Technical Report TUD-BS-1999-02, Darmstadt University of Technology, .
- [RFC5869]
- Krawczyk, H. and P. Eronen, "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)", RFC 5869, DOI 10.17487/RFC5869, , <https://www.rfc-editor.org/info/rfc5869>.
- [RFC6973]
- Cooper, A., Tschofenig, H., Aboba, B., Peterson, J., Morris, J., Hansen, M., and R. Smith, "Privacy Considerations for Internet Protocols", RFC 6973, DOI 10.17487/RFC6973, , <https://www.rfc-editor.org/info/rfc6973>.
- [RFC7942]
- Sheffer, Y. and A. Farrel, "Improving Awareness of Running Code: The Implementation Status Section", BCP 205, RFC 7942, DOI 10.17487/RFC7942, , <https://www.rfc-editor.org/info/rfc7942>.
- [RFC8392]
- Jones, M., Wahlstroem, E., Erdtman, S., and H. Tschofenig, "CBOR Web Token (CWT)", RFC 8392, DOI 10.17487/RFC8392, , <https://www.rfc-editor.org/info/rfc8392>.
- [RFC9052]
- Schaad, J., "CBOR Object Signing and Encryption (COSE): Structures and Process", STD 96, RFC 9052, DOI 10.17487/RFC9052, , <https://www.rfc-editor.org/info/rfc9052>.
- [RFC9449]
- Fett, D., Campbell, B., Bradley, J., Lodderstedt, T., Jones, M., and D. Waite, "OAuth 2.0 Demonstrating Proof of Possession (DPoP)", RFC 9449, DOI 10.17487/RFC9449, , <https://www.rfc-editor.org/info/rfc9449>.
- [RFC9964]
- Prorock, M. and O. Steele, "ML-DSA for JSON Object Signing and Encryption (JOSE) and CBOR Object Signing and Encryption (COSE)", RFC 9964, DOI 10.17487/RFC9964, , <https://www.rfc-editor.org/info/rfc9964>.
- [SCHOENMAKERS]
- Schoenmakers, B., "A Simple Publicly Verifiable Secret Sharing Scheme and Its Application to Electronic Voting", Advances in Cryptology - CRYPTO '99, LNCS 1666, pp. 148-164, DOI 10.1007/3-540-48405-1_10, , <https://doi.org/10.1007/3-540-48405-1_10>.
- [TOB]
- Trail of Bits, "Weaponizing image scaling against production AI systems", , <https://blog.trailofbits.com/2025/08/21/weaponizing-image-scaling-against-production-ai-systems/>.
- [VI]
- Verifiable Intent Working Group, "Verifiable Intent: Credential Format, version 0.1-draft", https://github.com/agent-intent/verifiable-intent, , <https://verifiableintent.dev>.
- [X402]
- x402 Foundation, "x402 Protocol Specification, Protocol Version 2", , <https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md>.
Appendix A. Test Vectors
All values were computed with SHA-256, NFC normalization, JCS, and base64url without padding. Objects are shown formatted for readability; their canonical forms follow from Section 5.1. Salts are fixed, not random, so that results can be reproduced.¶
Item bytes (as US-ASCII strings) and salts:¶
item 0: "You are a payments assistant. Pay only bills the user
approved." (one line, single space after "user",
no trailing newline)
salt: 32 octets of 0xa0
item 1: "example invoice email body" salt: 32 octets of 0xa1
item 2: "example as-sent image bytes" salt: 32 octets of 0xa2
item 3: "{\"noul\":0.88}" salt: 32 octets of 0xa3
¶
Item descriptors (content_digest values included) and item digests:¶
item 0
salt = oKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKA
descriptor =
{
"calls": [
0
],
"content_digest": "rsu-z89tDjJoX3LK0DRrSIRz_EuYgWJ95Woi06yuRxg",
"media_type": "text/plain",
"role": "instructions",
"source": {
"kind": "system"
}
}
item_digest = 9QV3ZRTxsG60bOe1e6gS5LL0q5XjkTivElxci0uqfqY
item 1
salt = oaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaE
descriptor =
{
"calls": [
0
],
"content_digest": "BlW2UA5Du9SFrwm-NpRxeQsmL5C2lYT2GyJbZW3-BPU",
"media_type": "message/rfc822",
"role": "as_sent",
"source": {
"fetched_at": "2026-10-10T14:02:51Z",
"kind": "email",
"uri": "mid:invoice-0042@supplier.example"
}
}
item_digest = 8m7geGcZw1hWHKHjFFOpEpPe_MQ9IgxwyUaFALTdw4Y
item 2
salt = oqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqI
descriptor =
{
"calls": [
0
],
"content_digest": "2xHGVXsHZv2LW4y8n9Kl7juZ1Q1dfdfdL6s7P114tmQ",
"derived_from": 1,
"media_type": "image/png",
"role": "as_sent",
"source": {
"fetched_at": "2026-10-10T14:02:51Z",
"kind": "email_part",
"uri": "mid:invoice-0042@supplier.example"
},
"transform": "resize/example-lib-1.0/lanczos3/1024"
}
item_digest = nkT1qIfqXLMxVXtuqJvcRQtNaDbLxitRYFq329hcjrI
item 3
salt = o6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6M
descriptor =
{
"calls": [
0
],
"content_digest": "hGYLopth2mQvEvJW8AsgAyAuYcLFyHR5qcILCdeaqRk",
"media_type": "application/json",
"role": "response",
"source": {
"kind": "model",
"uri": "https://provider.example/v1/requests/req-0001"
}
}
item_digest = 2g8OXnhoGOAx3nkSIwTWsKYxkRuD5-sVOJAFUxh7uxA
¶
Context receipt:¶
{
"alg": "sha-256",
"item_count": 4,
"items": [
"9QV3ZRTxsG60bOe1e6gS5LL0q5XjkTivElxci0uqfqY",
"8m7geGcZw1hWHKHjFFOpEpPe_MQ9IgxwyUaFALTdw4Y",
"nkT1qIfqXLMxVXtuqJvcRQtNaDbLxitRYFq329hcjrI",
"2g8OXnhoGOAx3nkSIwTWsKYxkRuD5-sVOJAFUxh7uxA"
],
"retention_until": "2028-04-10T00:00:00Z",
"type": "adc-receipt",
"v": 1
}
¶
receipt_digest = _Ov_XzNXS4z1mVz9sb2CgIRLZbyUlSlN_G2lw080W0s¶
Decision record. Four members use stand-in values so that the vector is self-contained: "prev" stands in for the commitment of record 40 and is DIGEST of the US-ASCII string "previous commitment placeholder"; "checkout_digest" stands in for the AP2 "checkout_hash" and is DIGEST of the US-ASCII string "example checkout_jwt"; "request_digest" is DIGEST of the US-ASCII string "example provider request body". In this appendix, DIGEST of a string means DIGEST of its octets, not of its JSON form. "schema_digest" is DIGEST of the JSON object {"kind":"probability","name":"pay_invoice"}.¶
{
"action": {
"taken": "pay"
},
"alg": "sha-256",
"answer": {
"probability": 0.88
},
"chain": {
"id": "ch-7f3a91",
"prev": "uuwyfPlzoD_kakG59boK4Ga0yJamhmuNq34CaV1WXMA",
"seq": 41
},
"issued_at": "2026-10-10T14:03:22Z",
"models": [
{
"id": "example-model-2026-09",
"provider": "provider.example",
"request_digest": "rLYUCRQ-un2g7mhK3wxADF_ud8Up-2bz66_UsWbCNOE",
"request_id": "req-0001"
}
],
"question": {
"kind": "probability",
"name": "pay_invoice",
"schema_digest": "e64ljzd9rTSr6AXA41WIigpstKOTVV82YIfxgxLidwY"
},
"receipt_digest": "_Ov_XzNXS4z1mVz9sb2CgIRLZbyUlSlN_G2lw080W0s",
"transaction": {
"amount": "48000",
"checkout_digest": "H6AT0BnDXRP7TfoMSOAdI7qs-u2D3Y99JeI7kKsQd2k",
"currency": "BRL",
"payee": "merchant.example",
"rail": "ap2"
},
"type": "adc-record",
"v": 1
}
¶
commitment salt (octets 0x00..0x1f) = AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8 C = iJbDuNj4EPEiCI_lVaAeT465fHTJn1qTDyDuBZSIIXA¶
Decision context claim and SD-JWT disclosure. "receipt_uri" in this example ends in an opaque value chosen by the holder. The disclosure salt is the 16 octets 0x20..0x2f ("ICEiIyQlJicoKSorLC0uLw"). The disclosure is the array [salt, "decision_context", claim] serialized without whitespace, with the claim members in the order shown; the disclosure digest is DIGEST of the US-ASCII disclosure string. Line breaks in the encoded value are for display only:¶
{
"v": 1,
"alg": "sha-256",
"commitment": "iJbDuNj4EPEiCI_lVaAeT465fHTJn1qTDyDuBZSIIXA",
"receipt_uri": "https://holder.example/adc/r/q8Zk2LxV"
}
¶
disclosure = WyJJQ0VpSXlRbEppY29LU29yTEMwdUx3IiwiZGVjaXNpb25fY29udGV4dCIseyJ2 IjoxLCJhbGciOiJzaGEtMjU2IiwiY29tbWl0bWVudCI6ImlKYkR1Tmo0RVBFaUNJ X2xWYUFlVDQ2NWZIVEpuMXFURHlEdUJaU0lJWEEiLCJyZWNlaXB0X3VyaSI6Imh0 dHBzOi8vaG9sZGVyLmV4YW1wbGUvYWRjL3IvcThaazJMeFYifV0 disclosure digest = sS4aHqmyXB7SM7eTSevd7HSwzvF2AuHi86hC_VH-BVU¶
x402 nonce, SHA-256(BASE64URL-DECODE(C) || r) in hexadecimal, with r ("nonce_random") = octets 0x30..0x3f:¶
nonce = 0xfeb329a3d9524e30640c71e39301af69
c63b7d54293eb1104a7d67e3765ad304
¶
Negative vector: changing item 1's "fetched_at" to "2026-10-10T14:02:52Z" yields item digest _zVFztwZa20kMSsIdkhpwOq1UeSKLe8yygvC0O5_3Cs and receipt_digest alHUluxRV0hr2pB-N82UZZPIJ5whPUSRm1oZeG2Hsh4, which does not match the record.¶
ISO 20022 remittance information for this commitment:¶
ADC1:8896c3b8d8f810f122088fe555a01e4f8eb97c74c99f5a930f20ee0594882170¶
Normalization: the strings "Cafe" followed by U+0301 and "Caf" followed by U+00E9 produce identical canonical forms after NFC normalization.¶
Appendix B. Example Mapping to Dispute Evidence
This appendix is informative. It illustrates which questions in a dispute about an agent-initiated payment the elements of this document can help answer. It does not assert that any scheme accepts them.¶
| Question | Element |
|---|---|
| Was this payment the result of a recorded decision, and when? | Claim in the mandate; anchor; holder chain |
| What did the model receive, including transformed media? | Context receipt and released items |
| Which model decided, and with what confidence? | models, answer; attestation at level A3 |
| Has the evidence changed since the decision? | Commitment and receipt digests |
| Were any decisions omitted? | Sequence gaps in the holder chain |
| Was evidence deleted, and when? | Erasure entries |
Acknowledgments
The author thanks the reviewers of earlier versions of this document.¶