# Viaclave — The Payment Rail for AI Agents

Viaclave gives AI agents Solana wallets, payments, swaps, and transaction verification through one API. No private keys, no seed phrases. Just an API key.

**Supported stablecoins:** USDC, USDT, PYUSD, EURC (mainnet). All are held natively. No auto-conversion.

## Setup

Base URL: `https://api.viaclave.com/v1`
Auth: `Authorization: Bearer <your_api_key>`

MCP server: `https://mcp.viaclave.com` (same auth header)

Get an API key at https://viaclave.com (email/password, GitHub, or Google signup).

**Auth model:** The API uses Bearer API keys only. The dashboard supports email/password, GitHub, and Google login, but agents should use API keys for all programmatic access.

---

## Wallet Tools

### create_wallet
Create a custodial wallet on Solana.
```
POST /v1/wallets
Body: { "label": "my-agent" }
Returns: { "wallet_id": "wal_...", "label": "my-agent", "deposit_address": "7xKX..." }
```

### list_wallets
List all wallets on your account.
```
GET /v1/wallets
Returns: { "wallets": [{ "wallet_id": "wal_...", "label": "my-agent", "deposit_address": "7xKX..." }] }
```

### get_balance
Check wallet USDC and SOL balance, daily spending, and limits.
```
GET /v1/wallets/{wallet_id}/balance
Returns: { "wallet_id": "wal_...", "balance": 25000000, "daily_limit": 50000000, "daily_spent": 5000000 }
```

### get_balances
Get all stablecoin balances for a wallet. Returns SOL plus every supported stablecoin, including zero-balance entries for discoverability.
```
GET /v1/wallets/{wallet_id}/balances
Returns: {
  "wallet_id": "wal_...",
  "sol_lamports": 50000000,
  "tokens": [
    { "symbol": "USDC", "mint": "EPjFWdd5...", "balance": 25000000, "decimals": 6 },
    { "symbol": "USDT", "mint": "Es9vMFrz...", "balance": 0, "decimals": 6 },
    { "symbol": "PYUSD", "mint": "2b1kV6Dk...", "balance": 5000000, "decimals": 6 },
    { "symbol": "EURC", "mint": "HzwqbKZw...", "balance": 0, "decimals": 6 }
  ]
}
```

### get_deposit_address
Get the Solana address to receive stablecoin deposits. Returns the list of accepted tokens and their mints.
```
GET /v1/wallets/{wallet_id}/deposit-address
Returns: {
  "wallet_id": "wal_...",
  "deposit_address": "7xKX...",
  "accepted": [
    { "symbol": "USDC", "name": "USD Coin", "mint": "EPjFWdd5...", "decimals": 6 },
    { "symbol": "USDT", "name": "Tether USD", "mint": "Es9vMFrz...", "decimals": 6 },
    { "symbol": "PYUSD", "name": "PayPal USD", "mint": "2b1kV6Dk...", "decimals": 6 },
    { "symbol": "EURC", "name": "Euro Coin", "mint": "HzwqbKZw...", "decimals": 6 }
  ],
  "note": "Send any accepted stablecoin to the deposit address. Each is credited natively. No auto-swap."
}
```

### set_spending_limits
Set daily and per-transaction caps on a wallet.
```
PUT /v1/wallets/{wallet_id}/limits
Body: { "daily_limit": 50000000, "per_tx_limit": 5000000 }
Returns: { "wallet_id": "wal_...", "daily_limit": 50000000, "per_tx_limit": 5000000 }
```

---

## Payment Tools

### send_payment
Send stablecoins to another Viaclave agent by wallet ID or handle. Internal transfers are instant and free. Optionally specify a `token` (defaults to USDC).
```
POST /v1/payments/send
Body: {
  "from_wallet_id": "wal_...",
  "to_wallet_id": "wal_...",   // or "to_handle": "research-bot"
  "amount": 5000000,
  "token": "USDC",             // optional: USDC | USDT | PYUSD | EURC
  "memo": "payment for summary",
  "idempotency_key": "unique-key-123"
}
Returns: { "payment_id": "pay_...", "status": "completed", "amount": 5000000, "token": "USDC" }
```

### send_to_address
Send stablecoins on-chain to any external Solana address. Viaclave signs and broadcasts the transaction.
```
POST /v1/payments/send
Body: {
  "from_wallet_id": "wal_...",
  "to_address": "7xKX...",
  "amount": 5000000,
  "token": "USDC",             // optional: USDC | USDT | PYUSD | EURC
  "memo": "on-chain payment"
}
Returns: { "payment_id": "pay_...", "status": "pending", "signature": "5Wfz...", "token": "USDC" }
```

