Mastercard and Google just open-sourced Verifiable Intent (VI), a spec that turns user intent into a first-class cryptographic object. Google, IBM, Fiserv, Checkout.com, and others are backing it.
It fills a gap in the current agent auth stack, for consumer purchases and machine-to-machine commerce alike. OAuth proves who you are and what an app can access. But when an agent acts on your behalf, a different question matters: what exactly did the user authorize, and within what bounds? OAuth doesn’t answer that. Verifiable Intent does: not identity, not access scopes, but the specific constraints a user set for an agent’s actions.
Three-Layer SD-JWT Chains
The architecture has three layers, built on SD-JWT (Selective Disclosure JSON Web Tokens):
- L1: Issuer proves identity
- L2: User defines intent (immediate or autonomous)
- L3: Agent signs what it intends to do
- L3a: Payment details go to the payment network
- L3b: Checkout details go to the merchant
Each layer is signed by the entity that controls that level of authority.
| Layer | Who signs | What it contains | Lifetime |
|---|---|---|---|
| L1 Credential | Issuer (e.g., card network) | Identity bound to public key via cnf.jwk |
~1 year |
| L2 Intent | User (key from L1) | Constraints: amounts, merchants, budgets, recurrence | 24h-30 days (autonomous) or ~15 min (immediate) |
| L3a Payment | Agent (key from L2) | Payment details, amount, payee, transaction_id |
~5 min |
| L3b Checkout | Agent (key from L2) | Line items, merchant, checkout_hash |
~5 min |
VI isn’t only for agents. L2 has two modes:
- Immediate: the user confirms the transaction themselves. The chain stops at L2, no agent involved.
- Autonomous: the user sets constraints and authorizes a specific agent (via its public key). Only that agent can generate L3 credentials within those bounds.
L3 splits into two credentials: L3a goes to the payment network, L3b goes to the merchant. They’re bound by transaction_id == checkout_hash. The chain stops at L3: the agent cannot delegate further.
Each layer includes an sd_hash: a hash of the previous layer’s SD-JWT as presented, including the specific disclosures that were revealed.
- L2 hashes the full serialized L1.
- L3a hashes the L2 base plus payment disclosures only.
- L3b hashes the L2 base plus checkout disclosures only.
Tampering with any disclosure breaks the hash in the next layer. Each L3 is cryptographically bound to its own selective view of L2.
Here’s what it looks like in practice. From the spec’s credential format examples:
L1 — signed by issuer
{
"iss": "https://issuer.mastercard.com",
"sub": "user-8a3f9c21",
"iat": 1700000000,
"exp": 1731536000,
"vct": "https://credentials.mastercard.com/card",
"cnf": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
"y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"
}
},
"pan_last_four": "1234",
"scheme": "mastercard",
"card_id": "card-mc-8842",
"_sd_alg": "sha-256",
"_sd": ["<hash-of-email-disclosure>"]
}
L2 payload — signed by user, hashes L1
{
"nonce": "b7e2f4a9c1d3e8b2",
"aud": "https://network.mastercard.com/vi/authorize",
"iat": 1700100000,
"exp": 1700186400,
"_sd_alg": "sha-256",
"sd_hash": "B64U(SHA-256(serialized_L1))",
"_sd": [
"<hash-of-checkout-constraint-mandate-disclosure>",
"<hash-of-payment-constraint-mandate-disclosure>"
],
"delegate_payload": [
{"...": "<hash-of-checkout-constraint-mandate-disclosure>"},
{"...": "<hash-of-payment-constraint-mandate-disclosure>"}
]
}
L2 checkout mandate — what the agent can buy
{
"vct": "mandate.checkout.open",
"cnf": {
"kid": "agent-key-1",
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "agent-public-key-x-component",
"y": "agent-public-key-y-component"
}
},
"constraints": [
{
"type": "mandate.checkout.allowed_merchant",
"allowed_merchants": [
{"...": "<hash-of-audioshop-disclosure>"},
{"...": "<hash-of-soundstore-disclosure>"}
]
},
{
"type": "mandate.checkout.line_items",
"items": [
{"...": "<hash-of-headphones-item-disclosure>"}
]
}
],
"prompt_summary": "Buy wireless headphones under $300 from approved merchants"
}
L2 payment mandate — how much the agent can spend
{
"vct": "mandate.payment.open",
"cnf": {
"kid": "agent-key-1",
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "agent-public-key-x-component",
"y": "agent-public-key-y-component"
}
},
"payment_instrument": {
"type": "mastercard.srcDigitalCard",
"id": "f199c3dd-7106-478b-9b5f-7af9ca725170",
"description": "Mastercard **** 1234"
},
"constraints": [
{
"type": "payment.amount",
"currency": "USD",
"min": 0,
"max": 30000
},
{
"type": "payment.allowed_payee",
"allowed_payees": [
{ "name": "AudioShop Inc.", "website": "https://audioshop.example.com" },
{ "name": "SoundStore", "website": "https://soundstore.example.com" }
]
},
{
"type": "payment.reference",
"conditional_transaction_id": "checkout-ref-9f3a2b1c"
}
]
}
L3a — signed by agent, hashes L2 + payment disclosures, sent to payment network
// Header
{
"alg": "ES256",
"typ": "kb-sd-jwt",
"kid": "agent-key-1"
}
// Payload
{
"nonce": "c9d1e2f3a4b5c6d7",
"aud": "https://network.mastercard.com/vi/authorize",
"iat": 1700200000,
"exp": 1700200300,
"sd_hash": "B64U(SHA-256(L2_base+payment_disclosure+merchant_disclosure))",
"_sd_alg": "sha-256",
"_sd": [
"<hash-of-payment-mandate-disclosure>",
"<hash-of-selected-merchant-disclosure>"
],
"delegate_payload": [
{"...": "<hash-of-payment-mandate-disclosure>"},
{"...": "<hash-of-selected-merchant-disclosure>"}
]
}
// Payment mandate disclosure
{
"vct": "mandate.payment",
"payment_instrument": {
"type": "mastercard.srcDigitalCard",
"id": "f199c3dd-7106-478b-9b5f-7af9ca725170",
"description": "Mastercard **** 1234"
},
"currency": "USD",
"amount": 27999,
"payee": {
"name": "AudioShop Inc.",
"website": "https://audioshop.example.com"
},
"transaction_id": "abc123def456..."
}
L3b — signed by agent, hashes L2 + checkout disclosures, sent to merchant
// Header
{
"alg": "ES256",
"typ": "kb-sd-jwt",
"kid": "agent-key-1"
}
// Payload
{
"nonce": "d8e2f4a5b6c7d8e9",
"aud": "https://audioshop.example.com/checkout",
"iat": 1700200000,
"exp": 1700200300,
"sd_hash": "B64U(SHA-256(L2_base+checkout_disclosure+item_disclosure))",
"_sd_alg": "sha-256",
"_sd": [
"<hash-of-checkout-mandate-disclosure>",
"<hash-of-selected-items-disclosure>"
],
"delegate_payload": [
{"...": "<hash-of-checkout-mandate-disclosure>"},
{"...": "<hash-of-selected-items-disclosure>"}
]
}
// Checkout mandate disclosure
{
"vct": "mandate.checkout",
"checkout_jwt": "<merchant-signed-jwt>",
"checkout_hash": "abc123def456...",
"line_items": [
{
"sku": "WH-1000XM5",
"name": "Sony WH-1000XM5 Wireless Headphones",
"quantity": 1,
"unit_price": 27999,
"currency": "USD"
}
]
}
Selective Disclosure
Each party only sees what they need to see.
| Data | Merchant | Payment Network | Dispute |
|---|---|---|---|
| User identity (L1) | ✓ | ✓ | ✓ |
| Constraints (L2) | checkout only | payment only | all |
| Line items | ✓ | – | ✓ |
| Payment instrument | – | ✓ | ✓ |
| Amount | – | ✓ | ✓ |
| Merchant details | ✓ | identifier only | ✓ |
Privacy by architecture, not by policy. The agent reveals only the relevant SD-JWT disclosures to each verifier. Both halves reference the same transaction via transaction_id == checkout_hash, without either party seeing the other’s data.
Both halves only come together during dispute resolution. VI provides the cryptographic evidence but does not define dispute procedures: liability assignment, chargebacks, and arbitration remain payment-network-specific.
Constraint Types
The spec defines eight machine-enforceable constraints:
| Constraint | What it bounds |
|---|---|
mandate.checkout.allowed_merchant |
Which merchants the agent can transact with |
mandate.checkout.line_items |
What the agent can buy, with quantities |
payment.allowed_payee |
Which payees the agent can send to |
payment.amount |
Acceptable range per transaction (min/max, currency) |
payment.budget |
Cumulative spend cap across transactions |
payment.recurrence |
Subscription setup (frequency, start/end) |
payment.agent_recurrence |
Multi-transaction authorization within bounds |
payment.reference |
Binds payment to a conditional transaction ID |
Budget, recurrence, and agent recurrence require the payment network to maintain state across transactions. That’s a significant infrastructure requirement, but it means constraint enforcement happens at the network level, not at the agent level. The agent can’t bypass its own limits.
Protocol Interoperability
VI is protocol-agnostic, with integration mappings for the three leading agentic commerce protocols:
| Protocol | Backed by | Status |
|---|---|---|
| AP2 (Agent Payments Protocol) | Google, Mastercard, AmEx, PayPal | 60+ partners |
| ACP (Agentic Commerce Protocol) | Stripe, OpenAI | Open standard |
| UCP (Universal Commerce Protocol) | Google, Shopify, Walmart, Visa | Announced Jan 2026 |
Built on specifications from IETF (SD-JWT, JWT, JWS, ES256), FIDO Alliance, EMVCo, and W3C.
SD-JWT vs Verifiable Credentials
The spec plans to integrate W3C Verifiable Credentials (VCs): VI “will be strengthened through integration with Verifiable Credentials, making consumer authorization and delegation even more explicit, portable and cryptographically verifiable.” The terminology is easy to mix up, so worth clarifying.
SD-JWT and VCs aren’t either/or. SD-JWT is a token format: how claims are encoded, signed, and selectively disclosed. W3C Verifiable Credentials are a data model: standardized claim structure and cross-ecosystem portability.
They combine. The SD-JWT VC profile uses SD-JWT as the encoding format for VCs. For example, the EU Digital Identity Wallet uses SD-JWT VC as one of its supported credential formats.
VI currently uses SD-JWT but defines its own claim structure without following the VC data model. The path forward: keep SD-JWT as the format, adopt the VC data model on top. That would make VI credentials portable beyond the issuers that define them today.
What It Doesn’t (Claim to) Solve
L3 is terminal. The agent cannot sub-delegate to another agent. No provision for multi-hop delegation chains. VI models a world where one agent acts for one user. As agent systems become more composable (agent calling agent calling agent), this single delegation step may prove insufficient.
Agent compromise. If an agent is compromised mid-execution (prompt injection, for example), the attacker could generate L3 credentials that satisfy L2 constraints but serve malicious purposes. The constraint system bounds the damage but doesn’t prevent it. To be fair, VI is generating proof of intent, not guaranteeing agent reliability. That remains the responsibility of the AI engineering behind the agent.
Trust bootstrapping. Agents are identified by their public key, but there’s no standard way to discover or verify those keys across organisations. The kid format is left to implementations, with no prescribed format like DIDs or URLs. Trust starts at the L1 issuer, but agent-to-agent discovery is out of scope. That’s the gap that A2A, TSP, and standards like DIDs aim to fill.
Liability. VI creates evidence chains for disputes. But who’s liable when L3 satisfies L2 constraints and the user is still unhappy with the result? The spec provides the evidence, not the answer.
Why This Matters
Verifiable Intent treats user intent as a cryptographic primitive in the agentic stack. The three-layer model maps directly to what trust infrastructure for agents needs:
- Identity (L1): who is this person?
- Intent (L2): what did they authorize, and within what bounds?
- Action (L3): what does the agent intend to do?
The selective disclosure model is genuinely novel for commerce. And the fact that Mastercard, Google, IBM, and the payment networks are converging on SD-JWT credential chains (not OAuth tokens, not API keys) signals where agent authorization is heading.
The spec is at Draft v0.1 (Apache 2.0, ES256), with a reference implementation on GitHub and integration into Mastercard Agent Pay coming in the next months.