Shane Deconinck Trusted AI Agents · Decentralized Trust
Just launched: trustedagentic.ai · a structured approach to your agentic transformation

Verifiable Intent: Mastercard and Google Open-Source Agent Authorization

· 7 min read

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.

Read the spec | GitHub


I'm building the PAC Framework™ to help organisations adopt agentic AI with trust at the center: where to deploy agents, how to hold them accountable, and how to keep them under control. More at trustedagentic.ai.

Stay in the loop

A few times per month at most. Unsubscribe with one click.

Your email will be stored with Buttondown and used only for this newsletter. No tracking, no ads.

↑ Back to top