Specifications Draft for comment

Emissar Receipt v0.1

A signed, hash-chained receipt of what two agents asked, answered and agreed to in an A2A task, with an extension to exchange it and an API to fetch it.

JSON Schema · OpenAPI · Demo (Node) · Validator (Python)

Document Emissar Receipt v0.1 (the format behind the Ledger module)
Status Draft for comment. Implemented in Emissar Front Door
Date 2026-09-26; published for comment 2026-10-05
Authors Emissar team
A2A extension URI https://emissar.ai/ext/receipt/v1 (redirects to this document)
Media types (provisional, unregistered) application/emissar-receipt+json, application/emissar-receipt-openings+json
Companion files JSON Schema receipt-v0.1.schema.json; OpenAPI receipt-v0.1.yaml; runnable demo receipt-demo.mjs and validator validate.py

Abstract

An Emissar Receipt is a signed, tamper-evident record of one completed A2A task between two agents. It names the parties and how each was identified, references the mandate used, commits to the request and the outcome with salted hashes instead of carrying the content, and records the final task state and timestamps. The agent that ran the task signs it; the calling agent countersigns it. Both signatures are JSON Web Signatures over the RFC 8785 canonical form of the same payload. Each signing key keeps a hash chain across the receipts it signs, and a receipt digest can be anchored in an RFC 9162 Merkle log. The receipt travels inside A2A as an artifact, and either party can retrieve it later through an authenticated API. Anyone holding a receipt can recompute its digest and check both signatures offline.

Status of This Document

This is a draft published for comment on 2026-10-05. It is not submitted to any standards body. Emissar Front Door implements it. v0.1 may change incompatibly; breaking changes get a new extension URI. Comments and questions: the contact form at https://emissar.ai/contact (topic “General”).

The A2A extension guidance says an extension’s specification should be hosted at its URI. The URI above redirects to this document (section 16, Q1).


1. Introduction

1.1. Problem

When two agents settle a refund or book freight in seconds, both sides still need a record that holds up if the customer disputes it, an auditor asks, or the agents disagree about what was agreed. A2A (v1.0.1, 2026-05-28) defines how agents exchange tasks, but a finished task is only what the remote agent reports. Nothing in it is signed. Section 3.7 of the A2A specification also lets the agent decide which messages to keep in task history. The Ledger module promises a receipt for each completed task: the parties, their verified identities, the mandate used, the request, the outcome and timestamps, signed by both sides, retrievable by either, and checkable by hash by anyone who holds it. This document defines that receipt.

1.2. Goals

  1. Two-party evidence. Both agents sign the same bytes, so neither can later present a different version.
  2. Content stays with the parties. The receipt commits to content with salted hashes. It can be stored, shared with an auditor, or logged without exposing the exchange.
  3. Offline verification. A holder needs the receipt, the public keys, and nothing else.
  4. Detectable rewriting. Each signing key chains its receipts; logs make that history public without making content public.
  5. Built from existing standards. RFC 8785, JWS, RFC 6920, RFC 7638 and RFC 9162, carried as an A2A extension. No new cryptography.
  6. Deletable content. Content can be destroyed on schedule while signatures and digests stay verifiable.

1.3. Non-goals

  • Defining mandates (Emissar Mandate v0.1) or payments (AP2, x402). Receipts reference them by hash.
  • Deciding whether an agent should be trusted. Verify decides that; a receipt records what each side checked.
  • Legal admissibility, or any specific retention period (section 13).
  • Recording every intermediate message. v0.1 commits to what the client sent and to the final result (section 6.5).

1.4. Design Decisions at a Glance

Decision Choice Why
Payload encoding JSON, canonicalized with RFC 8785 A2A is JSON; Emissar’s Agent Card signatures already use RFC 8785
Signature container JWS general JSON serialization, detached payload, one entry per party Two independent signatures over identical bytes; standard JOSE libraries verify each entry
Content in the receipt Salted commitments, one per message and artifact Low-entropy content (“cancel my plan”) is guessable from a plain hash; per-item commitments allow selective disclosure
Hash format RFC 6920 ni: URIs Algorithm travels with every digest; IANA registry already exists
Party key binding RFC 7638 thumbprint (jkt) inside the signed payload Verification survives key-set changes; matches the key binding planned for Mandate
Chain position In each party’s protected header Each party chains independently at its own signing time; no coordination between parties
Delivery in A2A An artifact on the task A2A says messages are not reliable delivery and results belong in artifacts
Countersignature return HTTPS Retrieval API A2A rejects messages to terminal tasks
Anchoring Optional RFC 9162 Merkle log; SCITT mapping defined Public tamper evidence without public content

2. Conventions and Definitions

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.

Notation: JCS(v) is the RFC 8785 serialization of JSON value v. UTF8(s) is the UTF-8 encoding of string s. ASCII(s) is its ASCII encoding. B64U(x) is base64url without padding [RFC4648]. H(x) is the receipt’s digestAlg applied to octets x. ni(a, x) is the RFC 6920 URI ni:///a;B64U(x), and raw(d) is the digest octets inside such a URI d. || is concatenation. EXT is the string https://emissar.ai/ext/receipt/v1.

3. Terminology

Term Meaning
Server The A2A agent that owns the task (it generated the taskId). It issues the receipt.
Client The A2A agent that sent the messages that created and continued the task.
Party The server or the client.
Principal The person or organization a party acts for.
Receipt The signed payload defined in section 5.2.
Receipt document The receipt plus its signatures and anchors (section 5.1). What parties store and exchange.
Receipt digest H(UTF8(JCS(receipt))) as an ni URI (section 6.3). “The receipt’s hash” in the Ledger promise.
Commitment A salted digest of one message or artifact (section 6.4).
Opening The salt that, with the original content, reproduces a commitment.
Issuer-signed A receipt with a valid server signature and no client signature.
Countersigned A receipt with valid server and client signatures.
Chain The sequence of receipts signed with one key, linked by hash (section 8).
Anchor A proof that a receipt digest is in a transparency log (section 9).
Transparency receipt A SCITT Receipt (RFC 9943), which RFC 9942 encodes as a COSE Receipt. Named this way here to avoid confusion with an Emissar Receipt.

