# Clawallex - Virtual Card API for AI Agents

> Read this file, then follow the **Setup** section to install and configure Clawallex in your current environment.

Clawallex converts USDC stablecoin balances into virtual Visa/Mastercard cards that work at any online checkout. Two modes: wallet-funded (Mode A) or on-chain self-custody (Mode B).

---

## Setup for Agents

Most agents cannot complete browser-based OAuth flows. Use the CLI to obtain credentials, then configure MCP or Skill locally.

### If you are an Openclaw agent

```bash
openclaw plugins install @clawallex/clawallex
openclaw gateway restart
```

Then run `clawallex_setup` to configure credentials.

---

### Step 1 - Install CLI

```bash
npm install -g @clawallex/cli
```

### Step 2 - Signup (requires one user action)

```bash
clawallex signup
```

Opens a browser tab. The **user** completes signup. Once done, the CLI prints credentials to stdout:

```
API Key:    sk_live_xxxxxxxxxxxxxxxxxxxx
API Secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

**Agent: capture `API Key` and `API Secret` from stdout.**

### Step 3 - Configure (choose one)

#### Option A: Local MCP server

Add to the MCP config file for your AI client:

- **Claude Desktop (macOS):** `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Claude Desktop (Windows):** `%APPDATA%\Claude\claude_desktop_config.json`
Requires Node.js >= 22.

**Step 1 - Detect your environment, then configure:**

#### Claude Code (CLI)

Check: `which claude` returns a path.

```bash
claude mcp add clawallex \
  -e CLAWALLEX_API_KEY=sk_live_... \
  -e CLAWALLEX_API_SECRET=... \
  -- npx @clawallex/mcp-server
```

No restart needed - effective immediately.

#### Claude Desktop

Config file locations:
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
- Linux: `~/.config/Claude/claude_desktop_config.json`

Merge into `mcpServers`:

```json
{
  "mcpServers": {
    "clawallex": {
      "command": "npx",
      "args": ["@clawallex/mcp-server"],
      "env": {
        "CLAWALLEX_API_KEY": "sk_live_...",
        "CLAWALLEX_API_SECRET": "..."
      }
    }
  }
}
```

Restart Claude Desktop after writing the config.

#### Cursor

Config file: `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project-level).

```json
{
  "mcpServers": {
    "clawallex": {
      "command": "npx",
      "args": ["@clawallex/mcp-server"],
      "env": {
        "CLAWALLEX_API_KEY": "sk_live_...",
        "CLAWALLEX_API_SECRET": "..."
      }
    }
  }
}
```

#### Windsurf

Config file: `~/.codeium/windsurf/mcp_config.json`.

```json
{
  "mcpServers": {
    "clawallex": {
      "command": "npx",
      "args": ["@clawallex/mcp-server"],
      "env": {
        "CLAWALLEX_API_KEY": "sk_live_...",
        "CLAWALLEX_API_SECRET": "..."
      }
    }
  }
}
```

#### Gemini CLI

Config file: `~/.gemini/settings.json`.

```json
{
  "mcpServers": {
    "clawallex": {
      "command": "npx",
      "args": ["@clawallex/mcp-server"],
      "env": {
        "CLAWALLEX_API_KEY": "sk_live_...",
        "CLAWALLEX_API_SECRET": "..."
      }
    }
  }
}
```

#### OpenCode / other clients

Check the client's MCP configuration file (usually `mcp.json` or `settings.json` under the client's config directory). Add the same `mcpServers` block above.

**Step 2 - Verify and bind identity:**

```
clawallex_setup   # verify credentials are working
bootstrap         # bind client_id to your API key on the server
```

> **Why `bootstrap`?** When the MCP server starts with a fresh API key it auto-generates a local `client_id`. That local ID won't match the server-side binding until you call `bootstrap`. Skip this and cards/transactions may appear under an unbound identity.
> If `clawallex_setup` already returns a bound `client_id`, you can skip `bootstrap`.

#### Option B: Skill (Python, no MCP required)

Requires Python 3.9+. No external dependencies.

```bash
# Clone to a path of your choice, e.g. your agent's skill directory
git clone https://github.com/clawallex/clawallex-skill <PATH>

# Connect credentials (saved to ~/.clawallex/credentials.json)
python3 <PATH>/scripts/clawallex.py setup \
  --action connect \
  --api-key sk_live_... \
  --api-secret ...