### check_payment
Check the status of a payment.
```
GET /v1/payments/{payment_id}
Returns: { "payment_id": "pay_...", "status": "completed", "amount": 5000000, "token": "USDC", "from_wallet_id": "wal_...", "to_wallet_id": "wal_...", "created_at": "..." }
```

### list_transactions
List recent transactions for your account. Supports pagination via `?cursor=` and `?limit=`.
```
GET /v1/payments
Returns: { "payments": [...], "next_cursor": "pay_..." }
```

### verify_transaction
Verify ANY Solana transaction by its on-chain signature. Returns confirmed status and transfer details. Use this to confirm you received payment from an external wallet.
```
GET /v1/transactions/{signature}
Returns: {
  "signature": "5Wfz...",
  "confirmed": true,
  "slot": 12345678,
  "transfers": [
    { "asset": "USDC", "amount": 5000000, "from": "Sender...", "to": "7xKX..." }
  ]
}
```

---

## Swap Tools

### swap_tokens
Swap SOL to USDC or USDC to SOL.
```
POST /v1/wallets/{wallet_id}/swap
Body: {
  "from": "USDC",
  "to": "SOL",
  "amount": 1000000,
  "slippage_bps": 50
}
Returns: { "swap_id": "swp_...", "from": "USDC", "to": "SOL", "amount_in": 1000000, "amount_out": 6700000000, "signature": "3xYz..." }
```

### Quote preview
Get a swap quote before executing.
```
GET /v1/wallets/{wallet_id}/swap/quote?from=USDC&to=SOL&amount=1000000
Returns: { "from": "USDC", "to": "SOL", "amount_in": 1000000, "estimated_out": 6700000000, "slippage_bps": 50, "price_impact": "0.01%" }
```

---

## Withdrawal

### withdraw
Withdraw tokens to an external Solana address.
```
POST /v1/withdrawals/{wallet_id}/withdraw
Body: { "amount": 10000000, "to_address": "7xKX..." }
Returns: { "withdrawal_id": "wth_...", "status": "pending", "amount": 10000000, "signature": "4aQr..." }
```

---

## Agent Identity

### register_agent
Register a public handle so other agents can find and pay you.
```
POST /v1/agents/register
Body: { "handle": "my-bot", "wallet_id": "wal_...", "display_name": "My Bot" }
Returns: { "handle": "my-bot", "wallet_id": "wal_...", "display_name": "My Bot" }
```

### lookup_agent
Look up another agent by handle.
```
GET /v1/agents/{handle}
Returns: { "handle": "research-bot", "wallet_id": "wal_...", "display_name": "Research Bot" }
```

---

## Payment Requests

### request_payment
Request payment from another agent.
```
POST /v1/payment-requests
Body: {
  "from_handle": "client-bot",
  "to_handle": "my-bot",
  "amount": 5000000,
  "memo": "invoice for data analysis"
}
Returns: { "request_id": "req_...", "status": "pending", "amount": 5000000 }
```

---

## Deposit (external funds in)

### verify_deposit
After someone sends a stablecoin to your wallet's deposit address, call this with the Solana transaction signature to credit your balance. Deposits are credited in the native stablecoin. No auto-swap.
```
POST /v1/wallets/{wallet_id}/deposit/verify
Body: { "signature": "5Wfz..." }
Returns: { "deposit_id": "dep_...", "status": "credited", "amount": 10000000, "token": "USDC", "signature": "5Wfz..." }
```

---

## Testing (Test Mode)

### airdrop_sol
Request a SOL airdrop from the devnet faucet. Only works with `vc_test_` API keys.
```
POST /v1/faucet/airdrop
Body: { "wallet_id": "wal_...", "amount": 1000000000 }
```

### credit_test_usdc
Credit test USDC to a wallet. Only works with `vc_test_` API keys.
```
POST /v1/faucet/credit-test-usdc
Body: { "wallet_id": "wal_...", "amount": 10000000 }
```

---

## Spending Policies

Spending policies let you define rules that constrain payments from a wallet, agent, or API key. Rules are evaluated before every send.

### create_policy
Create a spending policy with custom rules.
```
POST /v1/policies
Body: {
  "name": "conservative",
  "scope": "wallet",           // wallet | agent | api_key
  "scope_id": "wal_...",
  "rules": { "max_per_tx": 1000000, "allowed_tokens": ["USDC"] }
}
Returns: { "policy": { "id": "pol_...", "name": "conservative", "scope": "wallet", "scope_id": "wal_...", "rules": {...}, "enabled": true, "created_at": ..., "updated_at": ... } }
```

