OpenCard API Documentation
x402-Powered Virtual Card Issuance for AI Agents
OpenCard lets AI agents autonomously purchase and manage virtual debit cards by paying with USDC on Base via the x402 payment protocol.
No accounts. No API keys. No signups. Payment is authentication.
Base URL
https://api.opencard.devOverview
OpenCard exposes a REST API with three classes of endpoints:
| Type | Auth Method | Cost | Use Case |
|---|---|---|---|
| Public | None | Free | Health checks, pricing info, tier discovery |
| Paid (x402) | USDC payment on Base | Varies by tier | Creating and funding cards |
| Wallet-signed | EIP-712 signature | Free | Viewing cards, freeze/unfreeze |
SDK
The official client SDK wraps the raw x402 flow (402 → parse challenge → pay USDC → retry with proof) into one-liner methods. No need to handle the payment handshake yourself.
Install
npm install @opencardsdk/sdk viemQuick Start
import { OpenCardClient } from '@opencardsdk/sdk';
const client = new OpenCardClient({
privateKey: '0x...', // or pass a viem WalletClient
baseUrl: 'https://api.opencard.dev',
});
// One line — SDK handles payment automatically
const card = await client.createCard({
amount: 10, // $10 tier
nameOnCard: 'AI AGENT',
email: '[email protected]',
});
console.log(card.cardDetails); // { cardNumber, cvv, expiry, ... }Configuration
new OpenCardClient(config)
| Parameter | Type | Description |
|---|---|---|
privateKey | 0x${string} | Hex private key — SDK creates wallet internally. Provide either this or walletClient. |
walletClient | WalletClient | Your own viem WalletClient (takes precedence over privateKey). |
baseUrl | string | API URL (default: https://api.opencard.dev) |
rpcUrl | string | Base RPC URL (default: public RPC) |
timeout | number | Request timeout in ms (default: 60000) |
Methods
client.createCard(params): Promise<CardResult>
Create a virtual card. Pays USDC automatically.
const result = await client.createCard({
amount: 50, // 10 | 25 | 50 | 100 | 200 | 500
nameOnCard: 'AI AGENT',
email: '[email protected]',
});client.fundCard(params): Promise<FundResult>
Fund an existing card.
const result = await client.fundCard({
amount: 25,
cardId: 'card-uuid',
});client.getTiers(): Promise<Tier[]>
Get pricing tiers and fee breakdown (no payment required).
client.health(): Promise<{ status: string }>
Check if the OpenCard API is reachable.
client.address: 0x${'string'}
The wallet address being used for payments.
Error Handling
import {
OpenCardClient,
InsufficientBalanceError,
PaymentError,
ApiError,
TimeoutError,
} from '@opencardsdk/sdk';
try {
const card = await client.createCard({ ... });
} catch (error) {
if (error instanceof InsufficientBalanceError) {
console.log(`Need ${error.required}, have ${error.available}`);
} else if (error instanceof PaymentError) {
console.log(`Payment failed: ${error.message}, tx: ${error.txHash}`);
} else if (error instanceof ApiError) {
console.log(`Server error ${error.status}:`, error.body);
} else if (error instanceof TimeoutError) {
console.log('Request timed out');
}
}Low-Level x402 Utilities
For custom integrations, the x402 protocol helpers are exported directly:
import {
parseChallenge,
checkBalance,
executePayment,
buildPaymentProof,
handleX402Payment,
} from '@opencardsdk/sdk';How It Works
Your Code SDK OpenCard API Base Chain
| | | |
|-- createCard ->| | |
| |--- POST /create/tier -->| |
| |<-- 402 + challenge -----| |
| | | |
| |--- USDC.transfer() ---|-------- tx --->|
| |<-- txHash ------------|--- receipt ----|
| | | |
| |--- POST + X-Payment -->| |
| |<-- 201 + card details -| |
|<- CardResult --| | |Authentication
OpenCard uses two authentication modes depending on the endpoint.
x402 Payment Flow
Paid endpoints use the x402 protocol — your agent pays USDC on Base, and the payment itself serves as authentication. No pre-registration required.
Step 1 — Request without payment
curl -X POST https://api.opencard.dev/cards/create/tier/10 \
-H "Content-Type: application/json" \
-d '{"nameOnCard": "AGENT ALPHA", "email": "[email protected]"}'Step 2 — Receive 402 with payment instructions
{
"x402Version": 1,
"accepts": [
{
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"maxAmountRequired": "17200000",
"payTo": "0x3Be892b244B7CE6731A297A7eBca16F16f27c288",
"maxTimeoutSeconds": 300,
"resource": "/cards/create/tier/10",
"description": "Create card with $10 load"
}
]
}| Parameter | Type | Description |
|---|---|---|
network | string | eip155:8453 — Base Mainnet |
asset | string | USDC contract on Base |
maxAmountRequired | string | Amount in USDC with 6 decimals (17200000 = $17.20) |
payTo | string | Treasury wallet address |
maxTimeoutSeconds | number | Payment window (5 minutes) |
Step 3 — Agent pays USDC on Base
Parse the accepts array, send the specified USDC amount to the payTo address on Base.
Step 4 — Retry with payment proof
Resend the original request with the X-Payment header containing a base64-encoded JSON payload:
{
"scheme": "exact",
"network": "eip155:8453",
"payload": {
"authorization": {
"from": "0xYourAgentWallet",
"to": "0xTreasuryAddress",
"value": "17200000"
},
"txHash": "0xYourTransactionHash..."
}
}@x402/client or @coinbase/x402), steps 2–4 are handled automatically. Your agent just makes a single request and the library manages the payment handshake.Wallet Signature (Free Endpoints)
For read operations and card management, authenticate by signing an EIP-712 typed message with your wallet.
Required Headers
| Parameter | Type | Description |
|---|---|---|
X-WALLET-ADDRESS | string | Your 0x... wallet address |
X-WALLET-SIGNATURE | string | EIP-712 signature |
X-WALLET-TIMESTAMP | string | Unix timestamp (must be within 5 minutes of server time) |
EIP-712 Domain & Types
// Domain
{ "name": "OpenCard", "version": "1", "chainId": 8453 }
// Types
{ "Auth": [
{ "name": "action", "type": "string" },
{ "name": "timestamp", "type": "uint256" }
] }
// Message
{ "action": "opencard-auth", "timestamp": 1707600000 }Example using viem
import { createWalletClient, http } from 'viem';
import { base } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY');
const timestamp = Math.floor(Date.now() / 1000);
const signature = await account.signTypedData({
domain: { name: 'OpenCard', version: '1', chainId: 8453 },
types: {
Auth: [
{ name: 'action', type: 'string' },
{ name: 'timestamp', type: 'uint256' },
],
},
primaryType: 'Auth',
message: { action: 'opencard-auth', timestamp: BigInt(timestamp) },
});
const response = await fetch('https://api.opencard.dev/cards', {
headers: {
'X-WALLET-ADDRESS': account.address,
'X-WALLET-SIGNATURE': signature,
'X-WALLET-TIMESTAMP': String(timestamp),
},
});Pricing
Card Creation
Creating a new virtual card includes the card load amount plus fees.
| Load | Issuance | Top-Up | Service | Total | Endpoint |
|---|---|---|---|---|---|
| $10 | $3.00 | $2.20 | $2.00 | $17.20 | /cards/create/tier/10 |
| $25 | $3.00 | $2.50 | $2.00 | $32.50 | /cards/create/tier/25 |
| $50 | $3.00 | $3.00 | $2.00 | $58.00 | /cards/create/tier/50 |
| $100 | $3.00 | $4.00 | $3.00 | $110.00 | /cards/create/tier/100 |
| $200 | $3.00 | $6.00 | $5.00 | $214.00 | /cards/create/tier/200 |
| $500 | $3.00 | $12.00 | $7.00 | $522.00 | /cards/create/tier/500 |
Card Funding
Adding funds to an existing card — no issuance fee.
| Fund | Top-Up | Service | Total | Endpoint |
|---|---|---|---|---|
| $10 | $2.20 | $2.00 | $14.20 | /cards/fund/tier/10 |
| $25 | $2.50 | $2.00 | $29.50 | /cards/fund/tier/25 |
| $50 | $3.00 | $2.00 | $55.00 | /cards/fund/tier/50 |
| $100 | $4.00 | $3.00 | $107.00 | /cards/fund/tier/100 |
| $200 | $6.00 | $5.00 | $211.00 | /cards/fund/tier/200 |
| $500 | $12.00 | $7.00 | $519.00 | /cards/fund/tier/500 |
Endpoints
Public Endpoints
These require no authentication.
Health check. Use this to verify the API is running.
{
"status": "ok",
"timestamp": "2026-02-11T14:00:00.000Z",
"version": "1.0.0"
}Returns the full pricing breakdown for all creation and funding tiers.
{
"creation": {
"tiers": [{
"loadAmount": 10,
"totalCost": 17.2,
"issuanceFee": 3.0,
"topUpFee": 2.2,
"ourFee": 2.0,
"endpoint": "/cards/create/tier/10"
}]
},
"funding": {
"tiers": [{
"fundAmount": 10,
"totalCost": 14.2,
"topUpFee": 2.2,
"ourFee": 2.0,
"endpoint": "/cards/fund/tier/10"
}]
}
}Returns available tiers with endpoints and detailed fee breakdowns.
{
"creation": [{
"loadAmount": 10,
"totalCost": 17.2,
"endpoint": "/cards/create/tier/10",
"breakdown": {
"cardLoad": 10, "issuanceFee": 3, "topUpFee": 2.2, "ourFee": 2, "buffer": 0
}
}],
"funding": [{
"fundAmount": 10,
"totalCost": 14.2,
"endpoint": "/cards/fund/tier/10",
"breakdown": { "fundAmount": 10, "topUpFee": 2.2, "ourFee": 2 }
}]
}Paid Endpoints (x402)
These endpoints require USDC payment via the x402 protocol on Base. See Authentication → x402 Payment Flow.
Create a new virtual card loaded with the specified tier amount.
Available tiers: 10, 25, 50, 100, 200, 500
| Parameter | Type | Description |
|---|---|---|
nameOnCard | string | Cardholder name (uppercase recommended) |
email | string | Email for card notifications |
// Response 201 Created (after successful payment)
{
"success": true,
"card": {
"cardId": "550e8400-e29b-41d4-a716-446655440000",
"nameOnCard": "AGENT ALPHA",
"balance": 10,
"status": "active",
"createdAt": "2026-02-11T14:00:00.000Z"
},
"payment": {
"amountCharged": 17.2,
"txHash": "0x...",
"network": "base"
},
"details": {
"cardNumber": "4111111111111111",
"expiryMonth": 12,
"expiryYear": 2028,
"cvv": "123",
"billingAddress": {
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zip": "94105",
"country": "US"
}
}
}GET /cards/:cardId/details but that endpoint is rate-limited.Add funds to an existing card.
| Parameter | Type | Description |
|---|---|---|
cardId | string (UUID) | The card to fund |
// Response 200 OK
{
"success": true,
"cardId": "550e8400-e29b-41d4-a716-446655440000",
"fundedAmount": 25,
"newBalance": 35.0,
"payment": {
"amountCharged": 29.4,
"txHash": "0x...",
"network": "base"
}
}Wallet-Signed Endpoints
These endpoints are free but require wallet signature authentication.
List all cards owned by the authenticated wallet.
{
"cards": [{
"cardId": "550e8400-e29b-41d4-a716-446655440000",
"nameOnCard": "AGENT ALPHA",
"lastFour": "1111",
"balance": 10.0,
"status": "active",
"createdAt": "2026-02-11T14:00:00.000Z"
}]
}Get detailed info for a specific card. Fetches live balance from the card provider.
{
"card": {
"cardId": "550e8400-e29b-41d4-a716-446655440000",
"nameOnCard": "AGENT ALPHA",
"email": "[email protected]",
"balance": 8.5,
"initialAmountUsd": 10,
"status": "active",
"createdAt": "2026-02-11T14:00:00.000Z",
"updatedAt": "2026-02-11T15:30:00.000Z"
}
}Retrieve sensitive card details — full card number, CVV, expiry, and billing address.
{
"details": {
"cardNumber": "4111111111111111",
"expiryMonth": 12,
"expiryYear": 2028,
"cvv": "123",
"billingAddress": {
"street": "123 Main St",
"city": "San Francisco",
"state": "CA",
"zip": "94105",
"country": "US"
}
}
}Freeze a card. Blocks all transactions until unfrozen.
{ "success": true, "cardId": "550e8400-...", "status": "frozen" }Unfreeze a previously frozen card.
{ "success": true, "cardId": "550e8400-...", "status": "active" }Errors
All error responses follow a consistent format:
{ "error": "Human-readable error message" }| Status | Meaning |
|---|---|
400 | Invalid request body or parameters |
401 | Missing or invalid authentication |
402 | Payment required — follow the x402 flow |
404 | Card not found or doesn't belong to your wallet |
429 | Rate limit exceeded — slow down |
500 | Internal server error |
Rate Limits
| Scope | Limit |
|---|---|
| General API | 100 requests / 15 min per IP |
| Paid endpoints | 10 requests / 5 min per IP |
| Card details | 3 requests / hour per card |
When you hit a rate limit, the API returns 429 Too Many Requests. Wait for the window to reset before retrying.
Architecture
AI Agent (Base wallet + USDC)
│
▼
HTTP Request (no auth)
│
▼
┌──────────────────────────────┐
│ OpenCard API │
│ (Express + x402) │
│ │
│ x402 paid (tier-based): │
│ POST /cards/create/tier/N │
│ POST /cards/fund/tier/N │
│ │
│ Wallet-signed (free): │
│ GET /cards │
│ GET /cards/:id │
│ GET /cards/:id/details │
│ POST /cards/:id/freeze │
│ POST /cards/:id/unfreeze │
│ │
│ Public: │
│ GET /health │
│ GET /pricing │
│ GET /cards/tiers │
└──────────┬───────────────────┘
│
┌─────┴─────┐
▼ ▼
Coinbase Card
x402 Issuing
Facilitator Provider