diff --git a/specs/extensions/cardano_masumi.md b/specs/extensions/cardano_masumi.md new file mode 100644 index 0000000000..ed3c84f734 --- /dev/null +++ b/specs/extensions/cardano_masumi.md @@ -0,0 +1,417 @@ +# Extension: `cardano-masumi` + +## Status + +**Draft — v0.1**. This extension is specified as an optional, composable addition to the Cardano `exact` scheme. Its behavioral requirements are stable; its wire shape MAY evolve to align with future updates to the x402 extensions architecture. + +## Summary + +The `cardano-masumi` extension adds escrow-based, agent-oriented payment semantics on top of the core Cardano `exact` scheme defined in [`specs/schemes/exact/scheme_exact_cardano.md`](../schemes/exact/scheme_exact_cardano.md). It is the canonical way to carry Masumi-specific fields — seller verification keys, purchaser/agent identifiers, input-hash commitments, and the escrow timing windows (`payByTime`, `submitResultTime`, `unlockTime`, `externalDisputeUnlockTime`) — across an x402 flow without embedding them in the core scheme. + +Resource servers that integrate with Masumi MUST declare their Masumi intent via this extension. Resource servers, clients, and facilitators that do not implement this extension MUST remain able to process the underlying Cardano `exact` payment as a generic script-parameterized payment (see §Core Compatibility below). + +This placement preserves three properties: + +1. The core Cardano scheme remains implementation-agnostic and vendor-neutral. +2. Masumi support continues to be fully expressible and unambiguous. +3. Multiple agent-payment protocols can coexist on Cardano without each needing a privileged slot in the core scheme. + +## Relationship to the Core Cardano Scheme + +The core Cardano `exact` scheme supports payments to script addresses via script-parameterized payments. A Masumi payment is, on-chain, a script-parameterized payment to the Masumi smart contract address, with a specific datum and redeemer shape. + +All core verification rules (network, recipient, amount, asset, nonce/replay, TTL) from the Cardano scheme continue to apply. This extension layers the off-chain Masumi semantics on top of those rules. + +## `PaymentRequired` + +A resource server advertises Masumi support by including the `cardano-masumi` extension in the `extensions` object of the **402 Payment Required** response, alongside a Cardano `exact` `accepts[]` entry that points at the Masumi script address. + +```json +{ + "x402Version": 2, + "error": "PAYMENT-SIGNATURE header is required", + "resource": { + "url": "https://api.example.com/premium-data", + "description": "Access to premium market data", + "mimeType": "application/json" + }, + "accepts": [ + { + "scheme": "exact", + "network": "cardano:mainnet", + "amount": "10000", + "asset": "c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad.0014df105553444d", + "payTo": "addr1...", // Masumi script address for this payment + "maxTimeoutSeconds": 600, + "extra": { + "scriptHash": "masumi_script_hash", + "script": { + "type": "plutusV3", + "code": "" + }, + "parameters": { + // generic script parameters, as defined by the core Cardano scheme + } + } + } + ], + "extensions": { + "cardano-masumi": { + "info": { + "paymentType": "Web3CardanoV1", + "blockchainIdentifier": "blockchain_identifier", + "sellerVkey": "sdasdqweqwewewewqe", + "agentIdentifier": "agent_identifier", + "identifierFromPurchaser": "aabbaabb11221122aabb", + "inputHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", + "payByTime": "1713626260", + "submitResultTime": "1713636260", + "unlockTime": "1713636260", + "externalDisputeUnlockTime": "1713636260" + }, + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "paymentType": { "type": "string" }, + "blockchainIdentifier": { "type": "string" }, + "sellerVkey": { "type": "string" }, + "agentIdentifier": { "type": "string" }, + "identifierFromPurchaser": { "type": "string" }, + "inputHash": { "type": "string" }, + "payByTime": { "type": "string" }, + "submitResultTime": { "type": "string" }, + "unlockTime": { "type": "string" }, + "externalDisputeUnlockTime": { "type": "string" } + }, + "required": [ + "paymentType", + "blockchainIdentifier", + "sellerVkey", + "agentIdentifier", + "identifierFromPurchaser", + "inputHash", + "payByTime", + "submitResultTime", + "unlockTime", + "externalDisputeUnlockTime" + ] + } + } + } +} +``` + +## `PaymentPayload` + +A client that recognises the `cardano-masumi` extension echoes it in the `extensions` field of the `PaymentPayload`, per the v2 rule that the client MUST include at least the info it received: + +```json +{ + "extensions": { + "cardano-masumi": { + "info": { + "paymentType": "Web3CardanoV1", + "blockchainIdentifier": "blockchain_identifier", + "sellerVkey": "sdasdqweqwewewewqe", + "agentIdentifier": "agent_identifier", + "identifierFromPurchaser": "aabbaabb11221122aabb", + "inputHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", + "payByTime": "1713626260", + "submitResultTime": "1713636260", + "unlockTime": "1713636260", + "externalDisputeUnlockTime": "1713636260" + }, + "schema": { /* same schema as above */ } + } + } +} +``` + +A client MAY add extension-defined fields that the server did not include (per the v2 rule that the client may append additional info but cannot delete or overwrite existing info). A client MUST NOT silently drop any field received from the server. + +## Field Semantics + +All fields are Masumi-specific and have no meaning in the core Cardano `exact` scheme. + +| Field | Type | Required | Description | +| ---------------------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------- | +| `paymentType` | string | Yes | Masumi payment-protocol tag (e.g., `"Web3CardanoV1"`). Identifies the Masumi variant in use. | +| `blockchainIdentifier` | string | Yes | Masumi blockchain identifier for the logical payment stream. | +| `sellerVkey` | string | Yes | Verification key of the seller in the Masumi protocol. | +| `agentIdentifier` | string | Yes | Masumi agent identifier for the service. | +| `identifierFromPurchaser` | string | Yes | Identifier supplied by the purchaser for the interaction. | +| `inputHash` | string | Yes | Hash commitment over the purchaser's inputs to the agent, as defined by Masumi. | +| `payByTime` | string | Yes | Unix timestamp (seconds, string-encoded) by which the purchaser must pay. | +| `submitResultTime` | string | Yes | Unix timestamp by which the seller must submit a result. | +| `unlockTime` | string | Yes | Unix timestamp at which funds become withdrawable by the seller absent dispute. | +| `externalDisputeUnlockTime` | string | Yes | Unix timestamp at which an external dispute path unlocks, as defined by Masumi. | + +The exact semantics of these fields are governed by the Masumi protocol specification. This extension records their on-the-wire placement and requires echo behaviour; it does not re-specify Masumi's off-chain rules. + +## Responsibilities + +**Resource server:** +- MUST place Masumi-specific fields in `extensions["cardano-masumi"].info`. +- MUST NOT place Masumi-specific fields in `PaymentRequirements.extra` or the core `payload` object. +- SHOULD populate `payTo` with the Masumi script address and `extra` with the script reference so that clients unaware of the Masumi extension can still construct a script-parameterized payment following the core scheme. + +**Client:** +- If the client recognises the `cardano-masumi` extension, it SHOULD use the Masumi-specific fields to construct a Masumi-compliant datum and redeemer. +- The client MUST echo the extension info back in `PaymentPayload.extensions["cardano-masumi"].info` unchanged. +- If the client does not recognise the extension, it MAY fall back to treating the payment as a generic script-parameterized Cardano payment, provided the server has also supplied `extra.script` / `extra.scriptHash` as required by the core scheme. Whether Masumi will ultimately accept such a payment is a Masumi-protocol matter, not an x402 matter. + +**Facilitator:** +- A facilitator that does not implement `cardano-masumi` MUST still enforce the core Cardano verification rules (network, recipient, amount, asset, nonce, TTL). +- A facilitator that does implement `cardano-masumi` MAY additionally enforce Masumi-specific checks (for example, verifying the datum structure, time-window consistency, or seller verification key binding). Such checks MUST NOT be used to reject payments from servers that only declare the core scheme. + +## Core Compatibility + +This extension is designed so that a minimally conformant x402 stack — one that implements only the core Cardano `exact` scheme — can still process a Masumi payment as a generic script-parameterized payment: + +- Recipient, amount, asset, nonce, and TTL checks are unchanged. +- The Masumi script address is visible in `payTo`. +- The Masumi script (or its hash) is visible in `extra`. +- The Masumi-specific fields are cleanly isolated under `extensions["cardano-masumi"]` and can be ignored without breaking verification. + +A non-Masumi stack cannot, of course, reproduce Masumi's off-chain invariants (escrow windows, dispute rights, agent accounting). That is precisely what makes these fields extension-level rather than core-level. + +## Settlement Response + +On success, the `PAYMENT-RESPONSE` payload MAY include extension-specific information under `extensions["cardano-masumi"]`: + +```json +{ + "success": true, + "network": "cardano:mainnet", + "transaction": "2f9a7b3c...", + "extensions": { + "status": "confirmed", + "cardano-masumi": { + "info": { + "blockchainIdentifier": "blockchain_identifier" + } + } + } +} +``` + +This extension does not currently specify additional normative fields in the settlement response; future versions MAY add them. + +## End-to-End Example + +This section shows a complete Masumi-routed flow using the Cardano `exact` scheme together with the `cardano-masumi` extension. All three messages — `PaymentRequired`, `PAYMENT-SIGNATURE`, `PAYMENT-RESPONSE` — are shown consistently. + +For brevity, the signed transaction is truncated; in practice it is the Base64-encoded CBOR of a fully signed Cardano transaction that pays the declared `amount` of the declared `asset` to `payTo` (the Masumi script address) and consumes the UTXO referenced by `payload.nonce` as an input. + +### Step 1 — `402 Payment Required` (server → client) + +```json +{ + "x402Version": 2, + "error": "PAYMENT-SIGNATURE header is required", + "resource": { + "url": "https://api.example.com/premium-data", + "description": "Access to premium market data", + "mimeType": "application/json" + }, + "accepts": [ + { + "scheme": "exact", + "network": "cardano:mainnet", + "amount": "10000", + "asset": "c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad.0014df105553444d", + "payTo": "addr1wxmasumiscriptaddressexample000000000000000000000000000000000", + "maxTimeoutSeconds": 600, + "extra": { + "scriptHash": "masumi_script_hash", + "script": { + "type": "plutusV3", + "code": "" + }, + "parameters": {} + } + } + ], + "extensions": { + "cardano-masumi": { + "info": { + "paymentType": "Web3CardanoV1", + "blockchainIdentifier": "blockchain_identifier", + "sellerVkey": "sdasdqweqwewewewqe", + "agentIdentifier": "agent_identifier", + "identifierFromPurchaser": "aabbaabb11221122aabb", + "inputHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", + "payByTime": "1713626260", + "submitResultTime": "1713636260", + "unlockTime": "1713636260", + "externalDisputeUnlockTime": "1713636260" + }, + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "paymentType": { "type": "string" }, + "blockchainIdentifier": { "type": "string" }, + "sellerVkey": { "type": "string" }, + "agentIdentifier": { "type": "string" }, + "identifierFromPurchaser": { "type": "string" }, + "inputHash": { "type": "string" }, + "payByTime": { "type": "string" }, + "submitResultTime": { "type": "string" }, + "unlockTime": { "type": "string" }, + "externalDisputeUnlockTime": { "type": "string" } + }, + "required": [ + "paymentType", + "blockchainIdentifier", + "sellerVkey", + "agentIdentifier", + "identifierFromPurchaser", + "inputHash", + "payByTime", + "submitResultTime", + "unlockTime", + "externalDisputeUnlockTime" + ] + } + } + } +} +``` + +### Step 2 — `PAYMENT-SIGNATURE` header (client → server) + +The client selects the offer above, builds and signs a Cardano transaction that pays the Masumi script address with a datum consistent with the Masumi fields, and submits it via the `PAYMENT-SIGNATURE` header. The decoded (pre-Base64) header payload is: + +```json +{ + "x402Version": 2, + "resource": { + "url": "https://api.example.com/premium-data", + "description": "Access to premium market data", + "mimeType": "application/json" + }, + "accepted": { + "scheme": "exact", + "network": "cardano:mainnet", + "amount": "10000", + "asset": "c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad.0014df105553444d", + "payTo": "addr1wxmasumiscriptaddressexample000000000000000000000000000000000", + "maxTimeoutSeconds": 600, + "extra": { + "scriptHash": "masumi_script_hash", + "script": { + "type": "plutusV3", + "code": "" + }, + "parameters": {} + } + }, + "payload": { + "transaction": "AAAIAQDi1Hwj...AAAAAAA=", + "nonce": "662cbf645fcd8914eb89115b83970a950493dd2fbaf39dea3b96e8cbdc132939#0" + }, + "extensions": { + "cardano-masumi": { + "info": { + "paymentType": "Web3CardanoV1", + "blockchainIdentifier": "blockchain_identifier", + "sellerVkey": "sdasdqweqwewewewqe", + "agentIdentifier": "agent_identifier", + "identifierFromPurchaser": "aabbaabb11221122aabb", + "inputHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", + "payByTime": "1713626260", + "submitResultTime": "1713636260", + "unlockTime": "1713636260", + "externalDisputeUnlockTime": "1713636260" + }, + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "paymentType": { "type": "string" }, + "blockchainIdentifier": { "type": "string" }, + "sellerVkey": { "type": "string" }, + "agentIdentifier": { "type": "string" }, + "identifierFromPurchaser": { "type": "string" }, + "inputHash": { "type": "string" }, + "payByTime": { "type": "string" }, + "submitResultTime": { "type": "string" }, + "unlockTime": { "type": "string" }, + "externalDisputeUnlockTime": { "type": "string" } + }, + "required": [ + "paymentType", + "blockchainIdentifier", + "sellerVkey", + "agentIdentifier", + "identifierFromPurchaser", + "inputHash", + "payByTime", + "submitResultTime", + "unlockTime", + "externalDisputeUnlockTime" + ] + } + } + } +} +``` + +Notes on this step: + +- The client MUST echo `extensions["cardano-masumi"].info` unchanged from what it received in Step 1 (per the v2 rule that the client may append but not delete or overwrite server-supplied extension info). +- The `extra` object still carries the generic script reference required by the core Cardano scheme; this is what allows a non-Masumi facilitator to verify the underlying payment independent of the extension. +- `payload.nonce` references a UTXO consumed as an input of the signed transaction, as required by the core scheme's replay-prevention rule. + +### Step 3 — `PAYMENT-RESPONSE` / `SettlementResponse` (server → client, on success) + +```json +{ + "success": true, + "network": "cardano:mainnet", + "transaction": "2f9a7b3c1d4e5f60718293a4b5c6d7e8f90112233445566778899aabbccddeeff", + "extensions": { + "status": "confirmed", + "cardano-masumi": { + "info": { + "blockchainIdentifier": "blockchain_identifier" + } + } + } +} +``` + +Notes on this step: + +- Core fields (`success`, `network`, `transaction`, `extensions.status`) follow the core Cardano scheme. +- The `cardano-masumi` settlement-extension payload echoes the `blockchainIdentifier` so clients and downstream consumers can correlate the on-chain tx with the Masumi off-chain payment stream. Future versions of this extension MAY specify additional settlement-side fields (e.g., Masumi state transitions); this example reflects the minimum required today. +- A non-Masumi-aware client receiving this response can still interpret the core fields correctly and SHOULD ignore `extensions["cardano-masumi"]`. + +### What a non-Masumi-aware stack sees + +A minimally conformant Cardano `exact` stack that does not implement the `cardano-masumi` extension processes the same flow as a generic script-parameterized Cardano payment: + +- Recipient, amount, asset, nonce, and TTL checks apply to the transaction exactly as specified in the core scheme. +- The Masumi script address is visible in `payTo`; the Masumi script (or hash) is visible in `extra`. +- The `cardano-masumi` extension block is ignored. + +Such a stack will not enforce Masumi's off-chain invariants (escrow timing, dispute windows, agent/seller accounting) — those are, by design, the exclusive responsibility of Masumi-aware implementations. + +## Security Considerations + +- This extension MUST NOT be used to weaken the core Cardano verification rules. In particular, possession of a valid Masumi datum does not substitute for the nonce/replay, TTL, or amount/asset checks. +- Implementers SHOULD treat Masumi-specific fields as untrusted off-chain assertions until validated against the Masumi protocol by an implementation that understands it. +- Facilitators that accept Masumi payments SHOULD clearly document which Masumi invariants they enforce and which they delegate to the resource server or the seller. + +## Privacy Considerations + +- Masumi identifiers (`agentIdentifier`, `identifierFromPurchaser`, `sellerVkey`) are identifying or near-identifying on an x402 wire. Implementations SHOULD treat them with the same care as other pseudonymous identifiers. +- Servers SHOULD NOT include Masumi extension fields in `PaymentRequired` responses for resources that do not actually route through Masumi. + +## Version History + +| Version | Date | Changes | Author | +| ------- | ---------- | ----------------------------------------------------------------------------- | -------- | +| 0.1 | 2026-04-23 | Initial draft; moves Masumi-specific fields out of the core Cardano scheme. | QBT Labs | diff --git a/specs/schemes/exact/scheme_exact_cardano.md b/specs/schemes/exact/scheme_exact_cardano.md new file mode 100644 index 0000000000..54453ea25d --- /dev/null +++ b/specs/schemes/exact/scheme_exact_cardano.md @@ -0,0 +1,333 @@ +# Scheme: exact on Cardano + +## Summary + +This document specifies the `exact` payment scheme for the x402 protocol on Cardano. The scheme facilitates payments of ADA and Cardano Native Tokens over the Cardano ledger, using signed transactions produced by the client and verified/settled by a resource server or facilitator. + +This scheme is intentionally defined as an **implementation-agnostic, facilitator-neutral, vendor-neutral** core. It does not name, require, or privilege any specific third-party protocol, operator, or ecosystem implementation. Optional ecosystem-specific semantics (for example, escrow-based agent payment protocols, dispute windows, off-chain identifiers, or reputation systems) MUST be expressed through the x402 v2 **extensions** model, not through the core scheme. + +The core scheme supports: + +1. **Address-to-address payments** — the canonical Cardano x402 flow, sending ADA or a native asset to a payment address specified by the resource server. +2. **Script-parameterized payments** — a generic Cardano-native flow in which `payTo` is a script address and `extra` carries the information required for a client to reconstruct or validate that script address during transaction building. + +Any additional flow that embeds the semantics of a specific external protocol (for example, Masumi) MUST be declared as an x402 extension; see [`specs/extensions/cardano_masumi.md`](../../extensions/cardano_masumi.md) for the Masumi extension. + +## Protocol Flow + +```mermaid +sequenceDiagram + participant Client as Client/Agent + participant Server as Server + participant Facilitator as Facilitator + participant Cardano as Cardano Blockchain + + %% Initial Request + Client->>Server: 1. HTTP GET /api + + %% Payment Required Response + Server->>Client: 2. HTTP 402 and Payment Details + + %% Client Prepares Payment + Note over Client: 3. Client selects payment option,
creates and
signs a Transaction + + %% Request with Payment + Client->>Server: 4. HTTP GET /api
Header: PAYMENT-SIGNATURE (signed transaction) + Note right of Client: Retries with payment header + + %% Server Verification + alt Server Verification + Server->>Server: 5. Verify transaction locally + else Remote Verification (via Facilitator) + Server->>Facilitator: 5. POST /verify
(Payment Payload + Requirements) + Note right of Facilitator: Facilitator validates:
- Payment amount
- Correct recipient
- Nonce in Transaction + end + + %% Server Verification + alt Server Submission + Server->>Cardano: 6a. Submit signed transaction + Note right of Cardano: Node accepts tx into mempool;
block inclusion is asynchronous + Cardano-->>Server: 6b. Transaction hash (submission ack) + else Remote Submission (via Facilitator) + Server->>Facilitator: 6a. POST /settle
(Payment details) + Facilitator->>Cardano: 6b. Submit signed transaction + Note right of Cardano: Node accepts tx into mempool;
block inclusion is asynchronous + Cardano-->>Facilitator: 6c. Transaction hash (submission ack) + Facilitator->>Server: 6d. Settlement Response
(txHash, status) + end + + Note right of Server: 7. Receives transaction hash and status + + %% Final Response + Server->>Client: 8. HTTP 200 OK + Resource
Header: PAYMENT-RESPONSE + Note left of Server: Returns requested resource
with transaction confirmation:
- transaction: "2f9a7b3c..."
- network: "cardano:mainnet"
- success: true +``` + +The protocol flow for `exact` on Cardano is client-driven. + +1. **Client** makes an HTTP request to a **Resource Server**. + +2. **Resource Server** responds with a `402 Payment Required` status, detailing the payment information: + - For address-to-address payments, `payTo` is the address to which the payment MUST be sent; `extra` MAY be empty or carry facilitator-neutral hints. + - For script-parameterized payments, `payTo` is the script address and `extra` carries the script reference (and any applied parameters) required for the client to build the transaction. + +3. **Client** constructs the transaction body, signs it, and returns it to the **Resource Server** via the `PAYMENT-SIGNATURE` header. + +4. **Resource Server** verifies the transaction is valid: + - **Local verification**: The server validates the transaction structure, amount, and recipient directly. + - **Remote verification**: The server forwards the `PAYMENT-SIGNATURE` header and `paymentRequirements` to a **Facilitator's** `/verify` endpoint. + +5. After successful verification, the signed transaction is submitted to the Cardano blockchain: + - **Server submission**: The **Resource Server** submits the transaction directly. + - **Facilitator submission**: The **Resource Server** sends the transaction to the **Facilitator's** `/settle` endpoint, which submits it to the blockchain. + +6. The Cardano node validates the submitted transaction and, if accepted, places it in its mempool and returns the transaction hash synchronously. Block inclusion — and any subsequent probabilistic confirmation — happens asynchronously and is observed later via the settlement `status` field. + +7. **Resource Server** receives the transaction hash and status. + - Cardano uses Ouroboros Praos, which has probabilistic finality. A transaction that has only been accepted into the mempool, or that appears in a recent block, can still be rolled back. Granting access on the basis of mempool acceptance alone (`status: "mempool"`) is therefore **strongly discouraged** and SHOULD NOT be used for any resource with real economic value. Servers that choose to accept mempool status MUST document this risk and accept full liability for rolled-back transactions. + +8. **Resource Server** grants the **Client** access to the requested resource, returning an HTTP 200 OK response with a `PAYMENT-RESPONSE` header containing: + - `transaction`: The Cardano transaction hash + - `network`: The Cardano network (e.g., `cardano:mainnet`) + - `status`: The transaction status (e.g., `confirmed` or `mempool`) + +## Core Compliance Surface + +An implementation claims conformance with the Cardano `exact` scheme by supporting the two core flows defined in this document (address-to-address and script-parameterized) and enforcing the verification rules in §Facilitator Verification Rules. + +The core scheme: + +- MUST NOT name, require, or privilege any specific third-party protocol, facilitator operator, product, or ecosystem implementation. +- MUST NOT embed ecosystem-specific fields (off-chain identifiers, agent-protocol fields, escrow timing windows, dispute windows, reputation identifiers, or similar) into the core `PaymentRequirements.extra` or the `payload` object. +- SHOULD treat any unknown fields in `extra` as pass-through information that does not affect core verification. +- SHOULD expose ecosystem-specific behavior through the x402 v2 extensions model (see §Extensions below). + +## `PaymentRequirementsResponse` + +### Core Schema (Address-to-Address) + +When the Resource Server responds with a `402 Payment Required`, the body contains the payment requirements: + +```js +{ + "x402Version": 2, + "error": "PAYMENT-SIGNATURE header is required", + "resource": { + "url": "https://api.example.com/premium-data", + "description": "Access to premium market data", + "mimeType": "application/json" + }, + "accepts": [ + { + "scheme": "exact", + "network": "cardano:mainnet", // cardano:preprod or cardano:preview for public testnets + "amount": "10000", // atomic units of the asset + "asset": "c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad.0014df105553444d", // ${policyId}.${assetNameHex}; for ADA, use the reserved value "lovelace" + "payTo": "addr1...", + "maxTimeoutSeconds": 600, + "extra": { + // MAY be empty. MAY contain facilitator-neutral hints. + // MUST NOT embed fields tied to a specific external protocol or operator. + } + } + ] +} +``` + +**Notes:** + +- The `asset` field follows the Cardano convention `${policyId}.${assetNameHex}`. For native ADA payments, implementations MUST use the reserved value `"lovelace"`. +- The `maxTimeoutSeconds` value SHOULD account for Cardano's slot/block cadence; values meaningfully below a single-block round trip SHOULD be avoided. + +### Core Schema (Script-Parameterized Payment) + +When the Resource Server requires payment to a script address, `payTo` is the script address and `extra` carries the information required to build and verify the payment against that script. Script-parameterized payments are a generic Cardano primitive and do not imply any specific off-chain protocol. + +```js +{ + "x402Version": 2, + "error": "PAYMENT-SIGNATURE header is required", + "resource": { + "url": "https://api.example.com/premium-data", + "description": "Access to premium market data", + "mimeType": "application/json" + }, + "accepts": [ + { + "scheme": "exact", + "network": "cardano:mainnet", + "amount": "10000", + "asset": "c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad.0014df105553444d", + "payTo": "addr1...", // script address; MUST match the address derived from `extra.script` (plus any applied `extra.parameters`) after parameterization + "maxTimeoutSeconds": 600, + "extra": { + "scriptHash": "script_hash_here", // OPTIONAL; if the script is already on-chain, its hash is sufficient for the client to resolve the full script + "script": { + // OPTIONAL; provided when the script is not yet on-chain or when the server wishes to attach the script body directly + "type": "plutusV2" | "plutusV3" | "native", + "code": "" + }, + "parameters": { + // OPTIONAL; script-specific parameters to be applied to a parameterised script during transaction building + "param1": { "value": "Hello World", "type": "bytes" }, + "param2": { "value": 42, "type": "bigint" } + } + } + } + ] +} +``` + +**Normative rules:** + +- Servers MUST include enough information in `extra` for a conforming client to derive the script address declared in `payTo`. A bare `scriptHash` is sufficient when the script is already on-chain; otherwise the full `script` body MUST be provided. +- If `parameters` are supplied, the client MUST apply them deterministically when constructing the transaction, and the derived address MUST equal `payTo`. +- A facilitator performing remote verification MUST validate that the transaction pays the declared `asset` and `amount` to `payTo`; it is NOT required to re-execute the script or interpret ecosystem-level semantics attached to it. + +### Extensions (Ecosystem-Specific Semantics) + +Any payment flow that depends on the semantics of a specific off-chain protocol (for example, escrow accounts, dispute windows, agent identifiers, seller verification keys, or inputs-hash commitments used by a particular agentic-payment framework) MUST be expressed as an x402 extension under the `extensions` field of the `PaymentRequired` and `PaymentPayload` objects. + +Such extensions MUST NOT require changes to the fields specified in this document. Facilitators that do not implement a given extension MUST remain capable of verifying the core scheme, treating the extension as advisory. + +For the Masumi escrow/agent-payment extension, see [`specs/extensions/cardano_masumi.md`](../../extensions/cardano_masumi.md). + +## `PAYMENT-SIGNATURE` Header Payload + +The `PAYMENT-SIGNATURE` header is base64-encoded and sent in the client's request to the resource server when paying for a resource. + +The `payload` field of the `PAYMENT-SIGNATURE` header MUST contain: + +| Field | Type | Required | Description | +| ------------- | ------ | -------- | --------------------------------------------------------------------------------- | +| `transaction` | string | Yes | The fully signed Cardano transaction, Base64-encoded (CBOR-serialised). | +| `nonce` | string | Yes | A UTXO reference of the form `${txHash}#${index}` consumed as an input, used for replay prevention. | + +Example `payload`: + +```js +{ + "transaction": "AAAIAQDi1HwjSnS6M+WGvD73iEyUY2FRKNj0MlRp7+3SHZM3xCvMdB0AAAAAIFRgPKOstGBLCnbcyGoOXugUYAWwVzNrpMjPCzXK4KQWAQCMoE29VLGwftex8rhIlOuFLFNfxLIJlHqGXoXA8hx6l+LMdB0AAAAAIHbPucTRIEWgO6lzqukswPZ6i72IHEKK5LyM1l9HJNZNAQBthSeHDVK8Xr5/zp3JMZPLtG5uAoVgedTA4pEnp+h8qUlUzRwAAAAAIACH0swYW/QfGCFczGnjAVPHPqZrQE5vfvJr36i6KVEFAQAC7W4K5vCwB+nprjxcNlLiOQ7SIIfyCZjmj2qSis2iTsCuzBwAAAAAIAkSUkXOoeq52GNdhwpbs+jZqqrqPdmiN3oPw5EzDIanAQAIyFNGWD6OxiFIyXSxrNEcFG0npm+nImk6InUssXb1EZgx1hwAAAAAILhsjmMKyM0n75Cd7z6ufH2LNhOMibFOGhNlLgV5RFuEAQC+Mh4kGkLwrw/11729oUQnt3xOmOreE6PcnuN6M68ZBcCuzBwAAAAAIO2PQhSSqSAawCbRr005lfjBgFOqIHo4zb2GcQ/WCxAlAAgA+QKVAAAAAAAgjiAHD0X4HNSdVPpJtf2E6W2uRc8kbvCHYkgEQ1B+w1MDAwEAAAUBAQABAgABAwABBAABBQACAQAAAQEGAAEBAgEAAQcAHrfFfj8r0Pxsudz/0UPqlX5NmPgFw1hzP3be4GZ/4LEB5XXrONxGw0qOUsq3yNKeUhOCOgCIwaa4pswKaer66EKqPGwdAAAAACBrOIN4poutFUmHfB6FbFJu8GgXoPPTGQWREqFpPfvO1B63xX4/K9D8bLnc/9FD6pV+TZj4BcNYcz923uBmf+Cx7gIAAAAAAABg4xYAAAAAAAA=", + "nonce": "662cbf645fcd8914eb89115b83970a950493dd2fbaf39dea3b96e8cbdc132939#0" +} +``` + +Full `PAYMENT-SIGNATURE` header (address-to-address): + +```js +{ + "x402Version": 2, + "resource": { + "url": "https://api.example.com/premium-data", + "description": "Access to premium market data", + "mimeType": "application/json" + }, + "accepted": { + "scheme": "exact", + "network": "cardano:mainnet", + "amount": "10000", + "asset": "c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad.0014df105553444d", + "payTo": "addr1...", + "maxTimeoutSeconds": 600, + "extra": {} + }, + "payload": { + "transaction": "AAAIAQDi1Hwj...AAAAAAA=", + "nonce": "662cbf645fcd8914eb89115b83970a950493dd2fbaf39dea3b96e8cbdc132939#0" + } +} +``` + +Full `PAYMENT-SIGNATURE` header (script-parameterized): + +```js +{ + "x402Version": 2, + "resource": { + "url": "https://api.example.com/premium-data", + "description": "Access to premium market data", + "mimeType": "application/json" + }, + "accepted": { + "scheme": "exact", + "network": "cardano:mainnet", + "amount": "10000", + "asset": "c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad.0014df105553444d", + "payTo": "addr1...", // script address + "maxTimeoutSeconds": 600, + "extra": { + "scriptHash": "script_hash_here", + "script": { + "type": "plutusV3", + "code": "" + }, + "parameters": { + "param1": { "value": "Hello World", "type": "bytes" }, + "param2": { "value": 42, "type": "bigint" } + } + } + }, + "payload": { + "transaction": "AAAIAQDi1Hwj...AAAAAAA=", + "nonce": "662cbf645fcd8914eb89115b83970a950493dd2fbaf39dea3b96e8cbdc132939#0" + } +} +``` + +## Facilitator Verification Rules + +A facilitator MUST enforce all of the following rules before accepting a payment as valid. Any failure MUST result in a rejection. These rules are intentionally facilitator-neutral: they depend only on the signed Cardano transaction, the declared `PaymentRequirements`, and the current on-chain UTXO set. + +1. **Network Validation**: The transaction MUST be destined for the Cardano network declared in `PaymentRequirements.network` (`cardano:mainnet`, `cardano:preprod`, or `cardano:preview`). Transactions built for a different network MUST be rejected. + +2. **Recipient Verification**: At least one transaction output MUST pay funds to the address specified in `PaymentRequirements.payTo`. The facilitator MUST NOT accept transactions where no output targets `payTo`. When `payTo` is a script address declared via script-parameterized payment, the facilitator MAY verify that the declared script (with applied `parameters`) hashes to the same address, but MUST NOT be required to re-execute the script. + +3. **Amount Verification**: The aggregate value sent to `payTo` for the declared `asset` MUST be greater than or equal to `PaymentRequirements.amount`. + +4. **Asset Verification**: The asset unit in the transaction MUST exactly match `PaymentRequirements.asset`. For native tokens, both the policy ID and the asset name MUST match. For ADA, the reserved value `"lovelace"` MUST be used and the lovelace output MUST satisfy `amount`. The facilitator MUST NOT accept a different asset, even one of equal market value. + +5. **Nonce / Replay Prevention**: `payload.nonce` MUST be a valid UTXO reference (`txHash#index`) that is consumed as an input of the transaction. The facilitator MUST verify that this UTXO exists in the current on-chain UTXO set and has not been spent. This ensures uniqueness and prevents replay. + +6. **TTL / Expiry Check**: The transaction's TTL (time-to-live slot) MUST NOT have already passed at the time of verification. The facilitator MUST reject transactions whose TTL is in the past. The TTL SHOULD be consistent with `PaymentRequirements.maxTimeoutSeconds`. + +Facilitators MAY enforce additional checks introduced by extensions when they implement those extensions, but extension-specific checks MUST NOT be treated as core conformance requirements. + +## `PAYMENT-RESPONSE` Header Payload + +The `PAYMENT-RESPONSE` header is base64-encoded and returned to the client by the resource server. + +Schema: + +```js +{ + "success": true, // true or false + "network": "cardano:mainnet", + "transaction": "2f9a7b3c...", // Cardano transaction hash if successful + "extensions": { + "status": "confirmed" // "confirmed" is the recommended value; "mempool" is permitted but strongly discouraged — see settlement warning above + }, + "errorReason": "Utxo not found in utxo set" // OPTIONAL; present only on failure +} +``` + +## Extensions + +Cardano-specific ecosystem semantics that go beyond the rules above MUST be carried through the x402 v2 extensions model, following the pattern: + +``` +extensions[extensionId].info // extension-specific data +extensions[extensionId].schema // JSON Schema for `info` +``` + +Known Cardano-related extensions (non-exhaustive): + +- [`cardano-masumi`](../../extensions/cardano_masumi.md) — Masumi escrow/agent-payment semantics, including seller verification keys, purchaser/agent identifiers, escrow timing windows, dispute windows, and input-hash commitments. + +Additional Cardano-related extensions MAY be proposed to cover other ecosystem-level behaviors (e.g., alternative escrow protocols, metadata-label commitments, chain-of-custody attestations). They MUST follow the extensions architecture defined in the x402 v2 core specification and MUST NOT require changes to this document. + +## Version History + +| Version | Date | Changes | Author | +| ------- | ---------- | ----------------------------------------------------------------------------------------------- | --------- | +| 0.2 | 2026-04-23 | Remove vendor-specific `assetTransferMethod: "masumi"` and Masumi-named fields from the core scheme; move Masumi semantics to `specs/extensions/cardano_masumi.md`. Clarify that the core scheme is facilitator- and vendor-neutral. Add reserved `"lovelace"` value for native ADA `asset`. | QBT Labs | +| 0.1 | 2026-02-06 | Initial Cardano `exact` scheme merged upstream. | Masumi |