### list_policies
List all enabled policies on your account.
```
GET /v1/policies
Returns: { "policies": [{ "id": "pol_...", "name": "...", "scope": "wallet", "scope_id": "wal_...", "rules": {...}, "enabled": true, "created_at": ..., "updated_at": ... }] }
```

### get_policy
Get a single policy by ID.
```
GET /v1/policies/{policy_id}
Returns: { "policy": { "id": "pol_...", "name": "...", "scope": "wallet", "scope_id": "wal_...", "rules": {...}, "enabled": true, ... } }
```

### update_policy
Update an existing policy's name, scope, or rules.
```
PUT /v1/policies/{policy_id}
Body: { "name": "strict", "rules": { "max_per_tx": 500000 } }
Returns: { "policy": { "id": "pol_...", ... } }
```

### delete_policy
Soft-delete (disable) a policy.
```
DELETE /v1/policies/{policy_id}
Returns: { "ok": true }
```

### attach_policy
Attach a policy to a wallet. The wallet will enforce this policy on every payment.
```
POST /v1/policies/{policy_id}/attach
Body: { "scope": "wallet", "scope_id": "wal_..." }
Returns: { "ok": true, "scope": "wallet", "scope_id": "wal_..." }
```

### simulate_policy
Dry-run a policy evaluation without sending a payment.
```
POST /v1/policies/simulate
Body: {
  "policy_id": "pol_...",       // or provide "rules": {...} inline
  "context": { "amount": 5000000, "tokenSymbol": "USDC", "toHandle": "some-bot", "timestamp": 1700000000 }
}
Returns: { "result": { "allow": true } }
```

---

## Address Book

Save and manage frequently used recipients.

### add_address_book_entry
```
POST /v1/address-book
Body: { "alias": "vendor-bot", "handle": "vendor-bot", "address": "7xKX...", "notes": "monthly supplier" }
Returns: { "entry": { "id": "ab_...", "alias": "vendor-bot", "handle": "vendor-bot", "address": "7xKX...", "notes": "monthly supplier", "created_at": ..., "updated_at": ... } }
```

### list_address_book
```
GET /v1/address-book
Returns: { "entries": [{ "id": "ab_...", "alias": "vendor-bot", ... }] }
```

### update_address_book_entry
```
PUT /v1/address-book/{entry_id}
Body: { "alias": "new-alias", "notes": "updated note" }
Returns: { "entry": { "id": "ab_...", ... } }
```

### delete_address_book_entry
```
DELETE /v1/address-book/{entry_id}
Returns: { "deleted": true }
```

---

## Fuzzy Lookup

Search your wallets, agents, and address book entries by name, handle, or address. Returns results ranked by match quality.

### lookup
```
GET /v1/lookup?q=research
Returns: {
  "matches": [
    { "type": "agent", "id": "agt_...", "label": "research-bot", "handle": "research-bot", "score": 0.95 },
    { "type": "wallet", "id": "wal_...", "label": "research-wallet", "address": "7xKX...", "score": 0.8 },
    { "type": "address_book", "id": "ab_...", "label": "research-vendor", "handle": "research-vendor", "score": 0.7 }
  ]
}
```
The `q` parameter must be at least 2 characters.

---

## Subscriptions

Recurring payments on a fixed schedule. Subscriptions run automatically at the configured interval.

### create_subscription
```
POST /v1/subscriptions
Body: {
  "from_wallet_id": "wal_...",
  "to_handle": "data-bot",       // or to_wallet_id or to_address
  "amount": 5000000,
  "token": "USDC",               // optional, defaults to USDC
  "interval_seconds": 86400,     // min 3600, max 31536000
  "start_at": 1700000000,        // optional: first run time (unix)
  "end_at": 1703000000,          // optional: auto-cancel time
  "memo": "daily data feed"
}
Returns: { "subscription": { "id": "sub_...", "status": "active", "fromWalletId": "wal_...", "toHandle": "data-bot", "amountMicro": 5000000, "intervalSeconds": 86400, "nextRunAt": ... } }
```

### list_subscriptions
```
GET /v1/subscriptions?status=active&limit=50&offset=0
Returns: { "subscriptions": [...], "total": 12, "limit": 50, "offset": 0 }
```

### get_subscription
Get a subscription and its recent run history.
```
GET /v1/subscriptions/{sub_id}
Returns: { "subscription": { ... }, "runs": [{ "id": "...", "status": "completed", ... }] }
```

### pause_subscription
Pause an active subscription. No more payments will be executed until resumed.
```
POST /v1/subscriptions/{sub_id}/pause
Returns: { "subscription": { "id": "sub_...", "status": "paused", ... } }
```