```

Then bind identity:

```bash
python3 <PATH>/scripts/clawallex.py bootstrap
```

All subsequent commands:

```bash
python3 <PATH>/scripts/clawallex.py <command> [args]
```

> **Note:** Skill and MCP maintain separate `client_id` values. If you use both, run `bootstrap` in each to align them with the same API key on the server.

---

## Modes

| Mode | mode_code | Funding source |
|------|-----------|----------------|
| **Mode A** | 100 | USDC wallet balance on Clawallex platform |
| **Mode B** | 200 | Self-custody wallet - agent signs EIP-3009 on-chain, no platform wallet needed |

Check `mode_code` returned by `cards` to determine which refill path a card uses.

---

## Card Types

| Type | Created by | Behaviour |
|------|------------|-----------|
| **FlashCard** | `pay` | Single-use. Auto-destroyed after first transaction. Cannot be refilled. |
| **StreamCard** | `subscribe` | Reloadable. Persists across multiple transactions. Top up with `refill`. |

---

## MCP Tools

| Tool | Description |
|------|-------------|
| `clawallex_setup` | Verify credentials and check current `client_id` binding |
| `bootstrap` | Bind local `client_id` to your API key on the server - run after first install |
| `clawallex_pay` | Create a single-use FlashCard for immediate payment |
| `clawallex_subscribe` | Create a reloadable StreamCard for recurring charges |
| `clawallex_refill` | Top up a StreamCard balance |
| `get_wallet` | Check wallet balance (USDC) |
| `list_cards` | List all cards created by this agent |
| `get_card_details` | Retrieve card info with encrypted PAN/CVV |
| `decrypt_card_data` | Decrypt encrypted PAN/CVV - use only to fill checkout forms |

---

## Skill Command Reference

### Setup & Identity

| Intent | Command |
|--------|---------|
| Check config status | `setup --action status` |
| Connect account | `setup --action connect --api-key KEY --api-secret SECRET` |
| Get sign-up link | `setup --action register` |
| Check API key binding | `whoami` |
| Bind client_id | `bootstrap` |

### Payments

| Intent | Command |
|--------|---------|
| One-time payment | `pay --amount N --description "X"` |
| One-time payment with custom expiry | `pay --amount N --description "X" --ttl SECONDS` (default 86400 = 24h) |
| Start subscription | `subscribe --amount N --description "X"` |
| Top up a StreamCard | `refill --card-id CID --amount N` |

### Wallet & Cards

| Intent | Command |
|--------|---------|
| Check wallet balance | `wallet` |
| Get deposit addresses | `recharge-addresses --wallet-id WID` |
| List all cards | `cards` |
| Check card balance | `card-balance --card-id CID` |
| Batch check balances | `batch-balances --card-ids CID1,CID2` |
| Get card details | `card-details --card-id CID` |
| Update card controls | `update-card --card-id CID --client-request-id UUID [--tx-limit] [--allowed-mcc] [--blocked-mcc]` |
| View transactions | `transactions` |

### Advanced (Mode B / x402)

| Intent | Command |
|--------|---------|
| Get x402 payee address | `x402-address --chain ETH --token USDC` |

---

## Workflows

### Mode A - One-time payment

```
1. setup --action status        -> confirm configured
2. wallet                       -> verify sufficient balance
3. pay --amount 50 --description "OpenAI API credits"
                                -> returns card_id
4. card-details --card-id <ID>  -> returns masked_pan, expiry, encrypted PAN/CVV
5. Decrypt PAN/CVV (see below)  -> use ONLY to fill checkout form, never display
```

### Mode A - Subscription

```
1. wallet                                          -> verify balance
2. subscribe --amount 100 --description "AWS"      -> create StreamCard, returns card_id
3. card-details --card-id <ID>                     -> get card for sign-up form
4. refill --card-id <ID> --amount 50               -> top up before balance runs out
```

### Mode B - On-chain payment (two stages)

**Stage 1 - Quote (402 response is expected, not an error):**

```
pay --amount 200 --description "GPU rental" --mode-code 200 --chain-code ETH --token-code USDC
```

Returns: `client_request_id`, `payee_address`, `asset_address`, `x402_reference_id`, `payable_amount`, fee breakdown.

**Agent signs an EIP-3009 `transferWithAuthorization`** using its own wallet/signing library:
- `from`: agent wallet address
- `to`: `payee_address` from Stage 1
- `value`: `payable_amount * 10^6` (USDC = 6 decimals)
- `validAfter` / `validBefore`: unix timestamps (seconds)
- `nonce`: random 32-byte hex, unique per authorization

**Stage 2 - Settle (must use same `client_request_id` as Stage 1):**

```bash
pay --amount 200 --description "GPU rental" \
  --mode-code 200 \
  --client-request-id "<uuid from stage 1>" \
  --x402-version 1 \
  --payment-payload '{
    "scheme": "exact",
    "network": "ETH",
    "payload": {
      "signature": "0x<EIP-3009 signature>",
      "authorization": {
        "from": "0x<agent wallet>",
        "to": "<payee_address>",
        "value": "<payable_amount * 10^6>",
        "validAfter": "<unix seconds>",
        "validBefore": "<unix seconds>",
        "nonce": "0x<random 32-byte hex>"
      }
    }
  }' \
  --payment-requirements '{
    "scheme": "exact",
    "network": "ETH",
    "asset": "<asset_address>",
    "payTo": "<payee_address>",
    "maxAmountRequired": "<payable_amount * 10^6>",
    "extra": { "referenceId": "<x402_reference_id>" }
  }' \
  --extra '{"card_amount": "200.0000", "paid_amount": "<payable_amount>"}'
