Agent Payments with Visa TAP
Visa Trusted Agent Protocol (TAP) brings cryptographic identity verification to agentic commerce. Using RFC 9421 HTTP Message Signatures, agents prove their identity by signing HTTP requests with their private keys. TAP enables transparent, verifiable agent payments without relying on API keys, tokens, or session state—just cryptographic proof of identity at the edge.
Prerequisites
- Connection to Tenzro MCP server at
mcp.tenzro.network/mcp - Registered agent identity (
did:tenzro:machine:*) - Funded wallet with TNZO balance
- Basic understanding of HTTP signatures and cryptographic keys
What You'll Learn
- Create Visa TAP payment challenges with domain binding
- Sign HTTP requests using RFC 9421 HTTP Message Signatures
- Verify agent identity through a 7-stage cryptographic verification pipeline
- Settle payments on Tenzro Ledger with receipt verification
What is Visa TAP?
Visa Trusted Agent Protocol (TAP) is a payment protocol designed for the agentic economy. Unlike traditional payment methods that rely on API keys or bearer tokens, TAP uses RFC 9421 HTTP Message Signatures to cryptographically bind payments to agent identities.
Key features of Visa TAP:
- Cryptographic identity: Agents sign requests with their private keys, verified against TDIP identities
- RFC 9421 compliance: Uses standard HTTP signature format (Signature-Input + Signature headers)
- Domain binding: Signatures tied to specific domains to prevent cross-site reuse
- Replay protection: Nonces and timestamp validation prevent signature replay attacks
- CDN-level verification: Signatures verified at the edge before requests reach application servers
- Transparent commerce: All payment requests are cryptographically auditable
Step 1: Create a Visa TAP Payment Challenge
Use the create_payment_challenge MCP tool to generate a Visa TAP payment challenge for a resource:
curl -s -X POST "https://mcp.tenzro.network/mcp" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Session-Id: $(uuidgen)" \
-d '{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "create_payment_challenge",
"arguments": {
"protocol": "visa-tap",
"resource": "/api/inference",
"amount": 250000,
"asset": "TNZO",
"recipient": "0x0000000000000000000000000000000000000001"
}
}
}'Response:
{
"jsonrpc": "2.0",
"id": 7,
"result": {
"content": [
{
"type": "text",
"text": {
"challenge_id": "visa_tap_ch_x9y8z7w6v5u4",
"protocol": "visa-tap",
"resource": "/api/inference",
"amount": 250000,
"asset": "TNZO",
"recipient": "0x0000000000000000000000000000000000000001",
"chain_id": 1337,
"required_headers": [
"@method",
"@authority",
"@path",
"content-digest",
"content-type"
],
"max_signature_age_secs": 480,
"domain_binding": "api.tenzro.network",
"created_at": "2026-03-24T14:20:00Z",
"expires_at": "2026-03-24T15:20:00Z"
}
}
],
"isError": false
}
}The challenge includes required_headers (the HTTP fields that must be signed), max_signature_age_secs (480 seconds = 8 minutes), and domain_binding to prevent cross-domain signature reuse.
Step 2: Sign the Request with RFC 9421
The agent must sign the HTTP request using RFC 9421 HTTP Message Signatures. The signature covers specific HTTP fields (method, authority, path, content-digest, content-type) and includes metadata (created timestamp, keyid, nonce).
Rust example using Tenzro SDK:
use tenzro_payments::visa_tap::VisaTapClient;
use tenzro_crypto::signers::Ed25519Signer;
// Agent's signing key
let signer = Ed25519Signer::from_private_key(&agent_private_key)?;
let agent_did = "did:tenzro:machine:agent123";
// Create TAP client
let client = VisaTapClient::new(signer, agent_did);
// Build the HTTP request parts
let request_parts = HttpRequestParts {
method: "POST",
authority: "api.tenzro.network",
path: "/api/inference",
headers: vec![
("content-type", "application/json"),
("content-digest", "sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:"),
],
body: r#"{"model":"gemma4-9b","prompt":"What is Tenzro?"}"#,
};
// Sign the request (produces Signature-Input and Signature headers)
let signed_request = client.sign_request(&request_parts).await?;
println!("Signature-Input: {}", signed_request.signature_input);
println!("Signature: {}", signed_request.signature);The resulting HTTP request headers:
POST /api/inference HTTP/1.1
Host: api.tenzro.network
Content-Type: application/json
Content-Digest: sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:
Signature-Input: sig1=("@method" "@authority" "@path" "content-digest" "content-type");created=1711289600;keyid="did:tenzro:machine:agent123";nonce="abc123xyz456"
Signature: sig1=:MEUCIQDXlI2fZH+sTmSu6Z1xN3vKjP5wQmN7rL8sF4tG9yH3xQIgRpLsK2mN8vT6wY1zU4cD5eF7gH9iJ0kL2mN4oP6qR8s=:
{"model":"gemma4-9b","prompt":"What is Tenzro?"}Step 3: Verify Payment Credential
Use the verify_payment MCP tool to verify the signed request and settle the payment on-chain:
curl -s -X POST "https://mcp.tenzro.network/mcp" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Session-Id: $(uuidgen)" \
-d '{
"jsonrpc": "2.0",
"id": 8,
"method": "tools/call",
"params": {
"name": "verify_payment",
"arguments": {
"challenge_id": "visa_tap_ch_x9y8z7w6v5u4",
"protocol": "visa-tap",
"payer_did": "did:tenzro:machine:agent123",
"amount": 250000,
"asset": "TNZO",
"signature": "MEUCIQDXlI2fZH+sTmSu6Z1xN3vKjP5wQmN7rL8sF4tG9yH3xQIgRpLsK2mN8vT6wY1zU4cD5eF7gH9iJ0kL2mN4oP6qR8s=",
"signature_input": "sig1=(\"@method\" \"@authority\" \"@path\" \"content-digest\" \"content-type\");created=1711289600;keyid=\"did:tenzro:machine:agent123\";nonce=\"abc123xyz456\""
}
}
}'Response:
{
"jsonrpc": "2.0",
"id": 8,
"result": {
"content": [
{
"type": "text",
"text": {
"verified": true,
"receipt": {
"payment_id": "visa_tap_pay_u4v5w6x7y8z9",
"challenge_id": "visa_tap_ch_x9y8z7w6v5u4",
"payer_did": "did:tenzro:machine:agent123",
"amount": 250000,
"asset": "TNZO",
"recipient": "0x0000000000000000000000000000000000000001",
"settlement_tx": "0x1122334455667788...",
"settled_at": "2026-03-24T14:22:35Z",
"stages_passed": [
"HeaderExtraction",
"KeyRetrieval",
"TimestampValidation",
"ReplayPrevention",
"DomainBinding",
"CryptographicVerification",
"ResultConstruction"
]
}
}
}
],
"isError": false
}
}The response shows verified: true and a receipt with the settlement transaction hash. The stages_passed array documents which verification stages succeeded.
Step 4: Understand the 7-Stage Pipeline
Visa TAP verification runs through a rigorous 7-stage pipeline to ensure cryptographic integrity and prevent fraud:
// 7-Stage Visa TAP Verification Pipeline
// Stage 1: Header Extraction
// Parses Signature-Input and Signature headers
// Example: sig1=("@method" "@authority" "@path" ...);created=1711289600;keyid="did:tenzro:machine:agent123"
// Stage 2: Key Retrieval
// Resolves agent DID via TDIP registry
// Fetches Ed25519 public key from identity document
// Stage 3: Timestamp Validation
// Checks 'created' parameter within max_signature_age_secs (480 seconds = 8 minutes)
// Prevents stale signatures from being replayed
// Stage 4: Replay Prevention
// Checks nonce against cache (in-memory or Redis)
// Each nonce can only be used once per challenge
// Stage 5: Domain Binding
// Verifies @authority matches required domain ("api.tenzro.network")
// Prevents cross-domain signature reuse
// Stage 6: Cryptographic Verification
// Constructs canonical signing payload per RFC 9421
// Verifies Ed25519 signature with agent's public key
// Stage 7: Result Construction
// Builds VerificationResult with all metadata
// Returns receipt with settlement transaction hashStage Breakdown
- Header Extraction — Parses
Signature-InputandSignatureheaders per RFC 9421 - Key Retrieval — Resolves agent DID via TDIP registry, fetches Ed25519 public key
- Timestamp Validation — Confirms
createdtimestamp is within 8-minute window - Replay Prevention — Checks nonce against cache (in-memory or Redis), each nonce usable once
- Domain Binding — Verifies
@authoritymatches required domain (api.tenzro.network) - Cryptographic Verification — Constructs canonical signing payload, verifies Ed25519 signature
- Result Construction — Builds
VerificationResultwith all metadata and settlement receipt
Step 5: Check Settlement Receipt
The settlement receipt provides a complete audit trail of the payment:
{
"payment_id": "visa_tap_pay_u4v5w6x7y8z9",
"challenge_id": "visa_tap_ch_x9y8z7w6v5u4",
"payer_did": "did:tenzro:machine:agent123",
"amount": 250000,
"asset": "TNZO",
"recipient": "0x0000000000000000000000000000000000000001",
"settlement_tx": "0x1122334455667788...",
"settled_at": "2026-03-24T14:22:35Z",
"stages_passed": [
"HeaderExtraction",
"KeyRetrieval",
"TimestampValidation",
"ReplayPrevention",
"DomainBinding",
"CryptographicVerification",
"ResultConstruction"
]
}How It Differs from MPP
Visa TAP (Cryptographic Identity)
- RFC 9421 HTTP signatures— Every request signed with agent's private key
- Stateless verification — No session management, signatures verified independently
- CDN-level authorization — Signatures can be verified at edge proxies before application layer
- Domain binding — Signatures tied to specific domains to prevent cross-site reuse
- Identity-first — Payment authorization tied directly to cryptographic identity
MPP (Session-Based Payments)
- Session tokens — Challenge → credential → voucher flow with session state
- Stateful verification — Requires session management and voucher tracking
- Application-level authorization — Verification happens at application server
- Session binding — Credentials tied to session IDs
- Payment-first — Authorization based on payment proof (vouchers)
Use Visa TAP when you need cryptographic agent identity verification, CDN-level authorization, or transparent commerce auditing. Use MPPfor streaming services, per-token billing, or when integrating with Stripe's Machine Payments Protocol.
Security Considerations
- Private key protection: Agent private keys must be stored securely in TEE enclaves or hardware wallets
- Nonce uniqueness: Every request must use a unique nonce to prevent replay attacks
- Timestamp validation: Enforce strict time windows (8 minutes) to limit signature lifetime
- Domain binding enforcement: Always verify
@authoritymatches expected domain - TDIP delegation scopes: Verify agent delegation scopes allow the payment amount, protocol, and chain
- Content-Digest integrity: Verify SHA-256 digest of request body matches
content-digestheader
What's Next?
- Payments with Mastercard Agent Pay — Learn Know Your Agent (KYA) verification and agentic token issuance
- Build an AI payment agent — Create an autonomous agent that uses Visa TAP
- RFC 9421: HTTP Message Signatures — Specification for HTTP signature format
- TDIP Documentation — Understand decentralized identity and delegation scopes
Ready to Accept Visa TAP Payments?
Explore the Tenzro Rust SDK for production-ready Visa TAP client and server implementations with RFC 9421 compliance and automatic signature verification.