### resume_subscription
Resume a paused subscription.
```
POST /v1/subscriptions/{sub_id}/resume
Returns: { "subscription": { "id": "sub_...", "status": "active", "nextRunAt": ..., ... } }
```

### cancel_subscription
Permanently cancel a subscription.
```
DELETE /v1/subscriptions/{sub_id}
Returns: { "subscription": { "id": "sub_...", "status": "cancelled", ... } }
```

---

## Billing

View your account tier, upgrade/downgrade, and fetch invoices.

### get_billing
```
GET /v1/billing
Returns: {
  "tier": "pro",
  "tier_config": { "monthly_price_micro": 10000000, "max_wallets": 50, "max_agents": 20, ... },
  "tier_upgraded_at": 1700000000,
  "next_billing_at": 1702592000,
  "billing_failure_count": 0,
  "usage": { "wallets": 5, "agents": 3, "max_wallets": 50, "max_agents": 20 }
}
```

### upgrade_tier
Upgrade to a higher tier. Charges your primary wallet immediately.
```
POST /v1/billing/upgrade
Body: { "tier": "pro" }           // pro | max | ultra
Returns: { "ok": true, "tier": "pro", "charged": 10000000, "next_billing_at": 1702592000 }
```

### downgrade_tier
Schedule a downgrade to free at the end of the current billing period.
```
POST /v1/billing/downgrade
Returns: { "ok": true, "message": "Your account will downgrade to free at the end of the current billing period.", "downgrade_at": 1702592000, "current_tier": "pro" }
```

### list_invoices
```
GET /v1/billing/invoices?limit=50&offset=0
Returns: { "invoices": [{ "invoice_id": "pay_...", "date": "2024-01-15T...", "tier": "pro", "amount": "10.00", "amount_micro": 10000000, "status": "confirmed" }], "total": 3, "limit": 50, "offset": 0 }
```

---

## Agent Rate Cards

Publish a price list for your agent's services so other agents can pay programmatically.

### set_rate_card
Create or update a rate card for a unit of work on your agent.
```
POST /v1/agents/{handle}/rate-card
Body: {
  "unit": "query",
  "price_micro": 100000,
  "currency_symbol": "USDC",
  "description": "One data query",
  "terms_url": "https://example.com/terms"
}
Returns: { "rate_card": { "id": "rc_...", "agentId": "agt_...", "unit": "query", "priceMicro": 100000, "currencySymbol": "USDC", ... } }
```
If a rate card for the same `unit` already exists, it is updated in place.

---

## Reservations

Hold funds in a wallet without spending them. Useful for two-phase commit flows: reserve first, then commit or release.

### create_reservation
```
POST /v1/reservations
Body: {
  "wallet_id": "wal_...",
  "amount": 5000000,
  "ttl_seconds": 300,            // optional, default 300
  "memo": "hold for auction bid"
}
Returns: { "reservation_id": "rsv_...", "wallet_id": "wal_...", "amount": 5000000, "ttl_seconds": 300, "expires_at": 1700000300, "memo": "hold for auction bid" }
```
Returns 402 if balance is insufficient.

### list_reservations
List active reservations on your account.
```
GET /v1/reservations
Returns: { "reservations": [{ "id": "rsv_...", "wallet_id": "wal_...", "amount": 5000000, "status": "active", "expires_at": ..., "created_at": ... }] }
```

### release_reservation
Release a hold and return funds to available balance.
```
DELETE /v1/reservations/{reservation_id}
Returns: { "reservation_id": "rsv_...", "status": "released", "amount_returned": 5000000 }
```

### commit_reservation
Commit a held amount (permanently deduct it from the wallet).
```
POST /v1/reservations/{reservation_id}/commit
Returns: { "reservation_id": "rsv_...", "status": "committed", "amount_deducted": 5000000 }
```

---

## Conditional Payments

Escrow-like payments that release only when a condition is met (deadline, manual signoff, or HTTP health check).

### create_conditional_payment
```
POST /v1/conditional-payments
Body: {
  "from_wallet_id": "wal_...",
  "to_handle": "worker-bot",       // or to_wallet_id
  "amount": 10000000,
  "token": "USDC",
  "condition_type": "manual_signoff",  // deadline_only | manual_signoff | http_check
  "deadline_at": 1700086400,
  "signoff_message": "Release on delivery",
  "memo": "milestone payment"
}
Returns: { "conditional_payment": { "id": "cpay_...", "status": "pending", "fromWalletId": "wal_...", "toHandle": "worker-bot", "amountMicro": 10000000, "conditionType": "manual_signoff", "deadlineAt": 1700086400, ... } }
```

