Beta — Smart contract audit in progress. We recommend keeping wallet balances under $100 USDC.
CardZero
Back to Docs

API Reference

Complete reference for the CardZero REST API. All requests and responses use JSON. The API base URL is https://api.cardzero.ai.

Authentication

CardZero uses two authentication methods:

JWT Token (Owner / Dashboard)

Obtained via /auth/claim or /auth/login. Pass in the Authorization header:

Authorization: Bearer eyJhbGci...
API Key (Agent)

Generated during wallet claim. Format: czapi_prefix_secret. Pass in the Authorization header:

Authorization: Bearer czapi_abc123_xxxxxxxx

API Keys are bound to a specific wallet. An Agent can only access the wallet its API Key was generated for. The walletId is derived from the API Key on the server -- it is not passed in the request body for payment endpoints.

Error Responses

All errors follow this format:

{
  "error": "ERROR_CODE",
  "message": "Human-readable description"
}
Common Error Codes
UNAUTHORIZED -- Missing or invalid token
FORBIDDEN -- Token valid but no access to this resource
INSUFFICIENT_BALANCE -- Wallet balance too low for payment + fee
WALLET_FROZEN -- Wallet is frozen by owner
WALLET_NOT_ACTIVE -- Wallet not yet claimed/deployed
NO_SESSION_KEY -- Internal signing key unavailable (auto-managed, rarely seen)
SPEND_LIMIT_EXCEEDED -- Per-transaction or daily limit exceeded
CLAIM_KEY_INVALID -- Claim key expired, used, or wrong
USERNAME_TAKEN -- Username already registered
DUPLICATE_PAYMENT -- Idempotency key already used

Endpoints

Authentication

POST/v1/wallets

Create a new wallet. This is a zero-gas, off-chain operation. Returns a wallet address (pre-computed via CREATE2) and a one-time claim key.

Auth: None
Request Body
{
  "name": "my-agent-wallet"  // optional
}
Response
{
  "walletId": "wallet_abc123",
  "walletAddress": "0x1234...5678",
  "claimKey": "czk_lookup_secret",
  "status": "pending"
}
Notes

The wallet address is valid immediately -- you can send USDC to it before claiming. The claim key expires after 7 days.

POST/v1/auth/claim

Claim a wallet, deploy the smart contract on-chain, and create an owner account. Returns JWT token and Agent API Key.

Auth: None (claimKey is the credential)
Request Body
{
  "claimKey": "czk_lookup_secret",
  "username": "alice",
  "password": "min8chars"
}
Response
{
  "token": "eyJhbGci...",
  "userId": "user_xyz",
  "walletId": "wallet_abc123",
  "walletAddress": "0x1234...5678",
  "agentConfig": {
    "apiKey": "czapi_prefix_secret",
    "walletId": "wallet_abc123"
  }
}
Notes

Takes 20-60 seconds (contract deployment). The API Key is shown only once. Username: 3-20 chars, alphanumeric + underscore. Password: min 8 chars.

POST/v1/auth/login

Log in with username and password. Returns JWT token for Dashboard access.

Auth: None (credentials)
Request Body
{
  "username": "alice",
  "password": "min8chars"
}
Response
{
  "token": "eyJhbGci...",
  "userId": "user_xyz"
}

Wallets

GET/v1/wallets

List all wallets owned by the authenticated user.

Auth: JWT
Response
{
  "wallets": [
    {
      "id": "wallet_abc123",
      "name": "my-agent-wallet",
      "chain_address": "0x1234...5678",
      "status": "active",
      "frozen": 0,
      "tx_limit": "5.00",
      "daily_limit": "50.00",
      "created_at": 1710000000
    }
  ]
}
GET/v1/wallets/:walletId

Get detailed information about a specific wallet, including a Config Summary block.

Auth: JWT (owner only)
Response
{
  "id": "wallet_abc123",
  "name": "my-agent-wallet",
  "chain_address": "0x1234...5678",
  "status": "active",
  "frozen": 0,
  "tx_limit": "5.00",
  "daily_limit": "50.00",
  "expires_at": null,
  "created_at": 1710000000
}
GET/v1/wallets/:walletId/balance

Query the USDC balance of a wallet on Base.