```

### Mode B - Refill (no 402 challenge, direct settle)

```
1. x402-address --chain ETH --token USDC   -> get payee_address
2. refill --card-id CID --amount 50 \
     --x402-reference-id "<unique ref id>" \
     --x402-version 1 \
     --payment-payload '{ ... EIP-3009 signature ... }' \
     --payment-requirements '{ ... }'
```

Idempotency key for Mode B refill is `x402_reference_id` (not `client_request_id`).

---

## Decrypting Card Data

`card-details` returns `encrypted_sensitive_data`. Decrypt PAN/CVV **only** to fill checkout forms - never display to user.

```
1. key   = HKDF-SHA256(ikm=api_secret, salt="", info="clawallex/card-sensitive-data/v1", length=32)
2. nonce = base64_decode(nonce)
3. raw   = base64_decode(ciphertext)
4. data  = raw[:-16],  tag = raw[-16:]
5. plain = AES-256-GCM(key, nonce, data, tag)   -> {"pan": "4111...", "cvv": "123"}
```

---

## Hard Rules

1. **Setup first** - run `setup --action status` before any payment; if not configured, run `setup --action connect`.
2. **Mode A**: run `wallet` before `pay` or `subscribe` to verify sufficient funds.
3. **Mode B**: 402 response on Stage 1 is expected - it is the quote, not an error.
4. **Never expose card secrets** - PAN/CVV are strictly for filling checkout forms. Show only `masked_pan` if asked.
5. **Confirm before paying** - echo amount and description back to user before creating a card.
6. **FlashCard**: one transaction only - do not attempt to reuse.
7. **StreamCard**: monitor with `card-balance`; refill before funds run out.
8. **Mode B settle**: `client_request_id` must be identical to Stage 1 - a different value creates a new order.
9. **One command at a time** - check output before proceeding to next step.
10. Minimum card amount: **$1.00 USD**.
11. Cards support online (card-not-present) only. No physical / in-store. No 3DS / SMS OTP.

---

## Error Recovery

| Error | Cause | Action |
|-------|-------|--------|
| "not configured" | No credentials saved | `setup --action connect` with valid credentials |
| "Invalid credentials" | Wrong key/secret | Check at app.clawallex.com/dashboard/settings |
| Cards/transactions not visible | `client_id` not bound to server | Run `bootstrap` (MCP) or `python3 ... bootstrap` (Skill) |
| Insufficient balance | Wallet too low | `recharge-addresses` to deposit, or switch to Mode B |
| 402 response | Mode B Stage 1 | Expected - proceed to Stage 2 with same `client_request_id` |
| Settle rejected | Invalid x402 params | Order stays `pending_payment` - fix params, retry same `client_request_id` |
| Card not found | Wrong card_id | Run `cards` to list valid IDs |
| Decryption failed | Wrong api_secret | Re-fetch `card-details`, verify credentials |

---

## Key Concepts

- **client_id**: Stable agent identity, separate from API key. Cards and transactions are isolated per `client_id`. Survives API key rotation. Stored at `~/.clawallex/credentials.json`.
  - **Auto-generation**: On first startup with a new API key, the MCP server generates a fresh local `client_id`. This won't match the server-side binding until you call `bootstrap`.
  - **Skill vs MCP isolation**: Skill and MCP each maintain their own `client_id`. If you use both, run `bootstrap` in each to align them under the same API key.
  - **`clawallex_setup` vs `bootstrap`**: `clawallex_setup` checks that credentials are valid and reports the current `client_id`. `bootstrap` registers/aligns that `client_id` with the server. Run `clawallex_setup` to diagnose, `bootstrap` to fix a mismatch.
- **Wallet**: USDC balance on Clawallex platform. Funds all Mode A operations.
- **mode_code 100**: Mode A (wallet). **mode_code 200**: Mode B (on-chain).

---

## Remote MCP (OAuth)

If your client supports browser-based OAuth, add directly - no API key required:

```json
{
  "mcpServers": {
    "clawallex": { "url": "https://api.clawallex.com/mcp" }
  }
}
```

---

## Links

- Dashboard: `https://app.clawallex.com`
- Docs: `https://docs.clawallex.com`
- GitHub: `https://github.com/clawallex`
- Skill repo: `https://github.com/clawallex/clawallex-skill`