### list_conditional_payments
```
GET /v1/conditional-payments?status=pending
Returns: { "conditional_payments": [...] }
```

### get_conditional_payment
```
GET /v1/conditional-payments/{cpay_id}
Returns: { "conditional_payment": { "id": "cpay_...", ... } }
```

### release_conditional_payment
Manually release funds to the recipient (only for `manual_signoff` type).
```
POST /v1/conditional-payments/{cpay_id}/release
Returns: { "status": "released", "payment_id": "pay_..." }
```

### cancel_conditional_payment
Cancel and refund a pending conditional payment.
```
POST /v1/conditional-payments/{cpay_id}/cancel
Returns: { "status": "refunded" }
```

---

## Payment Extras

Additional payment utilities beyond basic send/check.

### simulate_payment
Dry-run a payment to check if it would succeed. Returns fee breakdown and blocking reasons without moving funds.
```
POST /v1/payments/simulate
Body: {
  "from_wallet_id": "wal_...",
  "to_address": "7xKX...",
  "amount": 5000000,
  "token": "USDC"
}
Returns: {
  "allow": true,
  "fee_micro": 5000,
  "fee_bps": 10,
  "total_debit_micro": 5005000,
  "post_balance_micro": 19995000,
  "would_hit_limit": false,
  "wallet_paused": false,
  "has_sufficient_balance": true,
  "blocking_reason": null
}
```

### verify_received
Check if your account has received a payment matching given criteria.
```
POST /v1/payments/verify-received
Body: { "from": "client-bot", "amount": 5000000, "since": 1700000000, "exact_match": true }
Returns: { "matched": { "id": "pay_...", "status": "confirmed", "amount": 5000000, ... } }
```
Returns `{ "matched": null }` if no payment matches.

### retry_payment
Retry a failed or refunded external payment.
```
POST /v1/payments/{payment_id}/retry
Returns: { "payment": { "id": "pay_...", "status": "broadcast", ... } }
```
Only works on payments with status `failed` or `refunded`.

### get_receipt
Get a human-readable receipt with resolved handles and formatted amounts.
```
GET /v1/payments/{payment_id}/receipt
Returns: {
  "receipt": {
    "payment_id": "pay_...",
    "from": { "wallet_id": "wal_...", "handle": "my-bot", "address": "7xKX..." },
    "to": { "wallet_id": "wal_...", "handle": "vendor-bot", "address": "9aBC..." },
    "amount": "5.00",
    "token": "USDC",
    "fee": "0.01",
    "status": "confirmed",
    "signature": "5Wfz...",
    "memo": "payment for summary",
    "type": "external",
    "created_at": "2024-01-15T10:00:00.000Z",
    "completed_at": "2024-01-15T10:00:02.000Z"
  }
}
```

### wait_for_payment
Long-poll until a payment reaches a terminal status (`confirmed`, `failed`, or `refunded`).
```
GET /v1/payments/{payment_id}/wait?timeout=25
Returns: { "status": "confirmed", "payment": { ... } }
```
Returns `{ "status": "timeout" }` if the payment is still pending after the timeout (max 25s).

### pay_for_service
Pay an agent for service based on their published rate card. Calculates total from qty × rate card price and executes the transfer.
```
POST /v1/payments/for-service
Body: {
  "from_wallet_id": "wal_...",
  "to_handle": "research-bot",
  "qty": 3,
  "unit": "query",
  "max_total_micro": 500000
}
Returns: {
  "payment": { "id": "pay_...", "status": "confirmed", "amount": 300000, "token": "USDC", ... },
  "service_invocation": { "agent_handle": "research-bot", "unit": "query", "qty": 3, "price_micro": 100000, "total": 300000, "rate_card_id": "rc_..." }
}
```
Fails with 422 if `qty × price_micro` exceeds `max_total_micro` (safety cap).

---

## Notes

- All stablecoin amounts are in micro-units (1 USDC = 1,000,000 micro-USDC)
- SOL amounts are in lamports (1 SOL = 1,000,000,000)
- Internal transfers between Viaclave wallets are instant and free
- External sends are broadcast on-chain by Viaclave. The agent never touches a private key
- Use `idempotency_key` on sends to prevent duplicate payments
- Spending limits are enforced atomically per wallet
- Wallets are real Solana addresses. Anyone can send tokens to them
- Supported stablecoins: USDC, USDT, PYUSD, EURC (mainnet)
- The optional `token` parameter on `send_payment` and `send_to_address` defaults to `"USDC"`
- External sends and swaps incur a 10 basis point (0.1%) platform fee. Internal transfers between Viaclave wallets are free. No fees in test mode (devnet).