Auth: JWT or API Key
Response
{
  "walletId": "wallet_abc123",
  "balance": "142.50",
  "currency": "USDC"
}
PATCH/v1/wallets/:walletId/config

Update spending rules for the wallet. All fields are optional -- only provided fields are updated on-chain.

Auth: JWT (owner only)
Request Body
{
  "txLimit": "5.00",       // max USDC per transaction
  "dailyLimit": "50.00",   // max total USDC per day
  "expiresAt": 1710864000, // unix timestamp, policy expiry
  "whitelist": ["0xabc..."] // allowed recipient addresses
}
Response
{
  "message": "Config updated",
  "txHash": "0x9876..."
}
Notes

Values are in USDC (human-readable). The contract stores them as 6-decimal integers internally.

POST/v1/wallets/:walletId/freeze

Immediately freeze the wallet. All payment attempts will be rejected until unfrozen.

Auth: JWT (owner only)
Response
{
  "message": "Wallet frozen",
  "txHash": "0x9876..."
}
POST/v1/wallets/:walletId/unfreeze

Unfreeze a frozen wallet, allowing payments to resume.

Auth: JWT (owner only)
Response
{
  "message": "Wallet unfrozen",
  "txHash": "0x9876..."
}
POST/v1/wallets/:walletId/api-key/rotate

Generate a new API Key for this wallet. The old key is immediately invalidated.

Auth: JWT (owner only)
Response
{
  "apiKey": "czapi_newprefix_newsecret",
  "walletId": "wallet_abc123",
  "message": "API key rotated successfully"
}
Notes

The new API Key is shown only once. Give it to your Agent to replace the old one.

Payments

POST/v1/payments

Make a direct USDC payment from the wallet. The wallet ID is derived from the API Key -- do not pass it in the body.

Auth: API Key
Request Body
{
  "to": "0xrecipient...",
  "amount": "5.00",
  "idempotencyKey": "unique-request-id"  // optional, prevents duplicate payments
}
Response
{
  "paymentId": "pay_abc123",
  "status": "confirmed",
  "txHash": "0x1234...",
  "amount": "5.00",
  "fee": "0.10"
}
Notes

A 2% fee is deducted separately from the wallet. The recipient receives the full amount. Session keys are managed automatically.

GET/v1/payments/:paymentId

Check the status of a specific payment. Payment IDs are not guessable.

Auth: None
Response
{
  "id": "pay_abc123",
  "wallet_id": "wallet_abc123",
  "to_address": "0xrecipient...",
  "amount": "5.00",
  "type": "direct",
  "status": "confirmed",
  "tx_hash": "0x1234...",
  "fee_amount": "0.10",
  "created_at": 1710000000
}
GET/v1/wallets/:walletId/payments

List payment history for a wallet. Supports pagination.

Auth: JWT or API Key
Response
{
  "payments": [
    {
      "id": "pay_abc123",
      "wallet_id": "wallet_abc123",
      "to_address": "0xrecipient...",
      "amount": "5.00",
      "type": "direct",
      "memo": null,
      "status": "confirmed",
      "tx_hash": "0x1234...",
      "fee_amount": "0.10",
      "fee_recipient": "0xfee...",
      "created_at": 1710000000
    }
  ]
}
Notes

Query params: ?limit=20&offset=0. Type is 'direct' for regular payments, 'x402' for HTTP auto-payments.

POST/v1/x402/pay

Make an x402 protocol payment. Used by Agents to pay for HTTP resources that return 402 Payment Required with x402 headers.

Auth: API Key
Request Body
{
  "paymentHeader": "base64-encoded-json",
  "idempotencyKey": "unique-request-id"  // optional
}
Response
{
  "paymentId": "pay_xyz789",
  "status": "confirmed",
  "txHash": "0x5678...",
  "amount": "0.50",
  "fee": "0.01"
}
Notes

The paymentHeader is a base64-encoded JSON object from the x402 server's 402 response. It contains the recipient, amount, network, and asset information.

Rate Limits

Wallet Creation: 50 requests per IP per hour
Authentication (claim/login): 10 requests per IP per hour
Payments: 60 requests per API Key per minute

Rate limit headers are included in responses: X-RateLimit-Remaining, X-RateLimit-Reset.