4. Overview

 Client agent                                  Server agent (issuer)
 ------------                                  ---------------------
 fetch Agent Card, see EXT in capabilities.extensions
 SendMessage  (A2A-Extensions: EXT; message.metadata[EXT] = signing key, principal)
        ------------------------------------------------>
                                               ... task runs, may pause for input ...
                                               terminal state reached
                                               build receipt: commitments, parties, timestamps
                                               sign as "server" (seq n on its key's chain)
        <------------------------------------------------
 Task with receipt artifact (document + openings), then the terminal status
 verify server signature, open every commitment
 against its own copies, check parties and times
 sign as "client" (seq m on its key's chain)
 POST {receiptApi}/receipts/{id}/signatures
        ------------------------------------------------>
                                               verify and append; either party may anchor the digest
 Either party: GET {receiptApi}/receipts/{id} at any time. Anyone holding the document: verify offline (section 12).

5. Receipt Data Model

5.1. Receipt Document

A receipt document is a JSON object with these members and no others:

Member Req. Content
receipt Yes The signed payload (section 5.2).
signatures Yes One or two signature entries (section 7). The server’s entry comes first.
anchors No Anchor objects (section 9.3). Not covered by the party signatures; each verifies on its own.

The document is a JWS in the general JSON serialization [RFC7515, section 7.2.1] with the payload member removed as described for detached content [RFC7515, Appendix F]. The JWS Payload is UTF8(JCS(receipt)). RFC 7515 requires implementations to ignore members they do not understand, so a generic JOSE library can verify an entry once the caller supplies the payload.

5.2. Receipt Payload

Member Req. Type Content
version Yes string "0.1" for this document.
receiptId Yes string UUID [RFC9562], version 4 or 7, lowercase. Generated by the server.
digestAlg Yes string Algorithm for every digest this receipt computes (section 6.2).
task Yes object Which A2A task (section 5.3).
parties Yes object server and client (section 5.4).
mandates No array Mandate references (section 5.5).
request Yes object Commitments to the client’s messages (section 5.6).
outcome Yes object Final state and commitments to the result (section 5.6).
payments No array Payment references (section 5.7).
timestamps Yes object Section 5.8.

A receipt MUST NOT contain members this document does not define. Verifiers MUST reject unknown members. Additions require a new version (section 16, Q9).

5.3. task

Member Req. Content
endpoint Yes The A2A interface URL (supportedInterfaces[].url in the server’s card) that handled the task. taskId values are unique only per server, so the endpoint scopes them.
taskId Yes The server-generated task ID [A2A, section 3.4.2].
contextId If present The task’s contextId. It MUST be included when the task has one.

A server MUST NOT issue two receipts with the same endpoint and taskId. Two such receipts are evidence of equivocation (section 14.3).

5.4. parties

parties.server:

Member Req. Content
agentCard.url Yes The server’s Agent Card URL.
agentCard.digest SHOULD ni digest of UTF8(JCS(card)), where card is the Agent Card JSON the server was serving when the task was accepted (the extended card if the client used one), signatures included. Shows which card, and so which card signature and endpoints, the client relied on.
signingKey.kid Yes Key ID of the server’s receipt-signing key.
signingKey.jkt Yes RFC 7638 SHA-256 thumbprint of that key (section 6.7). Authoritative: verifiers match the key by thumbprint, not by kid alone.
signingKey.jku No HTTPS URL of a JWK Set holding the key. A hint, subject to the verifier’s key-location policy (section 14.7).
principal SHOULD The organization the server acts for.

parties.client:

Member Req. Content
authentication.scheme Yes Name of the securitySchemes entry in the server’s card that the client authenticated with.
authentication.subject Yes The identity the server authenticated: an OAuth client ID, a certificate subject or thumbprint, or similar. MUST NOT be a person’s identifier.
signingKey If countersigning The client’s receipt-signing key as kid, jkt, optional jku, copied from the client’s request metadata (section 10.2). Absent when the client did not ask to countersign.
agentCard.url No The client’s own Agent Card URL, when it publishes one.
principal No Who the client acts for.

A principal is either {"type": "organization", "id": <domain name or public identifier>} or {"type": "person", "commitment": <ni digest>}. A person MUST NOT be named in clear. The commitment is commit(salt, {"id": <identifier both parties already hold>}) (section 6.4). The salt goes in the openings, not the receipt.

“Verified identities” in the Ledger promise means what these fields record: the card and card digest the client relied on, and the authentication the server performed. The server asserts both by signing; the client confirms them by countersigning (section 7.5, checks 3 and 4). A jkt in an issuer-signed receipt is only the server’s claim about the client’s key. Only the client’s countersignature proves the client holds that key.

5.5. mandates

Each entry is {"type", "digest", "id"?}. digest identifies the credential presented for this task, computed as in section 6.6. type values defined here:

type Credential
emissar-mandate Emissar Mandate v0.1 (specification)
ap2-checkout-mandate, ap2-payment-mandate AP2 v0.2 Checkout or Payment Mandate (SD-JWT)
verifiable-intent A Verifiable Intent credential (SD-JWT layers, v0.1-draft)

The receipt never contains the mandate itself. Whoever needs to prove what was authorized presents the credential alongside the receipt, and the verifier hashes it (section 12, step 14).

5.6. request and outcome

request.messages lists one {"messageId", "digest"} per message the client sent for this task and the server accepted, in the order the server received them, deduplicated by messageId [A2A, section 3.3.1]. It MUST contain at least one entry.

outcome has:

Member Req. Content
state Yes The terminal TaskState: TASK_STATE_COMPLETED, TASK_STATE_FAILED, TASK_STATE_CANCELED or TASK_STATE_REJECTED.
statusMessage If present {"messageId", "digest"} for the message in the terminal TaskStatus, if it has one.
artifacts Yes {"artifactId", "digest"} for each artifact of the final task, in task order, excluding receipt artifacts (section 10.4). May be empty.

Interim agent messages, such as an INPUT_REQUIRED prompt, are not committed in v0.1. A2A allows clients to miss status messages during streaming [A2A, section 3.7], so a client could not always check such commitments before countersigning (section 16, Q4).

5.7. payments

Each entry is {"protocol", "reference", "receiptDigest"?, "amount"?}. protocol is ap2, x402 or other. reference is the payment’s identifier in that protocol (AP2 payment_id, x402 transaction). receiptDigest is the digest of that protocol’s own payment receipt (section 6.6). amount is {"value": <decimal string>, "currency": <ISO 4217 code>}. Amounts are strings, never JSON numbers, so they never depend on floating-point formatting. amount is OPTIONAL because the payment receipt already carries it (section 15).

The Emissar Receipt does not replace the payment protocol’s receipt. It binds the task to it. For AP2 v0.2, which returns a merchant-signed Checkout Receipt and a processor-signed Payment Receipt, the Emissar Receipt records which of those belongs to which A2A task.

5.8. timestamps

Member Meaning
acceptedAt When the server accepted the message that created the task.
terminalAt The timestamp of the terminal TaskStatus, truncated to milliseconds. The field is optional in A2A; a server issuing receipts MUST set it.
issuedAt When the server built the receipt payload.
countersignBy issuedAt plus the server’s countersignWindowSeconds (section 10.1).

All four MUST be UTC with exactly three fractional digits and a Z suffix (YYYY-MM-DDTHH:mm:ss.sssZ), the format A2A recommends [A2A, section 5.6.1]. They MUST satisfy acceptedAt <= terminalAt <= issuedAt < countersignBy. Implementations compare instants, never strings. Each signature also carries its own signedAt (section 7.2).

5.9. Openings

An openings object holds the salts for one receipt: {"receiptId", "request": {<messageId>: salt}, "outcome": {"statusMessage"?: salt, "artifacts": {<artifactId>: salt}}, "principals"?: {"server"?: salt, "client"?: salt}}. Openings are for the parties only. They MUST NOT appear in a receipt document, in a log, or in any response to someone other than a party. Section 13 covers their deletion.

5.10. JSON Schema

receipt-v0.1.schema.json (JSON Schema 2020-12) validates a receipt document at its root. Its $defs also cover decoded protected headers, openings, refusals, anchors, the Agent Card extension entry, the request metadata, the receipt artifact metadata, and the Retrieval API bodies. The schema checks shape only. Signatures, digests, chains and proofs need section 12.

5.11. Example

The payload of receipt 1 from the demo (Appendix B). Parties and task are illustrative; the keys are the public RFC test keys.

{
  "version": "0.1",
  "receiptId": "0199a1c2-7d4e-7b31-9a0f-3c5e2d1b4a60",
  "digestAlg": "sha-256",
  "task": {
    "endpoint": "https://freight.example.net/a2a/v1",
    "taskId": "5f1c2d9e-8b7a-4e3c-9d21-6a0b4c7e2f13",
    "contextId": "c0a1e2d3-4b5c-4d6e-8f70-81a2b3c4d5e6"
  },
  "parties": {
    "server": {
      "agentCard": { "url": "https://freight.example.net/.well-known/agent-card.json", "digest": "ni:///sha-256;QP1uJLtxLHnl4gMnLTwfHl_uS93YQ74tq3u8lPQUrF4" },
      "signingKey": { "kid": "freight-receipts-2026-09", "jkt": "oKIywvGUpTVTyxMQ3bwIIeQUudfr_CkLMjCE19ECD-U" },
      "principal": { "type": "organization", "id": "freight.example.net" }
    },
    "client": {
      "agentCard": { "url": "https://buyer.example.com/.well-known/agent-card.json" },
      "authentication": { "scheme": "oauth2", "subject": "buyer-agent-client-7" },
      "signingKey": { "kid": "buyer-agent-2026-09", "jkt": "kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k", "jku": "https://buyer.example.com/.well-known/receipt-keys.json" },
      "principal": { "type": "organization", "id": "buyer.example.com" }
    }
  },
  "request": { "messages": [ { "messageId": "3b8f0c2e-5d1a-4f6b-9e7c-2a4d6f8b0c1e", "digest": "ni:///sha-256;eUPaB322iJYC4HH6L-sAWn57N3hQZLvYRNauwmwkHnE" } ] },
  "outcome": {
    "state": "TASK_STATE_COMPLETED",
    "statusMessage": { "messageId": "9d0e1f2a-3b4c-4d5e-8f6a-7b8c9d0e1f2a", "digest": "ni:///sha-256;J2GvM9NGvf3xlrF1uz7AS5d4hplQS_5EdDX1fl8PcsY" },
    "artifacts": [ { "artifactId": "quote-Q-5521", "digest": "ni:///sha-256;owWmKJEugAFqylcpIcFYlgUdurZrPxnWjyBdUPzBo2k" } ]
  },
  "timestamps": {
    "acceptedAt": "2026-09-26T14:03:10.120Z",
    "terminalAt": "2026-09-26T14:03:12.004Z",
    "issuedAt": "2026-09-26T14:03:12.050Z",
    "countersignBy": "2026-09-27T14:03:12.050Z"
  }
}

Receipt digest: ni:///sha-256;H3q3chQq7RN83Y8dwl2u7NITy9-pIjAwfopO1HrRDj0. The server’s decoded protected header:

{
  "alg": "ES256",
  "kid": "freight-receipts-2026-09",
  "typ": "emissar-receipt+json",
  "crit": ["https://emissar.ai/ext/receipt/v1"],
  "https://emissar.ai/ext/receipt/v1": {
    "role": "server",
    "seq": 1,
    "prev": "ni:///sha-256;qAJNRBhETnh4TFB6obJl6wP9ZdE0Niu4EwRLt3zlrdM",
    "signedAt": "2026-09-26T14:03:12.051Z"
  }
}

node receipt-demo.mjs --json prints the complete documents, including receipt 2 with a mandate reference, a payment reference and an anchor.

6. Canonicalization and Digests

6.1. Canonical Form

Every signed or hashed JSON value in this document is serialized with RFC 8785 (JCS). JCS sorts object members by UTF-16 code units, writes no whitespace, and serializes strings and numbers the way ECMAScript does. The value MUST be I-JSON [RFC7493]. Receivers MUST parse receipt documents strictly and reject duplicate member names, because a lenient parser that keeps the last duplicate would check a different object from the one a strict parser sees. The receipt payload contains no JSON numbers. The integers elsewhere in the format (sequence numbers, tree sizes, leaf indexes) MUST NOT exceed 2^53 − 1. JCS applies no Unicode normalization; strings are compared as code units.

Content inside commitments (section 6.5) may carry arbitrary JSON from A2A data parts. If a projection has no canonical form, for example because it contains an unpaired surrogate, the server MUST NOT issue a receipt for the task and SHOULD report uncanonicalizable_content (section 10.6). Parties SHOULD send numbers that do not fit an IEEE 754 double as strings, as RFC 8785 recommends.

RFC 8785 is an Informational RFC from the Independent Submission stream. This document makes it normative, as A2A does for Agent Card signing [A2A, section 8.4.1]. Unlike Agent Card signing, receipts need no default-value pruning: the payload is defined here, not by a Protocol Buffers schema, and optional members are simply absent.

6.2. Digest Format and Algorithm Agility

Every digest is an RFC 6920 ni URI with an empty authority: ni:///<alg>;<B64U(digest)>. <alg> is a name from the IANA Named Information Hash Algorithm Registry. This version allows:

Name Support
sha-256 MUST implement; the default digestAlg
sha-384, sha-512 MAY implement

Truncated suites such as sha-256-128 MUST NOT be used. Verifiers MUST reject a digest whose algorithm they do not support or whose length does not match the algorithm.

digestAlg selects the algorithm for the receipt digest, every commitment, every rawDigest, and the chain values of the signing keys (section 8). Digests of external credentials and payment receipts (section 6.6) follow the external protocol’s algorithm but are still written as ni URIs. Merkle anchoring always uses SHA-256 (section 9.2). A new algorithm is added by listing it here in a later version; nothing else in the format changes.

6.3. Receipt Digest

receiptDigest = ni(digestAlg, H(UTF8(JCS(receipt))))

It covers the payload only. It does not change when the client countersigns or when anchors are added, so it identifies the receipt for its whole life. The same bytes, base64url-encoded, are the JWS Payload.

6.4. Commitments

commit(s, v) = ni(digestAlg, H(UTF8(JCS({"salt": s, "value": v}))))

s is B64U of at least 16 octets from a cryptographically secure random generator. The server MUST generate a fresh salt for every commitment and MUST NOT reuse one. A salted commitment reveals nothing about low-entropy content to someone without the salt, which is the property RFC 9901 relies on for selective disclosure. That document also recommends at least 128 bits of salt.

Opening a commitment means presenting s and the original A2A object. The verifier recomputes the projection (section 6.5) and the commitment.

6.5. Projections of A2A Objects

Commitments cover a projection of each A2A object, not its wire form. SDKs rewrite metadata, extension lists and default values differently, and a digest over the wire form would depend on the SDK.

Object Projection (members in the projection) Excluded
Message messageId, role, parts (projected), referenceTaskIds if non-empty contextId, taskId (recorded in receipt.task), metadata, extensions
Part Exactly one of: text; data; url; or rawDigest = ni(digestAlg, H(decoded raw bytes)). Plus mediaType and filename if non-empty metadata
Artifact artifactId, parts (projected), name and description if non-empty metadata, extensions

Artifacts delivered in chunks are projected in their final assembled form, as GetTask returns them. An artifact whose extensions list contains EXT is a receipt artifact and is excluded from outcome.artifacts. Hashing raw file bytes into rawDigest keeps projections small and lets a party open a commitment without resending the file.

Projections are defined on the A2A v1.0 data model. A v0.3 implementation maps names first: parts with kind text, file or data; file.bytes to raw, file.uri to url, file.name to filename, file.mimeType to mediaType; roles user and agent to ROLE_USER and ROLE_AGENT; lowercase states such as completed to TASK_STATE_COMPLETED.

6.6. Digests of External Credentials and Receipts

Object Digest input
JWS or JWT in compact serialization ASCII(compact string)
SD-JWT credential (AP2 v0.2 mandates, Verifiable Intent) The US-ASCII bytes of the presented SD-JWT up to and including the last ~, excluding any Key Binding JWT, hashed with the credential’s _sd_alg. This is how RFC 9901 computes sd_hash and how AP2 v0.2 computes a Mandate Receipt’s reference, so the base64url part of the ni URI equals the AP2 value.
JSON credential without a compact form UTF8(JCS(credential))
AP2 v0.2 Checkout or Payment Receipt (JWT) ASCII(compact JWT)
x402 settlement response (one element of x402.payment.receipts) UTF8(JCS(element))

6.7. Key Thumbprints

jkt is the RFC 7638 JWK thumbprint with SHA-256, base64url-encoded (43 characters). It covers only the required public members (crv, kty, x, y for EC; crv, kty, x for OKP). Because the thumbprint is inside the signed payload, a verifier can use any copy of the public key, current or archived, and know it is the one the parties named.

7. Signatures

7.1. Serialization

Each element of signatures is {"protected", "signature"}, a general-serialization JWS signature entry [RFC7515, section 7.2.1]. The JWS Signing Input is ASCII(protected || "." || B64U(UTF8(JCS(receipt)))). The unprotected header member MUST NOT be present; verifiers MUST reject an entry that has one. All parameters are integrity-protected.

The RFC 7797 unencoded-payload option (b64: false) is not used. The payload is detached, so base64url encoding happens only inside the signing computation and adds nothing on the wire. Keeping the default gives Emissar one signing-input form, the one Agent Card signing uses, and works with JOSE libraries that do not implement RFC 7797.

7.2. Protected Header

Parameter Req. Value
alg Yes ES256, ES384 or Ed25519 (section 7.3).
kid Yes MUST equal parties[role].signingKey.kid.
typ Yes emissar-receipt+json. Explicit typing per RFC 8725, section 3.11.
crit Yes Exactly ["https://emissar.ai/ext/receipt/v1"].
https://emissar.ai/ext/receipt/v1 Yes {"role", "seq", "prev", "signedAt"} (below).
jku No JWK Set URL hint; same rules as signingKey.jku.

No other parameters are allowed. The receipt parameter uses the extension URI as its name, a collision-resistant public name under RFC 7515, section 4.2. Listing it in crit makes a generic JWS verifier that does not implement this document fail closed [RFC7515, section 4.1.11]. Its members:

Member Content
role "server" or "client".
seq This receipt’s position in the signing key’s chain, starting at 1 (section 8).
prev The key’s chain value before this receipt, h(seq − 1).
signedAt When this party signed, in the section 5.8 format.

7.3. Algorithms

Producers MUST sign with ES256, ES384 or Ed25519. Verifiers MUST implement ES256 and Ed25519 and MAY implement ES384. Ed25519 is the fully specified name from RFC 9864, which deprecates the polymorphic EdDSA. EdDSA, RSA algorithms, HMAC algorithms and none MUST NOT be used or accepted. HMAC cannot provide two-party evidence because both sides would hold the key. ECDSA signatures are raw R || S (64 octets for ES256) [RFC7518, section 3.4].

A receipt-signing key SHOULD be used only for receipts and refusals. It MUST NOT be used to sign Agent Cards. The distinct typ values and the crit parameter block cross-use, but separate keys also keep compromise and rotation separate.

7.4. Signing Order and What Each Signature Covers

The server signs first, when it issues the receipt, and its entry is signatures[0]. The client countersigns later and its entry is signatures[1]. Verifiers MUST identify entries by role, not by position.

Each signature covers its own protected header (role, chain position, signing time) and the same payload bytes. Neither covers the other’s signature, which would add nothing: both commit to the identical payload. Verification needs no particular order, and neither party can change the payload after the other has signed.

Signature The signer attests
Server The task ran as recorded; it authenticated the client as recorded; it holds openings for every commitment; this is its only receipt for the task.
Client The commitments match what it sent and received; the parties and card are the ones it dealt with; the times are within its tolerance; the mandates and payments are the ones it presented or made.

7.5. Countersigning

Before countersigning, the client MUST:

  1. Verify the server signature (section 12, steps 1 to 9).
  2. Check that task.endpoint is the URL it called and that taskId and contextId are its task.
  3. Check that parties.client.signingKey is its own key and that authentication.subject is the identity it used.
  4. Check parties.server.agentCard.url and, if present, digest against the card it relied on.
  5. Open every request commitment against the messages it sent, and every outcome commitment against the final task from GetTask, not only from stream events.
  6. Check each timestamp against its own observations, within its clock skew tolerance (default 300 seconds).
  7. Check each mandates digest against the credential it presented and each payments entry against its own payment records.

If every check passes, the client signs with role client and the next position on its own chain, and submits the entry before countersignBy (section 10.5).

7.6. Refusal and Partial Signing

A receipt ends in one of four states:

State Meaning
countersigned Both signatures verify. Evidence both parties agreed to the record.
issued Only the server signature, and the window is open.
expired Only the server signature, and countersignBy has passed.
refused Only the server signature, plus a signed refusal from the client.

An issuer-signed receipt proves what the server asserted, which is proof of origin in RFC 4949’s terms, but not that the client agreed. Verifiers MUST report which state they found. A policy MAY require countersigned.

If a check in section 7.5 fails, the client SHOULD post a refusal: {"refusal": {"receiptId", "receiptDigest", "reason", "detail"?, "refusedAt"}, "signatures": [entry]}. The entry is signed over UTF8(JCS(refusal)) with the client’s receipt key, typ emissar-receipt-refusal+json, and no chain parameter. Reasons: request_mismatch, outcome_mismatch, identity_mismatch, timestamp_out_of_range, unknown_task, other. A refusal closes the countersign window. The server MUST store it with the receipt and return it to either party. The server MUST NOT alter or reissue the refused receipt for the same task (section 5.3). Disputes then follow the business’s own process with both documents as evidence.

A client that asked for a receipt and got none keeps its own audit trail. The missing receipt is itself a fact it can raise. A receipt with a client signature and no valid server signature is invalid; v0.1 has no client-issued receipts (section 16, Q5).

8. Hash Chaining

8.1. Chain Values

Each receipt-signing key has one chain. With H the key’s digest algorithm and jkt its thumbprint:

  • h(0) = H(UTF8(jkt))
  • h(n) = H(raw(h(n − 1)) || raw(d_n)), where d_n is the receipt digest of the n-th receipt the key signed.

The n-th signature by the key carries seq = n and prev = h(n − 1). Anyone holding that receipt can compute h(n). This is the chain formula from the published hash-chain page, with a defined starting value that binds the chain to one key. Without it, two parties that sign the same receipts would share chain values.

Each signature commits, through prev, to every earlier receipt signed with the key, including their chain positions. All receipts in one chain MUST use the same digestAlg.

8.2. Concurrency and Key Rotation

Signing with one key is serial: seq values MUST increase by exactly one, with no gaps and no reuse. A party that needs parallel signing uses several keys, one chain each, all published in its JWK Set. A counterparty holds a head for each key it has seen.

Key rotation starts a new chain at h(0) of the new key. In v0.1, seq 1 MUST carry prev = h(0). Linking a retired chain to its successor is an open question (section 16, Q6). Anchoring the retired key’s last head (section 9.1) records where the old chain ended.

8.3. Checking a Chain

Given consecutive receipts signed with key k, a verifier checks that each seq is one more than the last and that each prev equals the h computed from the previous receipt. For seq 1 it checks prev = h(0).

Counterparties hold chain values without extra messages: every receipt a client keeps fixes the server’s h(n) at that position, and every countersigned receipt the server keeps fixes the client’s. A client can ask the server’s Retrieval API for chain entries (GET /chains/{kid}/entries/{seq}). A returned head that differs from the value the client computed proves the chain was rewritten at or before that position. Appendix B shows this: the issuer re-signs an altered receipt 1 with a valid signature, and receipt 2’s prev exposes it. A gap, or two receipts with the same kid and seq, is evidence of deletion or a fork.

9. Transparency Anchoring

Anchoring is OPTIONAL. Chains let the two parties detect rewriting. A log lets anyone detect it, and it bounds when a receipt existed with a clock neither party controls.

9.1. Leaf Encoding

A receipt leaf is UTF8(JCS({"digest": receiptDigest, "type": "emissar-receipt"})). A chain-head leaf is UTF8(JCS({"head": h(n), "jkt": jkt, "seq": n, "type": "emissar-receipt-chain"})). Anchoring a chain head anchors every earlier receipt in that chain, so a party can anchor one head per interval instead of every receipt. The log receives digests only, never receipts, content or openings.

9.2. Log Structure and Proofs

A log is the RFC 9162 Merkle tree (section 2.1): leaf hash SHA-256(0x00 || leaf), node hash SHA-256(0x01 || left || right), with inclusion proofs (section 2.1.3) and consistency proofs (section 2.1.4). RFC 9942 registers this structure for COSE as RFC9162_SHA256. The log signs each tree head {"log", "treeSize", "rootHash", "timestamp"} as a detached JWS over its RFC 8785 form, with typ emissar-tree-head+json. Verifiers MUST obtain the log’s public key out of band. RFC 9162 is an Experimental RFC. This document uses only its tree definition and proof algorithms.

9.3. Anchor Objects

Member Content
type rfc9162-sha256
log The log’s URL (its identity)
leafIndex Index of the receipt leaf
inclusionPath Base64url node hashes, leaf to root
treeHead, treeHeadSignature The signed tree head the proof leads to

To verify: check treeHeadSignature with a trusted key for log; recompute the leaf hash from the receipt digest; run the RFC 9162 section 2.1.3.2 algorithm with leafIndex, treeHead.treeSize and inclusionPath; compare the result with treeHead.rootHash. A verifier that already holds an older tree head for the same log SHOULD request a consistency proof, because a log could otherwise show different histories to different parties. treeHead.timestamp gives an upper bound on when the receipt existed.

9.4. Relation to SCITT

SCITT’s architecture, RFC 9943 (Standards Track, June 2026), defines Transparency Services that register Signed Statements and return Receipts. RFC 9942 (June 2026) encodes those as COSE Receipts carrying proofs over a verifiable data structure, including RFC9162_SHA256. RFC 9995 (July 2026), the COSE Hash Envelope, lets an issuer sign a statement over a hash, the approach RFC 9943 section 6.2 describes for large or sensitive statements. The SCITT Reference APIs (draft-ietf-scitt-scrapi-11, June 2026) is in the RFC Editor queue, not yet an RFC. It defines POST /entries for registration and GET /entries/{id} for fetching a Receipt.

A party that wants SCITT anchoring builds a Signed Statement: a COSE_Sign1 using the Hash Envelope, whose payload is raw(receiptDigest) and whose preimage content type is application/emissar-receipt+json. It registers the statement with a Transparency Service and stores the statement and the returned COSE Receipt as an anchor of type scitt-cose-receipt. Verification follows RFC 9942: apply the inclusion proof to the candidate entry, then check the COSE_Sign1 signature. The Transparency Service’s registration policy and leaf format are its own.

Emissar Receipts are JSON with JWS, not COSE. A2A and Agent Card signing are JSON and JOSE, and parties already hold JOSE keys; the Hash Envelope keeps the COSE route available without a second receipt format. In SCITT terms, the registering party is the Issuer and the Emissar Receipt is what its statement is about. The word “receipt” collides: this document calls SCITT’s object a transparency receipt or COSE Receipt.

10. Exchange in A2A

10.1. Extension Declaration

A server that issues receipts declares an AgentExtension in capabilities.extensions of its Agent Card:

{
  "uri": "https://emissar.ai/ext/receipt/v1",
  "description": "Issues a signed Emissar Receipt as an artifact when a task reaches a terminal state.",
  "required": false,
  "params": {
    "receiptApi": "https://freight.example.net/receipts/v0",
    "jku": "https://freight.example.net/.well-known/receipt-keys.json",
    "algs": ["ES256", "Ed25519"],
    "digestAlgs": ["sha-256"],
    "countersignWindowSeconds": 86400,
    "logs": ["https://log.example.org/receipts/v1"]
  }
}
Param Req. Content
receiptApi Yes Base URL of the Retrieval API (section 11).
jku SHOULD JWK Set with the server’s current and retired receipt keys.
algs Yes Signature algorithms the server signs with and accepts for countersignatures.
digestAlgs Yes Digest algorithms the server can use.
countersignWindowSeconds Yes 60 to 2,592,000.
logs No Logs the server anchors in.

required SHOULD be false. A server MAY set it to true when its policy only serves clients that countersign; A2A then requires clients to activate the extension or receive ExtensionSupportRequiredError (-32008) [A2A, section 3.3.2].

10.2. Activation and Request Metadata

A client activates the extension through the A2A-Extensions service parameter [A2A, section 3.2.6] (the A2A-Extensions HTTP header in the HTTP bindings; X-A2A-Extensions for v0.3 peers). On the message that creates the task, it sets message.extensions to include EXT and puts request metadata in message.metadata[EXT]:

Member Req. Content
countersign Yes true if the client will countersign.
signingKey If countersign {"kid", "jkt", "jku"?} of its receipt key.
agentCard No The client’s Agent Card URL.
principal No As in section 5.4. For a person, send an identifier the server already holds; the server makes the commitment.

The server uses the metadata from the message that created the task and echoes activation in its response, as the A2A extensions guidance shows. A server that did not echo EXT will not issue a receipt.

10.3. When a Receipt Is Produced

When a task with the extension active reaches a terminal state, the server MUST issue a receipt for TASK_STATE_COMPLETED and SHOULD issue one for FAILED, CANCELED and REJECTED. A rejected refund or a canceled order needs a record as much as a completed one. The interrupted states INPUT_REQUIRED and AUTH_REQUIRED are not terminal and produce no receipt. A request answered with a Message instead of a Task has no taskId and gets no receipt in v0.1 (section 16, Q7).

10.4. Delivery

The server delivers the receipt document as an artifact of the task:

Artifact field Value
artifactId emissar-receipt- followed by the receiptId
name Emissar Receipt
extensions ["https://emissar.ai/ext/receipt/v1"]
parts[0] {"data": <receipt document>, "mediaType": "application/emissar-receipt+json"}
parts[1] {"data": <openings>, "mediaType": "application/emissar-receipt-openings+json"}
metadata[EXT] {"receiptId", "receiptDigest"}

The server fixes the terminal status and its timestamp, builds and signs the receipt, and adds the artifact before or together with the terminal status. In streaming, the TaskArtifactUpdateEvent MUST precede the terminal TaskStatusUpdateEvent, because the stream closes at the terminal state [A2A, section 3.1.2]. Push notifications follow the same order.

Why an artifact:

Option Verdict
Artifact Chosen. A2A says results belong in artifacts and that messages are not a reliable delivery mechanism [A2A, section 3.7]. Artifacts persist in the task, so a client that missed the stream gets the receipt from GetTask. A2A lists artifacts as an extension point [A2A, section 4.6.2].
Metadata on the terminal status message Rejected. Unreliable delivery, and the receipt would sit inside the status message it commits to.
Pointer only, fetched from the API Allowed as a fallback (section 11), but the artifact is the default so a receipt arrives with the result.
New RPC method (method extension) Deferred. It would need separate definitions for JSON-RPC, gRPC and HTTP+JSON.

If the task is visible to principals other than the client (for example a shared context), the server MAY leave out parts[1] and serve openings only through the API.

10.5. Returning the Countersignature

The client posts its signature entry to {receiptApi}/receipts/{receiptId}/signatures. It does not use A2A for this because A2A rejects messages to tasks in a terminal state with UnsupportedOperationError [A2A, section 3.1.1]. The server verifies and appends it (section 11). The client keeps its own countersigned copy whether or not the post succeeds.

10.6. Errors

If the server cannot issue a receipt for a task where the extension is active, it sets task.metadata[EXT] to {"error": <code>}, with code receipt_unavailable, uncanonicalizable_content or unsupported_algorithm. A client that receives no receipt and no error SHOULD fetch the task with GetTask and then query the Retrieval API by taskId before treating the receipt as missing.

11. Retrieval API

receipt-v0.1.yaml (OpenAPI 3.1) defines the API. The issuer serves it at receiptApi, directly or through a host such as an Emissar Front Door.

Operation Purpose
GET /receipts List receipts where the caller is a party. Filters: taskId, contextId, issuedAfter, status; cursor pagination.
GET /receipts/{receiptId} The current receipt document. The response header Emissar-Receipt-Digest carries the digest.
POST /receipts/{receiptId}/signatures Submit the client’s countersignature. Returns 409 if one is already recorded, 410 after countersignBy, 422 if it fails verification. Idempotent for an identical entry.
POST and GET /receipts/{receiptId}/refusals Record or read the client’s signed refusal.
GET /receipts/{receiptId}/openings Salts, for parties only. Returns 410 after content retention ends (section 13).
GET /chains/{kid} The latest chain entry for one of the server’s keys.
GET /chains/{kid}/entries/{seq} One chain entry. Entries outlive deleted documents.

Authentication. The client uses the same security scheme it used for the task: an OAuth 2.0 bearer token or mutual TLS. A server MAY also accept HTTP Message Signatures [RFC9421] made with the key in parties.client.signingKey, which proves the caller holds the receipt key.

Authorization. Only the two parties may read a receipt or its openings. The server maps the caller to parties.client by comparing the authenticated identity with parties.client.authentication. For non-parties the server returns 403, or 404 so that receipt IDs cannot be probed. Chain entries reveal only digests, counts and times, and any authenticated counterparty MAY read them. Third parties such as auditors get receipts from a party, not from this API.

Other rules. Responses carry Cache-Control: private, no-store. Errors are RFC 9457 problem details. The server rate-limits and returns 429 with Retry-After. The API is a convenience; each party’s own copy is its evidence.

12. Verification Algorithm

Input: a receipt document; the verifier’s configuration (accepted algorithms, trusted keys or a key-location policy, clock skew, whether countersigned is required); optionally earlier receipts or chain entries, log keys, openings with the original content, and mandate credentials. Steps 1 to 10 are required. Steps 11 to 14 run when their inputs are available.

  1. Parse the document strictly, rejecting duplicate member names. Validate it against the JSON Schema. Require receipt.version to be "0.1".
  2. Compute P = UTF8(JCS(receipt)). If receipt has no canonical form, reject. Compute receiptDigest with receipt.digestAlg.
  3. Require one or two entries in signatures. Reject any entry with a header member.
  4. For each entry, base64url-decode protected as UTF-8 JSON. Require typ = emissar-receipt+json, crit = [EXT], a valid EXT parameter, and an alg on the verifier’s allowlist.
  5. Take role from the EXT parameter. Reject a second entry with the same role. Require kid = parties[role].signingKey.kid.
  6. Resolve the public key for kid from the verifier’s key store, or from jku or params.jku only if the URL passes the verifier’s key-location policy (section 14.7). Compute its RFC 7638 thumbprint and require it to equal parties[role].signingKey.jkt. Require the key to be valid at signedAt and not reported compromised before it (section 14.1).
  7. Verify the signature over ASCII(protected || "." || B64U(P)).
  8. If any check in steps 4 to 7 fails for any entry, reject the document. Do not fall back to the remaining signature; a failed entry means the document was altered or corrupted, and the verifier should obtain a clean copy.
  9. Require a valid server entry. Require acceptedAt <= terminalAt <= issuedAt < countersignBy; server signedAt >= issuedAt − skew; and, if present, client signedAt >= server signedAt − skew and client signedAt <= countersignBy + skew.
  10. Report countersigned if a valid client entry exists, otherwise issued, expired or refused as known from context. If policy requires countersigned and it is missing, reject.
  11. Chain (section 8.3): with the previous receipt or chain entry for a signer’s key, require seq to follow it and prev to equal the computed h. For seq 1, require prev = h(0).
  12. Anchors (section 9.3): verify each anchor whose log the verifier trusts. An anchor that fails verification is reported, and the verifier MUST NOT treat the receipt as anchored by that log.
  13. Openings: for each disclosed item, recompute the projection and commit(salt, projection), and require it to equal the committed digest.
  14. Mandates: hash each presented credential as in section 6.6 and require a matching mandates entry. Where the Mandate format binds the agent’s key by thumbprint, require that binding to equal parties.client.signingKey.jkt.

Output: validity, state, receiptDigest, per-signer kid, seq and signedAt, and which of steps 11 to 14 ran and passed. The reference implementation is verifyReceipt, verifyChain, verifyAnchor and checkOpenings in receipt-demo.mjs. It trusts a fixed key store, uses a lenient JSON parser, and skips step 1’s schema validation, which validate.py performs separately.

13. Retention and Deletion

A receipt separates content from evidence so that each can have its own lifetime:

Layer Contents Held by Lifetime
Content A2A messages and artifacts; principal identifiers Each party, in its own systems The party’s content retention policy
Openings Salts Parties only No longer than the content they open
Receipt document IDs, parties, digests, timestamps, signatures, anchors Both parties The party’s record retention policy, usually longer
Chain entry kid, jkt, seq, prev, head, receiptId, receiptDigest, signedAt Each signer The life of the key’s chain
Log leaf A digest The log Permanent

Crypto-shredding. Parties SHOULD encrypt stored content and openings under data keys scoped to one receipt or one principal, and delete by destroying the key. NIST SP 800-88 Rev. 2 (September 2025) covers cryptographic erase as a sanitization technique. After the salts are gone, a commitment cannot be opened and cannot be tested against guesses, but the receipt still verifies: its signatures cover digests, not content.

Deleting a receipt document. Allowed under the party’s policy. The chain entry stays, marked documentDeleted, so later receipts still verify. A deleted chain entry leaves a gap no verifier can bridge, so parties MUST NOT delete chain entries while either party retains any later receipt in the chain.

Periods. This document sets no retention period. Obligations differ by sector and jurisdiction: record-keeping rules, limitation periods for disputes, and data-protection limits on keeping personal data, such as storage limitation and erasure under the GDPR (Articles 5(1)(e) and 17) and PIPEDA’s limiting-retention principle in Canada. NIST SP 800-53 control AU-11 treats audit-record retention as an organization-defined period. AP2 v0.2 likewise leaves dispute-evidence retention out of scope. Each party decides its own periods with its own legal advisers; nothing here is legal advice. An Emissar-hosted Ledger must publish its periods and enforce them in code.

Hashes and personal data. A digest of personal data may still count as personal data in some regimes. The design reduces that risk: people appear only as salted commitments, and destroying the salt leaves nothing to link. Whether this is enough in a given regime is a legal question (section 16, Q10).

14. Security Considerations

14.1. Key Compromise

An attacker with a party’s receipt key can sign receipts as that party and choose signedAt. Chains limit the damage: forged receipts must extend the real chain, so they either fork it, which counterparties holding later heads detect, or appear after the last legitimate position. Anchors made before the compromise stay valid evidence. Verifiers need to know when a key stopped being trustworthy. v0.1 requires parties to keep retired public keys available, in their JWK Set or an archive, for the retention period of any receipt signed with them. It requires them to publish the time from which a compromised key must not be trusted. The publication format is an open question (Q8), to be aligned with Mandate revocation.

14.2. Backdating and Clock Trust

issuedAt, terminalAt and each signedAt are self-asserted. The countersigning client checks the server’s times against its own clock. The server’s API checks the client’s signedAt against the window. Chains fix order but not absolute time. For time that neither party controls, anchor the receipt or chain head and use the signed tree head’s timestamp as an upper bound, or obtain an RFC 3161 timestamp token over the receipt digest. Verifiers MUST allow bounded skew (default 300 seconds) and MUST NOT accept a countersignature more than the skew past countersignBy.

14.3. Equivocation

A server could issue two receipts for one task, one to the client and another for its own records. Countermeasures: at most one receipt per (endpoint, taskId) (section 5.3); both receipts would consume chain positions the client can query; and a countersigned receipt binds the client to one version only. A log could present conflicting views to different parties (RFC 9162, section 11.3; RFC 9943 calls this equivocation). Verifiers holding earlier tree heads SHOULD check consistency proofs, and parties SHOULD compare tree heads when they exchange receipts.

14.4. Replay

A signature cannot move to another receipt: each covers the full payload, including receiptId, task and both parties’ key thumbprints. An old receipt cannot pass as evidence for a new task, because taskId and endpoint differ. The client’s request metadata could be copied by an attacker to make a server name the victim’s key in a receipt. That produces only an issuer-signed claim, which never binds the victim (section 5.4). Reuse of a single-use mandate across tasks shows up as the same mandate digest in two receipts. Enforcing single use is Mandate’s job.

14.5. Partial Signing

A client can withhold its countersignature to keep a record in dispute. That is its right, and the issued, expired and refused states record it. A server can withhold the receipt or drop the countersignature from its copy. The client holds its own countersigned copy and its own audit trail, and chain gaps expose deletions. Verifiers MUST NOT present an issuer-signed receipt as agreed by both sides.

14.6. Commitment Guessing

Without salts, a receipt holder could test guesses such as “the refund amount was 40.00” against a digest. Salts of 128 bits or more make this infeasible (section 6.4). Leaked openings for one item reveal nothing about other items, because each has its own salt.

14.7. Key Discovery

jku values are chosen by the signer. An attacker who alters a receipt and re-signs it with its own key can point jku at its own key set. The jkt check in step 6 stops this only if the verifier trusts the jkt values, which come from the same document. So verifiers MUST decide key trust independently: pinned keys, the counterparty’s JWK Set at a location tied to its Agent Card host, or a registry such as the future Verify. The published signed-agent-cards guide makes the same point for cards.

14.8. Canonicalization

Canonicalization bugs cause false rejections, and a parser that is lenient about duplicate names creates two readings of one document. Implementations SHOULD test against the RFC 8785 examples; the demo’s self-tests and Emissar’s own test suite do so. Projections exclude metadata precisely because SDKs rewrite it.

14.9. Denial of Service

Receipt documents and API bodies are bounded by the schema (at most 1,000 commitments, 2 signatures, 8 anchors, 64 path nodes). Servers SHOULD cap artifact sizes before hashing, rate-limit the Retrieval API, and verify countersignatures before storing anything.

15. Privacy Considerations

What a receipt reveals to a holder. That two agents dealt with each other, when, under which organizations, the client’s authentication subject, message and artifact IDs, the final state, and, if included, payment amounts. It reveals no content. Implementations MUST NOT put personal data in IDs or in authentication.subject, and SHOULD omit payments[].amount when the payment protocol’s receipt already records it.

Who sees what. Parties see receipts and openings. An auditor sees whatever receipt and openings a party chooses to give it. A log sees digests and the timing of submissions; batching and chain-head anchoring reduce timing correlation.

Selective disclosure. Each message and artifact has its own commitment. A party proves one fact, such as “the booking artifact said B-20931”, by disclosing that artifact and its salt, and keeps the rest closed. People are referenced only by commitment, and the commitment is opened only when needed.

Volume leakage through chains. A signature’s seq tells each counterparty how many receipts the key has signed. A server that considers its volume confidential SHOULD spread signing over several keys (section 8.2). Per-counterparty chains would remove the leak but weaken cross-counterparty equivocation checks (Q3).

Openings in transit. Openings reach the client inside the task. When other principals can read the task, the server keeps them to the API (section 10.4).

16. Open Questions

# Question Default in this draft
Q1 Publication, and what the extension URI serves. A2A’s guidance says the spec should be hosted there. Settled: published on 2026-10-05; the URI redirects to this document
Q2 Register application/emissar-receipt+json and the openings type, or use application/json with a profile? Unregistered names
Q3 One chain per key, per key and counterparty, or per tenant? Affects contention, volume leakage and equivocation detection. Per key
Q4 Commit to interim agent messages (for example INPUT_REQUIRED prompts) given A2A’s unreliable message delivery? Not committed
Q5 Allow client-issued receipts when the server does not support the extension? No
Q6 Link a retired key’s chain to its successor with a signed rotation statement? No link; anchor the last head
Q7 Receipts for tasks answered with a Message and no Task? None
Q8 Key status and compromise publication: status list, JWK Set fields, or a transparency log. Align with Mandate revocation. Parties publish; format TBD
Q9 Extension points in the payload versus a closed schema with version bumps. Closed
Q10 Are salted commitments with destroyable salts enough, under GDPR and PIPEDA, to keep receipts after an erasure request? Under legal review
Q11 Does Emissar run a public receipt log, rely on SCITT services, or leave anchoring to parties? Parties choose
Q12 Should the client’s receipt key be the key its Mandate is bound to, so step 14 is automatic? Allowed, not required
Q13 Corrections: a signed superseding receipt, or never? Never in v0.1
Q14 Whether the countersign channel should also exist as an A2A method extension. HTTPS API only

17. References

Accessed 2026-09-26 unless marked otherwise. “Cited via” means taken from a source list on a published Emissar page and not re-fetched for this draft.

17.1. Normative References

Ref Title URL
[A2A] Agent2Agent Protocol Specification (main, last changed 2026-08-28; latest release v1.0.1, 2026-05-28), sections 3.1.1, 3.1.2, 3.2.6, 3.3.2, 3.4.2, 3.7, 4.6, 5.6.1, 8.4.1 https://github.com/a2aproject/A2A/blob/main/docs/specification.md
[A2A-PROTO] a2a.proto: Task, TaskStatus, TaskState, Message, Part, Artifact, AgentExtension https://github.com/a2aproject/A2A/blob/main/specification/a2a.proto
[RFC2119] Key words for use in RFCs to Indicate Requirement Levels https://www.rfc-editor.org/rfc/rfc2119
[RFC8174] Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words https://www.rfc-editor.org/rfc/rfc8174
[RFC8785] JSON Canonicalization Scheme (JCS), Informational https://www.rfc-editor.org/rfc/rfc8785
[RFC7493] The I-JSON Message Format https://www.rfc-editor.org/rfc/rfc7493
[RFC7515] JSON Web Signature (JWS), sections 4.1.11, 4.2, 7.2.1, Appendix F https://www.rfc-editor.org/rfc/rfc7515
[RFC7518] JSON Web Algorithms (JWA), section 3.4 https://www.rfc-editor.org/rfc/rfc7518
[RFC8037] CFRG ECDH and Signatures in JOSE (OKP keys) https://www.rfc-editor.org/rfc/rfc8037
[RFC9864] Fully-Specified Algorithms for JOSE and COSE (October 2025) https://www.rfc-editor.org/rfc/rfc9864
[RFC7638] JSON Web Key (JWK) Thumbprint https://www.rfc-editor.org/rfc/rfc7638
[RFC6920] Naming Things with Hashes; IANA Named Information Hash Algorithm Registry https://www.rfc-editor.org/rfc/rfc6920 ; https://www.iana.org/assignments/named-information/
[RFC4648] The Base16, Base32, and Base64 Data Encodings https://www.rfc-editor.org/rfc/rfc4648
[RFC9162] Certificate Transparency Version 2.0, section 2.1 (Experimental) https://www.rfc-editor.org/rfc/rfc9162
[RFC9562] Universally Unique IDentifiers (UUIDs) https://www.rfc-editor.org/rfc/rfc9562
[RFC3339] Date and Time on the Internet: Timestamps https://www.rfc-editor.org/rfc/rfc3339
[RFC9457] Problem Details for HTTP APIs https://www.rfc-editor.org/rfc/rfc9457

17.2. Informative References

Ref Title URL
[A2A-EXT] A2A documentation: Extensions (activation, required extensions, hosting the spec at the URI) https://github.com/a2aproject/A2A/blob/main/docs/topics/extensions.md
[RFC7797] JWS Unencoded Payload Option https://www.rfc-editor.org/rfc/rfc7797
[RFC8725] JSON Web Token Best Current Practices, section 3.11 https://www.rfc-editor.org/rfc/rfc8725
[RFC9901] Selective Disclosure for JSON Web Tokens (November 2025), sections 4.3.1, 9.3 https://www.rfc-editor.org/rfc/rfc9901
[RFC9943] An Architecture for Trustworthy and Transparent Digital Supply Chains (SCITT), Standards Track, June 2026 https://www.rfc-editor.org/rfc/rfc9943
[RFC9942] CBOR Object Signing and Encryption (COSE) Receipts, Standards Track, June 2026 https://www.rfc-editor.org/rfc/rfc9942
[RFC9995] COSE Hash Envelope, Standards Track, July 2026 https://www.rfc-editor.org/rfc/rfc9995
[SCRAPI] SCITT Reference APIs, draft-ietf-scitt-scrapi-11 (26 June 2026; in the RFC Editor queue per the Datatracker) https://datatracker.ietf.org/doc/draft-ietf-scitt-scrapi/
[RFC3161] Internet X.509 PKI Time-Stamp Protocol (TSP) https://www.rfc-editor.org/rfc/rfc3161
[RFC9421] HTTP Message Signatures https://www.rfc-editor.org/rfc/rfc9421
[RFC4949] Internet Security Glossary, Version 2 (non-repudiation) https://www.rfc-editor.org/rfc/rfc4949
[AP2] Agent Payments Protocol v0.2 (v0.2.0 released 2026-04-28): specification, Agent Authorization (Mandate Receipt), Checkout and Payment Receipt schemas https://github.com/google-agentic-commerce/AP2/tree/v0.2.0/docs/ap2 ; https://github.com/google-agentic-commerce/AP2/tree/v0.2.0/code/sdk/schemas/ap2
[X402-A2A] x402 v2 transport: A2A (x402.payment.receipts) https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/a2a.md
[VI] Verifiable Intent specification, version 0.1-draft (2026-02-18), Apache-2.0 https://github.com/agent-intent/verifiable-intent
[FIDO-AGENTIC] FIDO Alliance, “FIDO Alliance to Develop Standards for Trusted AI Agent Interactions” (2026-04-28): Agentic Authentication TWG; AP2 and Verifiable Intent contributed to the Payments TWG https://fidoalliance.org/fido-alliance-to-develop-standards-for-trusted-ai-agent-interactions/
[SP800-88] NIST SP 800-88 Rev. 2, Guidelines for Media Sanitization (September 2025) https://csrc.nist.gov/pubs/sp/800/88/r2/final
[SP800-53] NIST SP 800-53 Rev. 5 controls AU-10, AU-11 (cited via the audit-trail and receipt glossary pages) https://github.com/usnistgov/oscal-content/blob/main/nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_catalog.json
[GDPR] Regulation (EU) 2016/679, Articles 5(1)(e) and 17 (cited via the data-retention and GDPR glossary pages) https://eur-lex.europa.eu/eli/reg/2016/679/oj/eng
[PIPEDA-P5] OPC Canada, PIPEDA principle 5: limiting use, disclosure and retention (cited via the data-retention glossary page) https://www.priv.gc.ca/en/privacy-topics/privacy-laws-in-canada/the-personal-information-protection-and-electronic-documents-act-pipeda/p_principle/principles/p_use/
[CROSBY] Crosby and Wallach, Efficient Data Structures for Tamper-Evident Logging, USENIX Security 2009 (cited via the tamper-evident-log page) https://www.usenix.org/legacy/event/sec09/tech/full_papers/crosby.pdf

Appendix A. Consistency with Published Emissar Pages

Page Statement This draft
Ledger module (https://emissar.ai/product/ledger) Parties, verified identities, mandate used, request, outcome, timestamps; both sides sign; either can retrieve; anyone holding it can check its hash Sections 5.4, 5.5, 5.6, 5.8, 7, 11, 6.3. “Request” and “outcome” are carried as salted commitments, not content
Glossary: receipt JCS, a JWS from each party, then a hash-chained or Merkle log; SCITT via RFC 9943 and RFC 9942 Consistent. The page’s JSON is labeled “not a published format”; field names here differ (parties, request.messages, timestamps.terminalAt instead of client, requestMessageIds, completedAt)
Glossary: hash chain h(n) = SHA-256(h(n-1) ‖ entry(n)); each side sends the new head to the other Same formula, with entry = receipt digest and a defined h(0) per key. No separate head message: the head follows from the signed prev in each receipt
Glossary: tamper-evident log Merkle proofs of inclusion and consistency; logs can show split views Consistent (section 9, 14.3)
Glossary: non-repudiation A2A signs only Agent Cards; exchange evidence needs extensions Consistent; this is such an extension
Glossary: JCS; learn: signed Agent Cards RFC 8785 Informational; alg/kid from the protected header only; ES256 raw R‖S; signer-chosen jku risk Consistent. Receipts use typ emissar-receipt+json where cards use JOSE, and need no default-value pruning (section 6.1)

Appendix B. Demo Output

node receipt-demo.mjs (Node v24.13.0, 2026-09-26). The script builds receipts for a sample A2A task and a follow-up task, signs as server (ES256, RFC 7515 A.3 test key) and client (Ed25519, RFC 8037 A.1 test key), chains them, anchors them in a five-leaf RFC 9162 tree, and checks the failure cases. Digests are reproducible; ES256 signatures and the log’s key change on every run.

Emissar Receipt v0.1 demo (illustrative parties; RFC test keys)

0. Self-tests
  PASS  RFC 8785 section 3.2.2 example canonicalizes to the published bytes
  PASS  RFC 8037 A.3 JWK thumbprint of the Ed25519 test key
  PASS  RFC 6920 section 8.1 ni name for 'Hello World!'
  PASS  RFC 9162: recursive MTH = stack algorithm; every inclusion and consistency proof verifies (n = 1..9)

1. Build and sign receipt 1 (task 1: freight quote)
  receipt digest   ni:///sha-256;H3q3chQq7RN83Y8dwl2u7NITy9-pIjAwfopO1HrRDj0
  server chain     seq 1, head ni:///sha-256;nuekjdr37ZhdgrHAEP1-KXOmtu1mMOS0rq3nOr26Lgk

2. Client checks before countersigning (section 7.5)
  PASS  issuer signature valid, not yet countersigned
  PASS  request, status message and artifact commitments open against the client's own copies
  PASS  parties.client.signingKey is the client's own key
  PASS  receipt 1 verifies with both signatures (section 12)
  client chain     seq 1, head ni:///sha-256;DkC7EjfPeQvgsnVRm0dgKDvP_DKXnzDdp95WU6QatEU

3. Build, sign and countersign receipt 2 (task 2: booking under a mandate, chained)
  PASS  client openings check
  PASS  receipt 2 verifies with both signatures
  receipt digest   ni:///sha-256;sEPvEnSKFLSahCiPC-xeOrW9RXhq_AN1EgboDdbzwWs
  PASS  server chain: receipt 2 prev = head after receipt 1
  PASS  client chain: receipt 2 prev = head after receipt 1
  server head      seq 2, ni:///sha-256;79GWMeWjhKbBkfgLEx5IAkhO2eU2teRoS7vLklDBrMk
  client head      seq 2, ni:///sha-256;JvMxjj64XKqZ0VWhf-pQMBuhZTDcmPP5jkAZ7wFM1ZU

4. A receipt the client refuses (section 7.6)
  PASS  client detects the mismatch and does not countersign  (artifact status-B-20931 commitment mismatch)
  PASS  signed refusal verifies with the client key
  PASS  receipt 3 stays valid as issuer-signed only (countersigned: false)

5. Anchor receipts in an RFC 9162 Merkle log (section 9)
  tree size 5, root Yl6r3AUZvqFA1a-YK6QtUU-KynO5X77_eA3EQQRB8qU
  receipt 1 at leaf 1, path length 3; receipt 2 at leaf 3, path length 3
  PASS  receipt 1 inclusion proof verifies against the signed tree head
  PASS  receipt 2 inclusion proof verifies against the signed tree head
  PASS  consistency proof: the 2-entry tree is a prefix of the 5-entry tree

6. Failure cases (each MUST be rejected)
  PASS  tampered field (payment amount): rejected  (server signature does not verify over the canonical receipt; client signature does not verify over the canonical receipt; no valid server (issuer) signature)
  PASS  missing second signature under a countersign-required policy: rejected  (policy requires both signatures; the client signature is missing)
  PASS  rewritten receipt 1 has a valid issuer signature on its own
  PASS  broken chain: receipt 2 prev no longer matches: rejected  (receipt seq 2: prev does not equal the head after seq 1)
  PASS  wrong inclusion proof (swapped sibling hash): rejected  (inclusion proof does not reach the signed root)
  PASS  inclusion proof presented for a different receipt: rejected  (inclusion proof does not reach the signed root)
  PASS  opening with a wrong salt: commitment does not open  (request message 3b8f0c2e-5d1a-4f6b-9e7c-2a4d6f8b0c1e commitment mismatch)

ALL CHECKS PASSED

Appendix C. Validation Output

python validate.py (Python 3.14.3, jsonschema 4.26.0, PyYAML 6.0.3, openapi-spec-validator installed).

JSON Schema (receipt-v0.1.schema.json, Draft 2020-12, format checks on)
  PASS  receipt document 0199a1c2-7d4e-7b31-9a0f-3c5e2d1b4a60 (2 signature(s), 1 anchor(s))
  PASS  receipt document 0199a1c4-d215-7c02-8e41-5a6b7c8d9e0f (2 signature(s), 1 anchor(s))
  PASS  receipt document 0199a1c6-0a3b-7e44-b2c5-6d7e8f9a0b1c (1 signature(s), 0 anchor(s))
  PASS  protected header 1 (ES256, role server, seq 1)
  PASS  protected header 2 (Ed25519, role client, seq 1)
  PASS  protected header 3 (ES256, role server, seq 2)
  PASS  protected header 4 (Ed25519, role client, seq 2)
  PASS  protected header 5 (ES256, role server, seq 3)
  PASS  openings for 0199a1c2-7d4e-7b31-9a0f-3c5e2d1b4a60
  PASS  openings for 0199a1c4-d215-7c02-8e41-5a6b7c8d9e0f
  PASS  openings for 0199a1c6-0a3b-7e44-b2c5-6d7e8f9a0b1c
  PASS  signed refusal
  PASS  anchor at leaf 1 of 5
  PASS  anchor at leaf 3 of 5
  PASS  Agent Card extension entry
  PASS  request metadata
  PASS  request metadata inside the A2A message
  PASS  receipt document inside the A2A receipt artifact
  PASS  openings inside the A2A receipt artifact
  PASS  receipt artifact metadata
  PASS  'tampered' example is schema-valid (it fails cryptographic checks, not the schema)
  PASS  'rewritten' example is schema-valid (it fails cryptographic checks, not the schema)
Schema negative tests (each MUST be rejected)
  PASS  truncated digest suite sha-256-128
  PASS  timestamp without milliseconds
  PASS  timestamp with an offset
  PASS  non-terminal final state
  PASS  person principal named in clear
  PASS  unknown receipt member
  PASS  unprotected JWS header
  PASS  three signatures
  PASS  amount as a JSON number
  PASS  http Agent Card URL
  PASS  attached JWS payload member
  PASS  header alg EdDSA (deprecated by RFC 9864)
  PASS  header with empty crit
  PASS  header without chain prev
OpenAPI (receipt-v0.1.yaml)
  PASS  YAML parses; openapi 3.1.0; 7 paths
  PASS  openapi-spec-validator: valid OpenAPI 3.1 (external $refs resolved)
  PASS  all 19 distinct $refs resolve
  PASS  all 33 internal schema $refs resolve

ALL CHECKS PASSED