Concepts Deep Dive
x402 Protocol
x402 is an HTTP-native payment protocol based on the 402 Payment Required HTTP status code. It adds structured payment information to the 402 response so clients can pay programmatically.
The 402 Response
When an AI service requires payment, it returns:
HTTP/1.1 402 Payment Required
Content-Type: application/json
{
"x402Version": 2,
"accepts": [
{
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": "1500000",
"payTo": "0xServiceProviderAddress",
"maxTimeoutSeconds": 60
}
],
"resource": {
"url": "https://api.example.com/chat",
"description": "AI chat completion",
"mimeType": "application/json"
}
}Key fields:
x402Version: 2— protocol version (must be 2)scheme: "exact"— pay the exact amount specifiednetwork: "eip155:8453"— CAIP-2 identifier for Base blockchainasset— USDC token contract address on Baseamount: "1500000"— amount in smallest units (USDC has 6 decimals, so this is $1.50)payTo— the service provider's receiving addressmaxTimeoutSeconds— how long the service will wait for payment settlement
The Payment Headers
After signing, the client retries the original request with payment headers:
GET /chat HTTP/1.1
Host: api.example.com
X-Payment: <base64-encoded payment payload>
X-Payment-Signature: 0x<65-byte ECDSA signature in hex>The X-Payment header contains a base64-encoded JSON payload with the authorization details (from, to, value, nonce, validity window).
The X-Payment-Signature header contains the EIP-712 signature in Ethereum format: 0x + R (32 bytes) + S (32 bytes) + V (1 byte).
Protocol Flow
Agent AI Service Viatika
│ │ │
│── GET /resource ────────>│ │
│<── 402 + x402 payload ──│ │
│ │ │
│── POST /sign-payment ──────────────────────────>│
│ │ Policy check ✓ │
│ │ Budget check ✓ │
│ │ EIP-712 sign │
│<── signature + auth ───────────────────────────│
│ │ │
│── GET /resource ────────>│ │
│ + X-Payment headers │ │
│ │── verify sig │
│ │── submit on-chain │
│<── 200 + response ──────│ │Credit System
Units
| Unit | Value | Example |
|---|---|---|
| 1 credit | $0.001 USD | — |
| 1,000 credits | $1.00 USD | Small API call |
| 1,500 credits | $1.50 USD | amount: "1500000" in x402 (USDC micro-units) |
| 10,000 credits | $10.00 USD | Typical daily budget |
How Credits Work
- Organization purchases credits — via Stripe (recurring or one-time)
- Credits are pooled at the organization level in a single account
- When an x402 payment is signed, credits are consumed from the organization's account
- The platform wallet pays on-chain — Viatika's centralized wallet actually sends the USDC
- Credits track the liability — the organization owes Viatika for payments made on their behalf
Credit ↔ USDC Conversion
USDC uses 6 decimal places. The conversion:
USDC micro-units = credits × 1000
(because 1 credit = $0.001 = 1000 USDC micro-units)
Examples:
1500 credits = 1,500,000 USDC micro-units = $1.50
50 credits = 50,000 USDC micro-units = $0.05
10000 credits = 10,000,000 USDC micro-units = $10.00Balance Tracking
Credits are tracked with optimistic locking in PostgreSQL:
balancefield on the credit account (integer, in credits)- Every consumption creates a
credit_transactionrecord with negative amount - Every purchase creates a
credit_transactionrecord with positive amount - Daily reconciliation job verifies sum of transactions matches balance
Policy Engine
Every payment request goes through the policy engine before signing. The engine runs in under 10ms using Redis for hot-path checks.
Evaluation Order
1. Whitelist/Blacklist check (Redis)
→ Is this provider allowed?
2. Budget check (Redis Lua script, atomic)
→ Does the entity have budget remaining?
→ Atomically decrements budget if allowed
3. Policy rules (Cedar SDK)
→ Do IAM-style policies permit this action?
→ Checks amount limits, time windows, etc.
4. Approval check (PostgreSQL)
→ Does this require manual approval?If any check fails, the payment is denied immediately. No signature is created.
Budget Policies
Budgets are per-entity (user, team, or swarm) and per-period:
| Period | Description | Budget Key (Redis) |
|---|---|---|
hourly | Resets every hour | budget:user:{id}:hourly:2025-01-15T12 |
daily | Resets at midnight UTC | budget:user:{id}:daily:2025-01-15 |
weekly | Resets Monday UTC | budget:user:{id}:weekly:2025-W03 |
monthly | Resets 1st of month | budget:user:{id}:monthly:2025-01 |
quarterly | Resets per quarter | budget:user:{id}:quarterly:2025-Q1 |
Budget enforcement is atomic (Redis Lua script) — no race conditions even under concurrent requests.
Hierarchy
Budget checks cascade up the entity hierarchy:
Swarm "data-collector" (daily: 5000 credits)
└─ Team "research" (monthly: 50000 credits)
└─ Department "engineering" (quarterly: 500000 credits)A payment from the swarm must pass all three budget checks.
Whitelist
Providers can be whitelisted at two levels:
- Global: all entities in the organization can pay this provider
- Entity-level: specific entity can pay this provider
Whitelists can include constraints:
- Maximum single transaction amount
- Daily/monthly spending caps for that provider
- Effective date range
Denial Reasons
When a payment is denied, the response includes structured reasons:
{
"approved": false,
"denialReasons": [
{
"category": "budget-exceeded",
"code": "DAILY_LIMIT",
"message": "Daily budget of 5000 credits exceeded (current: 4800, requested: 1500)",
"policyId": "budget-policy-uuid"
}
]
}Categories:
budget-exceeded— entity budget limit reachednot-whitelisted— provider not on approved listrate-limit-exceeded— too many payments in time windowamount-exceeded— single payment exceeds maximumprovider-blocked— provider is on blacklist
EIP-712 Signing
Viatika signs payments using EIP-712 typed structured data. This is the Ethereum standard for signing human-readable data with domain separation.
What Gets Signed
An ERC-3009 TransferWithAuthorization message:
Domain:
name: "USD Coin"
version: "2"
chainId: 8453 (Base)
verifyingContract: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 (USDC on Base)
Message:
from: 0xViatikaWallet (platform wallet address)
to: 0xServiceProvider (payee address)
value: 1500000 (amount in USDC micro-units)
validAfter: 0 (immediately valid)
validBefore: 1769917810 (expires ~5 minutes from signing)
nonce: 0x<random 32 bytes> (replay protection)Signing Process (inside Viatika)
- Load keystore: encrypted private key, decrypted with Scrypt (~10ms first time, cached after)
- Construct EIP-712 TypedData: domain separator + message hash
- Hash:
Keccak256("\x19\x01" || domainSeparator || messageHash) - ECDSA Sign: produce V (27 or 28), R (32 bytes), S (32 bytes)
- Return: signature components + authorization details
Total signing time: ~100ms
Security Properties
- Time-bounded: signatures expire in ~5 minutes (
validBefore) - Replay-protected: random 32-byte nonce; USDC contract rejects used nonces
- Domain-separated: signature is only valid for USDC on the specific chain
- Non-extractable: private key never leaves the encrypted keystore
Settlement Flow
After Viatika signs the payment and the agent retries the request:
1. Agent sends request with X-Payment + X-Payment-Signature headers
2. AI Service receives the headers
3. AI Service (or its facilitator) verifies:
- Signature matches the authorization
- ecrecover returns the `from` address
- Nonce hasn't been used
- Current time is within validAfter/validBefore window
4. AI Service calls USDC contract: transferWithAuthorization(from, to, value, validAfter, validBefore, nonce, v, r, s)
5. USDC contract executes on Base blockchain:
- Verifies signature
- Transfers USDC from Viatika's wallet to Service Provider
- Emits Transfer event
6. Service processes the request and returns responseKey Points
- Viatika never submits on-chain transactions. The service provider (or their facilitator) submits the signed authorization.
- Settlement is asynchronous from signing. Viatika signs in ~150ms; on-chain settlement happens when the service submits.
- Credits are consumed at signing time, not at settlement time. This prevents double-spending.
- If settlement fails (e.g., nonce already used, insufficient USDC in wallet), that's between the service and the blockchain. The credits have already been consumed.
Transaction States
Viatika records every signed payment in blockchain_transactions:
| Status | Meaning |
|---|---|
signed | Signature created, returned to client |
submitted | Known to be submitted on-chain (if tracked) |
pending | Awaiting confirmation |
confirmed | Settled on-chain with sufficient confirmations |
failed | On-chain transaction failed |
expired | validBefore passed without settlement |
Platform Wallet
Viatika operates one platform wallet per blockchain (not per-organization). All organizations' payments are signed from the same wallet. This is by design:
- Simpler key management and security
- Single USDC balance to manage on-chain
- Organizations are tracked via credits, not on-chain accounts
- Key rotation happens at the platform level with zero impact on organizations