Visa Trusted Agent Protocol (TAP)
Visa Trusted Agent Protocol (TAP) enables AI agents to make purchases and access resources by signing HTTP requests using RFC 9421 HTTP Message Signatures. TAP provides a three-layer verification system: Agent Recognition (cryptographic identity), Consumer Recognition (human delegation), and Payment Container (settlement details). On Tenzro Network, TAP leverages TDIP for decentralized agent identities and TEE-backed key storage.
Overview
Visa TAP solves the core challenge of agentic commerce: how do merchants verify that an AI agent is authorized to make purchases on behalf of a human consumer? Traditional payment flows assume a human is directly interacting with the merchant, but in an AI-first world, agents act autonomously within delegated boundaries.
TAP introduces three distinct data layers:
- Agent Recognition — Cryptographic proof of agent identity using RFC 9421 HTTP signatures with Ed25519 or RSA-PSS-SHA256
- Consumer Recognition — JWT ID token proving the human consumer who delegated authority to the agent, including delegation scope
- Payment Container — Payment method details (card token, stablecoin address, Tenzro settlement ID) for transaction execution
Architecture
Visa TAP places a CDN Proxy between the agent and merchant to perform verification before the request reaches the merchant's infrastructure:
┌──────────────┐
│ AI Agent │
│ (Tenzro) │
└──────┬───────┘
│ HTTP POST /checkout
│ Signature-Input: sig=(...);created=1711374000;nonce="abc123";keyid="did:tenzro:machine:agent123"
│ Signature: sig=:SGVsbG8gV29ybGQ=:
│ Authorization: Bearer <JWT ID token>
│ X-Payment-Container: {"method":"TenzroSettlement","settlement_id":"..."}
▼
┌─────────────────────────────────────────┐
│ CDN Proxy (7-Stage Verification) │
│ 1. Header Extraction │
│ 2. Key Retrieval (TDIP registry) │
│ 3. Timestamp Validation (±8 min) │
│ 4. Replay Prevention (nonce cache) │
│ 5. Domain Binding (@authority) │
│ 6. Cryptographic Verification │
│ 7. Result Construction │
└──────┬──────────────────────────────────┘
│ Verified request
│ X-Agent-DID: did:tenzro:machine:agent123
│ X-Consumer-DID: did:tenzro:human:user456
│ X-Verification-Status: verified
▼
┌──────────────┐ ┌──────────────────┐
│ Merchant │────────>│ Agent Registry │
│ Server │ Lookup │ (TDIP on-chain) │
└──────────────┘ └──────────────────┘The CDN Proxy sits at the edge, close to the agent, performing all cryptographic verification before forwarding the request to the merchant. This reduces latency and protects the merchant from processing invalid requests.
7-Stage Verification Pipeline
The CDN Proxy implements a rigorous 7-stage pipeline to verify agent requests:
- Header Extraction — Parse
Signature-InputandSignatureheaders per RFC 9421. Extract keyid, algorithm, created timestamp, nonce, and covered components. - Key Retrieval — Resolve the agent's public key from the TDIP registry using the
keyid(DID format:did:tenzro:machine:*). Verify the agent identity is Active and not Suspended or Revoked. - Timestamp Validation — Verify the
createdtimestamp is within ±8 minutes of the current time. This prevents old signatures from being reused and accounts for clock skew across distributed systems. - Replay Prevention — Check the
nonceagainst an in-memory cache with 8-minute TTL. Reject duplicate nonces to prevent replay attacks where a valid signature is intercepted and resubmitted. - Domain Binding — Verify the
@authoritycomponent in the signature matches the request's Host header. This prevents signatures from being reused against different merchants (cross-site signature theft). - Cryptographic Verification — Reconstruct the signature base string per RFC 9421 Section 2.5, then verify the signature using the agent's public key with Ed25519 or RSA-PSS-SHA256 algorithm via
tenzro_crypto::signatures::verify(). - Result Construction — Build a
VerificationResultwith validity status, agent DID, algorithm used, and any error messages. Inject verification metadata into request headers for downstream processing.
Agent Recognition
Agent Recognition is the cryptographic proof of agent identity. The agent signs the HTTP request with its private key, and the CDN Proxy verifies the signature using the agent's public key from the TDIP registry.
use tenzro_payments::visa_tap::*;
use tenzro_crypto::Ed25519Signer;
#[derive(Debug, Clone)]
pub struct AgentRecognition {
/// RFC 9421 signature input header
pub signature_input: String,
/// RFC 9421 signature header
pub signature: String,
/// Agent DID (did:tenzro:machine:*)
pub key_id: String,
/// Signing algorithm (Ed25519 or RsaPssSha256)
pub algorithm: SigningAlgorithm,
/// Unix timestamp when signature was created
pub created_at: i64,
/// Random nonce for replay prevention
pub nonce: String,
}
// Example: Creating a signed request
async fn create_agent_request(
agent_did: &str,
agent_signer: &Ed25519Signer,
) -> Result<http::Request<String>, VisaTapError> {
let request_body = json!({
"items": [{"product_id": "prod_123", "quantity": 1}],
"total_amount": 29.99,
"currency": "USD"
}).to_string();
let request = http::Request::builder()
.method("POST")
.uri("https://merchant-xyz.tenzro.network/api/checkout")
.header("content-type", "application/json")
.body(request_body)?;
// Sign the request with RFC 9421
let signer_config = SignerConfig {
key_id: agent_did.to_string(),
algorithm: SigningAlgorithm::Ed25519,
covered_components: vec![
"@authority".into(),
"@path".into(),
"@method".into(),
"content-type".into(),
"content-digest".into(),
],
};
let signed_request = sign_request(
request,
agent_signer,
signer_config,
).await?;
Ok(signed_request)
}Consumer Recognition
Consumer Recognition proves that a human consumer has delegated authority to the agent. This is critical for compliance with payment regulations that require proof of consumer consent.
The agent includes a JWT ID token in the Authorization header, issued by the consumer's identity provider (Tenzro Network for TDIP identities). The JWT contains:
- sub — Consumer's DID (
did:tenzro:human:*) - agent_did — Agent's DID (
did:tenzro:machine:*) - delegation_scope_id — Reference to the delegation scope defining spending limits, allowed merchants, time bounds, etc.
- iat/exp — Token issued/expiration timestamps
use tenzro_payments::visa_tap::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConsumerRecognition {
/// JWT ID token from consumer's identity provider
pub id_token: String,
/// Delegation scope ID (from TDIP registry)
pub delegation_scope_id: String,
/// Consumer DID (did:tenzro:human:*)
pub consumer_did: String,
/// Agent DID (did:tenzro:machine:*)
pub agent_did: String,
}
// Example: Verifying consumer delegation
async fn verify_consumer_delegation(
id_token: &str,
identity_registry: &IdentityRegistry,
) -> Result<ConsumerRecognition, VisaTapError> {
// Decode JWT (signature verified by identity provider)
let claims: JwtClaims = decode_jwt(id_token)?;
// Resolve consumer identity
let consumer = identity_registry
.resolve_identity(&claims.sub)
.await?;
// Verify agent is in consumer's controlled_machines list
if !consumer.controlled_machines.contains(&claims.agent_did) {
return Err(VisaTapError::UnauthorizedAgent);
}
// Resolve delegation scope
let scope = identity_registry
.get_delegation_scope(&claims.delegation_scope_id)
.await?;
// Verify delegation hasn't expired
if let Some(end_time) = scope.time_bound.end_time {
if Utc::now().timestamp() > end_time {
return Err(VisaTapError::DelegationExpired);
}
}
Ok(ConsumerRecognition {
id_token: id_token.to_string(),
delegation_scope_id: claims.delegation_scope_id,
consumer_did: claims.sub,
agent_did: claims.agent_did,
})
}Payment Container
The Payment Container carries payment method details separate from the identity layers. This separation allows the same agent identity to use different payment methods across transactions.
use tenzro_payments::visa_tap::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentContainer {
/// Payment method variant
pub method: PaymentMethod,
/// Currency code (USD, EUR, TNZO)
pub currency: String,
/// Optional merchant category code
pub mcc: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PaymentMethod {
/// Tenzro Network settlement
TenzroSettlement {
settlement_id: String,
escrow_address: String,
},
/// Tokenized card (Visa token service)
CardToken {
token: String,
token_requestor_id: String,
cryptogram: String,
},
/// Stablecoin transfer (USDC, USDT)
StablecoinTransfer {
token_address: String,
chain_id: u64,
transfer_signature: String,
},
}
// Example: Creating a payment container
fn create_payment_container(
settlement_id: String,
) -> PaymentContainer {
PaymentContainer {
method: PaymentMethod::TenzroSettlement {
settlement_id: settlement_id.clone(),
escrow_address: format!("0x{}", hex::encode(settlement_id.as_bytes())),
},
currency: "TNZO".to_string(),
mcc: Some("5734".to_string()), // Computer software stores
}
}Tenzro Integration
Tenzro Network provides a fully decentralized implementation of Visa TAP, replacing Visa's centralized agent registry with TDIP on-chain identities:
- Agent Registry — TDIP IdentityRegistry on Tenzro Ledger replaces centralized Visa registry. All agent DIDs are on-chain and verifiable by anyone.
- Key Storage — Agent private keys stored in TEE enclaves (Intel TDX, AMD SEV-SNP, AWS Nitro) or MPC wallets, never exposed to application code.
- Settlement — Native settlement via Tenzro Ledger using TNZO token, eliminating card network fees for on-chain merchants.
- Delegation Scopes — Fine-grained spending limits, merchant allowlists, time bounds, and protocol restrictions enforced on-chain via TDIP delegation scopes.
use tenzro_payments::visa_tap::*;
use tenzro_payments::PaymentProtocol;
// Initialize Visa TAP server with Tenzro components
let visa_tap_server = VisaTapServer::new(
identity_registry.clone(),
settlement_engine.clone(),
nonce_cache.clone(),
);
// Handle incoming agent request
async fn handle_agent_checkout(
request: http::Request<String>,
visa_tap_server: &VisaTapServer,
) -> Result<CheckoutResponse, VisaTapError> {
// Verify RFC 9421 signature (7-stage pipeline)
let verification_result = visa_tap_server
.verify_agent_signature(&request)
.await?;
if !verification_result.valid {
return Err(VisaTapError::InvalidSignature);
}
// Extract consumer JWT
let auth_header = request.headers()
.get("authorization")
.ok_or(VisaTapError::MissingConsumerToken)?;
let id_token = auth_header
.to_str()?
.strip_prefix("Bearer ")
.ok_or(VisaTapError::InvalidAuthHeader)?;
// Verify consumer delegation
let consumer_recognition = visa_tap_server
.verify_consumer_delegation(id_token)
.await?;
// Extract payment container
let payment_header = request.headers()
.get("x-payment-container")
.ok_or(VisaTapError::MissingPaymentContainer)?;
let payment_container: PaymentContainer =
serde_json::from_str(payment_header.to_str()?)?;
// Execute settlement
let settlement_result = visa_tap_server
.execute_settlement(
&verification_result.agent_did,
&consumer_recognition.consumer_did,
&payment_container,
29.99, // amount
)
.await?;
Ok(CheckoutResponse {
status: "confirmed".to_string(),
transaction_id: settlement_result.transaction_id,
agent_did: verification_result.agent_did,
consumer_did: consumer_recognition.consumer_did,
})
}MCP Usage
AI agents can create Visa TAP payment challenges programmatically using Tenzro's Model Context Protocol (MCP) server:
# Create a Visa TAP payment challenge
curl -X POST https://mcp.tenzro.network/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "create_payment_challenge",
"arguments": {
"protocol": "visa-tap",
"amount": "29.99",
"currency": "USD",
"merchant_id": "merchant_123",
"resource_url": "https://merchant-xyz.tenzro.network/api/checkout",
"requires_consumer_jwt": true,
"allowed_payment_methods": ["TenzroSettlement", "CardToken"]
}
},
"id": 1
}'
# Response
{
"jsonrpc": "2.0",
"result": {
"content": [{
"type": "text",
"text": "{\"challenge_id\":\"ch_abc123\",\"protocol\":\"visa-tap\",\"amount\":\"29.99\",\"currency\":\"USD\",\"merchant_id\":\"merchant_123\",\"resource_url\":\"https://merchant-xyz.tenzro.network/api/checkout\",\"requires_consumer_jwt\":true,\"allowed_payment_methods\":[\"TenzroSettlement\",\"CardToken\"],\"expires_at\":1711377600}"
}]
},
"id": 1
}Security Considerations
- Timestamp Validation Window — The ±8 minute window balances security and usability. Narrower windows increase replay protection but cause issues with clock skew; wider windows increase risk.
- Nonce Replay Protection — Each request must have a unique nonce. The 8-minute TTL prevents unbounded memory growth while covering the timestamp validation window.
- Domain Binding Prevents Cross-Site Attacks — The
@authoritycomponent ensures a signature for merchant A cannot be reused against merchant B, even if the agent is authorized for both. - Content-Digest Integrity — The
content-digestheader (SHA-256 hash of request body) prevents tampering with payment amounts or item quantities after signature creation. - TEE Key Storage — Agent private keys are stored in hardware-backed Trusted Execution Environments, preventing extraction even if the agent's host system is compromised.
Next Steps
- Compare with Mastercard Agent Pay for identity-first agentic commerce
- Review RFC 9421 for HTTP signature implementation details
- Explore TDIP identity protocol for agent DID management
- Learn about TEE key storage for secure agent key management
- Try the quick start tutorial to deploy your first Visa TAP agent