# Sponge Wallet

> Crypto wallet, token swaps, cross-chain bridges, and access to paid external services (search, image gen, web scraping, AI, and more) via x402 payments.

- Skill: `paysponge/sponge-wallet` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add paysponge/sponge-wallet`
- Raw SKILL.md: https://api.skillmd.com/api/skills/paysponge/sponge-wallet/raw
- Safety review: PASS (external: skill-scanner PASS, skillspector FAIL)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: paysponge (https://skillmd.com/u/paysponge)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/paysponge/sponge-wallet

---


```
SPONGE WALLET API QUICK REFERENCE v0.2.2
Base:   https://api.wallet.paysponge.com
Auth:   Authorization: Bearer <SPONGE_API_KEY>
Ver:    Sponge-Version: 0.2.2  (REQUIRED on every request)
Docs:   This file is canonical (skills guide + params)

Capabilities: wallet + swaps (Solana/Base/Ethereum/Polygon/Arbitrum/Tempo) + bridges + payment links + paid external services (x402 + MPP) + trading + shopping + checkout + banking + Sponge Card + onramp

Paid services (search, image gen, scraping, AI, data, etc.):
  GET  /api/discover                     -> Step 1: find services by query/category
  GET  /api/discover/:serviceId          -> Step 2: get endpoints, params, pricing (REQUIRED before fetch)
  POST /api/paid/fetch                   -> Step 3: call service endpoint (auto-selects x402 USDC or MPP on Tempo)
  POST /api/x402/fetch                   -> Step 3 alt: call service endpoint (x402 USDC only)
  POST /api/mpp/fetch                    -> Step 3 alt: call service endpoint (MPP on Tempo; asset negotiated per endpoint, typically USDC.e)
  POST /api/siwe/generate                -> optional SIWE auth for endpoints that require EIP-4361 signatures

Wallet & tokens:
  GET  /api/balances                     -> get balances (includes Polymarket USDC.e)
  POST /api/payment-links                -> create reusable x402 payment link
  GET  /api/payment-links/:paymentLinkId -> get payment link status/details
  POST /api/transfers/evm                -> EVM transfer (ETH/USDC)
  POST /api/transfers/solana             -> Solana transfer (SOL/USDC)
  POST /api/transfers/tempo              -> Tempo transfer (pathUSD/AlphaUSD/BetaUSD/ThetaUSD)
  POST /api/solana/sign                  -> Sign pre-built Solana transaction only
  POST /api/solana/sign-and-send         -> Sign and submit pre-built Solana transaction
  POST /api/transactions/swap            -> Solana swap (DFlow); also supports EVM chains
  POST /api/transactions/swap/quote      -> Solana swap quote only (DFlow)
  POST /api/transactions/swap/execute    -> Execute a previously obtained swap quote
  POST /api/transactions/base-swap       -> Base swap (0x)
  POST /api/transactions/tempo-swap      -> Tempo swap (StablecoinExchange DEX)
  POST /api/transactions/bridge          -> Bridge (Relay)
  GET  /api/solana/tokens                -> list SPL tokens
  GET  /api/solana/tokens/search         -> search Jupiter token list
  GET  /api/transactions/status/:txHash  -> transaction status
  GET  /api/transactions/history         -> transaction history
  POST /api/wallets/withdraw-to-main     -> withdraw to owner

Secrets & checkout data:
  POST /api/credit-cards                 -> store card details as a normal agent payment method
  GET  /api/credit-cards                 -> list saved card payment methods and the default method
  POST /api/agents/:id/link-payment-methods/link -> connect Link, then save a Link payment method with required shipping/contact info
  POST /api/agents/:id/link-payment-methods/credential -> generate one-time card credentials from a saved Link payment method
  GET  /api/agent-keys                   -> list stored secret metadata
  GET  /api/agent-keys/value             -> retrieve a stored non-card secret value
  DELETE /api/agent-keys                 -> delete a stored non-card secret by service
  POST /api/agent-keys                   -> store non-card service keys

Cards (stored + enrolled):
  POST /api/cards                        -> fetch the user's card (Sponge Card or stored card); auto-detects source
  POST /api/virtual-cards                -> issue a per-transaction virtual card scoped to amount and merchant
  POST /api/card-usage                   -> report outcome of a purchase attempt

Sponge Card (beta preview):
  GET  /api/sponge-card/status           -> onboarding/consent/card readiness status
  POST /api/sponge-card/onboard          -> start internal KYC verification when needed, then submit the KYC-backed Sponge Card application + consent acknowledgements
  POST /api/sponge-card/terms            -> accept terms for an existing application
  POST /api/sponge-card/create-card      -> create the virtual Sponge Card after approval
  GET  /api/sponge-card/details          -> encrypted PAN/CVC + secret_key + spending power (decrypt client-side)
  POST /api/sponge-card/fund             -> top up card collateral with USDC from your wallet
  POST /api/sponge-card/withdraw         -> withdraw card collateral back to your wallet

Onramp:
  POST /api/onramp/crypto               -> create fiat-to-crypto onramp link (Stripe/Coinbase)

Planning & proposals:
  POST /api/plans/submit                 -> submit multi-step plan
  POST /api/plans/approve                -> approve and execute plan
  POST /api/trades/propose               -> propose single swap for approval

Banking:
  POST /api/bank/onboard          -> start/resume internal KYC verification and Bridge terms onboarding
  GET  /api/bank/status           -> check KYC/onboarding status
  POST /api/bank/virtual-account  -> create/get virtual bank account (USD→USDC deposits)
  GET  /api/bank/virtual-account  -> get deposit instructions for a wallet
  GET  /api/bank/external-accounts -> list linked bank accounts
  POST /api/bank/external-accounts -> link US bank account for ACH or wire payouts
  POST /api/bank/send             -> off-ramp: send USD to linked bank via ACH or wire (USDC→USD)
  GET  /api/bank/transfers        -> list fiat transfer history

Trading & shopping:
  POST /api/polymarket                     -> Polymarket prediction market trading
  POST /api/hyperliquid                  -> Hyperliquid perps/spot trading
  POST /api/checkout             -> checkout from any online store; fire-and-forget approval by email
  GET  /api/checkout/:sessionId  -> URL checkout request status

Auth (one-time setup):
  POST /api/agents/register              -> register (no auth)
  POST /api/oauth/device/authorization   -> device login start (humans)
  POST /api/oauth/device/token           -> device token poll (agents + humans)

Rules: use register (agents), never login | store key in ~/.spongewallet/credentials.json | requests are JSON
Errors: HTTP status + JSON error message
```

# Sponge Wallet API - Agent Skills Guide

This skill is **doc-only**. There is no local CLI. It documents the public Sponge Wallet REST API.

## What you can do with Sponge

1. **Manage crypto** — check balances, transfer tokens (EVM, Solana, and Tempo), swap on Solana/Base/Ethereum/Polygon/Arbitrum/Tempo, bridge cross-chain
   - For pre-built Solana transactions returned by an external API, use `POST /api/solana/sign-and-send`.
   - Use `POST /api/solana/sign` only when you explicitly need a partially/fully signed transaction back without broadcasting.
2. **Create payment links** — generate reusable x402 payment URLs and check payment status
3. **Access paid external services** — search, image generation, web scraping, AI models, data enrichment, and more. Always follow these 3 steps:
   1. `GET /api/discover?query=...` — find a service
   2. `GET /api/discover/{serviceId}` — get its endpoints, params, and payment config **(do not skip)**
   3. `POST /api/paid/fetch` — call the endpoint (auto-selects x402 USDC or MPP on Tempo based on service). Alternatively use `POST /api/x402/fetch` (USDC only) or `POST /api/mpp/fetch` (MPP on Tempo; asset negotiated per endpoint, typically USDC.e).
   - If the target endpoint also requires SIWE auth, call `POST /api/siwe/generate` first and include its output in the fetch headers.
4. **Banking** — KYC onboarding, virtual bank accounts (receive USD as USDC), link bank accounts, send USD to bank (off-ramp USDC)
5. **Trade on prediction markets and perps** — Polymarket, Hyperliquid
6. **Shop from online stores** — use `POST /api/checkout` for any merchant URL
7. **Store a credit card for checkout** — use `POST /api/credit-cards`
8. **Connect Link for checkout** — use `POST /api/agents/{agentId}/link-payment-methods/link`; first call can start Link login without shipping/contact info, saving the selected Link payment method requires shipping, phone, and email
9. **Use enrolled / stored cards** — fetch the user's card via `POST /api/cards` (auto-detects source), or issue a per-transaction virtual card (`POST /api/virtual-cards`)
10. **Fiat onramp** — buy crypto with card/bank payment via Stripe or Coinbase

**If a task requires an external capability you don't have** (e.g., generating images, searching the web, scraping a URL, looking up a person's email), use the 3-step discover flow above. There is likely a paid service available for it.

## Why the steps matter (short rationale)

- **Register vs login**: agents create a new managed wallet tied to a human owner. That owner must explicitly claim the agent. Login is only for humans who already have an account.
- **Claim URL**: ensures the human owner explicitly links the agent to their account and controls allowlists/funding.
- **Credential persistence**: the `apiKey` is returned once (immediately for agent-first mode, or after device approval for standard mode). If you lose it, you must re-register or re-authenticate.

## Standard credential storage (required)

Store the API key in **one canonical location**:

- `~/.spongewallet/credentials.json`

Recommended file contents:
```json
{
  "apiKey": "sponge_live_...",
  "claimCode": "ABCD-1234",
  "claimUrl": "https://wallet.paysponge.com/device?code=ABCD-1234"
}
```

Optional environment export (runtime convenience):
```bash
export SPONGE_API_KEY="$(jq -r .apiKey ~/.spongewallet/credentials.json)"
```

## Base URL & Auth

- Base URL: `https://api.wallet.paysponge.com`
- Auth header: `Authorization: Bearer <SPONGE_API_KEY>`
- Content-Type: `application/json`

Quick env setup:

```bash
export SPONGE_API_URL="https://api.wallet.paysponge.com"
export SPONGE_API_KEY="$(jq -r .apiKey ~/.spongewallet/credentials.json)"
```

## SIWE Signature

Use `POST /api/siwe/generate` when an external API requires SIWE (EIP-4361) authentication.

Required args:
- `domain`
- `uri`

Optional args:
- `statement`, `nonce`, `chain_id`, `expiration_time`, `not_before`, `request_id`, `resources`

Returns:
- `message`, `signature`, `address`, `chainId`, `nonce`, `issuedAt`, `expirationTime`, `base64SiweMessage`

Workflow:
1. Call `POST /api/siwe/generate` with the exact target `domain` and `uri`.
2. Build the `Authorization` header using returned `signature` and `base64SiweMessage` in the target API's expected format.
3. Pass that header to `/api/paid/fetch` or `/api/x402/fetch` when calling the protected endpoint.

## CRITICAL: AI Agents Must Use `register`, NOT `login`

### 1) Agent Registration (AI agents only)

There are two modes:
- **Standard auth flow** (default): human must approve before the API key is returned.
- **Agent-first** (`agentFirst: true`): agent receives the API key immediately, and the human can claim later.

**Step 1 — Start registration (agent-first recommended)**
```bash
curl -sS -X POST "$SPONGE_API_URL/api/agents/register" \
  -H "Sponge-Version: 0.2.2" \
  -H "Content-Type: application/json" \
  -d '{
    "name":"YourAgentName",
    "agentFirst": true
  }'
```

Response includes:
- `verificationUriComplete` (claim URL for the human owner)
- `claimCode`, `deviceCode`, `expiresIn`, `interval`, `claimText`
- `apiKey` (returned immediately in agent-first mode)

Store `apiKey`, `claimCode`, and `verificationUriComplete` (as `claimUrl`) in `~/.spongewallet/credentials.json` so a human can claim later if context resets.

**Step 2 — Send the claim URL to the human owner**
They log in, optionally post the tweet text, and approve the agent.

Claim link format:
- `verificationUriComplete` (example path: `/device?code=ABCD-1234`)
- The base URL is the frontend (prod or local), so just pass the full `verificationUriComplete` to the human.

**Step 3 — Poll for completion (standard auth flow only)**
```bash
while true; do
  response="$(curl -sS -X POST "$SPONGE_API_URL/api/oauth/device/token" \
    -H "Sponge-Version: 0.2.2" \
    -H "Content-Type: application/json" \
    -d '{
      "grantType":"urn:ietf:params:oauth:grant-type:device_code",
      "deviceCode":"<deviceCode>",
      "clientId":"spongewallet-skill"
    }')"

  if printf '%s' "$response" | grep -q '"apiKey"'; then
    printf '%s\n' "$response"
    break
  fi

  if ! printf '%s' "$response" | grep -q '"authorization_pending"'; then
    printf '%s\n' "$response"
    exit 1
  fi

  sleep 5
done
```

Keep polling while the human approves the current claim code. Approval in a browser
does not write credentials by itself; the still-running poll must receive the
success response. Do not ask the user to come back and say "done"; this poll
loop is the completion signal. If polling times out or is interrupted, request a
fresh code.

On success, the response includes `apiKey`. Save it to `~/.spongewallet/credentials.json` and use it as `SPONGE_API_KEY`.

Note: In **agent-first mode**, you already have the `apiKey` from Step 1. The auth token will remain pending until the human claims.

### 2) Human Login (existing accounts only)

**Phase 1 — Request device code**
```bash
curl -sS -X POST "$SPONGE_API_URL/api/oauth/device/authorization" \
  -H "Sponge-Version: 0.2.2" \
  -H "Content-Type: application/json" \
  -d '{
    "clientId":"spongewallet-skill"
  }'
```

**Phase 2 — Poll for token** (same endpoint as agents)
```bash
while true; do
  response="$(curl -sS -X POST "$SPONGE_API_URL/api/oauth/device/token" \
    -H "Sponge-Version: 0.2.2" \
    -H "Content-Type: application/json" \
    -d '{
      "grantType":"urn:ietf:params:oauth:grant-type:device_code",
      "deviceCode":"<deviceCode>",
      "clientId":"spongewallet-skill"
    }')"

  if printf '%s' "$response" | grep -q '"apiKey"'; then
    printf '%s\n' "$response"
    break
  fi

  if ! printf '%s' "$response" | grep -q '"authorization_pending"'; then
    printf '%s\n' "$response"
    exit 1
  fi

  sleep 5
done
```

Keep Phase 2 alive until it returns success or a terminal error. Device approval
can happen in any browser, but credentials are only available to the agent after
the polling request succeeds and the returned `apiKey` is written to
`~/.spongewallet/credentials.json`. Do not ask the user to come back and say
"done"; this poll loop is the completion signal. If the poller was killed, timed
out, or lost its pending state, start again from Phase 1 and use the new code.

## Tool Call Pattern

Use the public REST endpoints documented in this file. Internal-only tools are intentionally omitted.

**Common headers (include on EVERY request)**
```bash
-H "Authorization: Bearer $SPONGE_API_KEY" \
-H "Sponge-Version: 0.2.2" \
-H "Content-Type: application/json" \
-H "Accept: application/json"
```

**Agent ID note:** `agentId` is optional for API key auth. It is only required when using a user session (e.g., Privy-based auth) or when explicitly operating on a different agent.

### API Endpoints Reference

| Endpoint | Method | Path | Params/Body |
|----------|--------|------|-------------|
| Get balances | GET | `/api/balances` | Query: `chain`, `allowedChains`, `onlyUsdc` |
| Create payment link | POST | `/api/payment-links` | Body: `amount`, optional `description`, `max_uses`, `expires_in_minutes`, `callback_url`, `livemode` |
| Get payment link status | GET | `/api/payment-links/{paymentLinkId}` | Query: `agentId` (optional) |
| List SPL tokens | GET | `/api/solana/tokens` | Query: `chain` |
| Search Solana tokens | GET | `/api/solana/tokens/search` | Query: `query`, `limit` |
| Tempo transfer | POST | `/api/transfers/tempo` | Body: `to`, `amount`, `token` (pathUSD/AlphaUSD/BetaUSD/ThetaUSD), optional `data` (hex calldata for raw contract calls) |
| EVM transfer | POST | `/api/transfers/evm` | Body: `chain`, `to`, `amount`, `currency` |
| Solana transfer | POST | `/api/transfers/solana` | Body: `chain`, `to`, `amount`, `currency` |
| Solana swap | POST | `/api/transactions/swap` | Body: `chain`, `inputToken`, `outputToken`, `amount`, `slippageBps` |
| Solana swap quote | POST | `/api/transactions/swap/quote` | Body: `chain`, `inputToken`, `outputToken`, `amount`, `slippageBps` |
| Solana swap execute | POST | `/api/transactions/swap/execute` | Body: `quoteId` |
| Base swap | POST | `/api/transactions/base-swap` | Body: `chain`, `inputToken`, `outputToken`, `amount`, `slippageBps` |
| Tempo swap | POST | `/api/transactions/tempo-swap` | Body: `chain` (tempo), `inputToken`, `outputToken`, `amount`, `slippageBps` |
| Bridge | POST | `/api/transactions/bridge` | Body: `sourceChain`, `destinationChain`, `token`, `amount`, `destinationToken`, `recipientAddress` |
| Transaction status | GET | `/api/transactions/status/{txHash}` | Query: `chain` |
| Transaction history | GET | `/api/transactions/history` | Query: `limit`, `chain` |
| Store credit card | POST | `/api/credit-cards` | Body (snake_case): `card_number`, `expiration` OR (`expiry_month` + `expiry_year`), `cvc`, `cardholder_name`, `email`, `billing_address`, `shipping_address` (**phone required**), optional `label`, `metadata`, `agentId`. Creates a saved card payment method for checkout |
| Add Link payment method | POST | `/api/agents/{agentId}/link-payment-methods/link` | First call can use `{}` to start Link login and may return `link_connection_required` with `verificationUrl`. Saving requires `email`, top-level `phone`, and `shipping` (`name`, `line1`, `city`, `state`, `postalCode`, `country`). Optional: `linkPaymentMethodId`, `setAsDefault`, `clientName`, `billing` |
| Create Link payment credential | POST | `/api/agents/{agentId}/link-payment-methods/credential` | Generate one-time card credentials from a saved Link payment method. First call body: `amount`, `merchantName`, `merchantUrl`; optional: `linkPaymentMethodId`, `currency`, `context`. If approval is required, call again with `spendRequestId` after approval |
| **Get card** | POST | `/api/cards` | Body: optional `card_type`, `payment_method_id`, `amount`, `currency`, `merchant_name`, `merchant_url`, `agentId`. Auto-detects source; returns `selection_required` when both sources are enrolled |
| Issue virtual card | POST | `/api/virtual-cards` | Body: `amount`, `merchant_name`, `merchant_url`; optional: `currency`, `merchant_country_code`, `description`, `products`, `shipping_address`, `enrollment_id`, `agentId` |
| Report card usage | POST | `/api/card-usage` | Body: `payment_method_id`, `status` (success/failed/cancelled); optional: `merchant_name`, `merchant_domain`, `amount`, `currency`, `failure_reason`, `agentId` |
| Store service key (non-card) | POST | `/api/agent-keys` | Body: `service`, `key`, optional `label`, `metadata` |
| List stored keys | GET | `/api/agent-keys` | Query: `agentId` (optional) |
| Get stored key value | GET | `/api/agent-keys/value` | Query: `service` |
| Delete stored key | DELETE | `/api/agent-keys` | Query: `service`, optional `agentId` |
| **Step 1: Discover services** | GET | `/api/discover` | Query: `type`, `limit`, `offset`, `query`, `category` |
| **Step 2: Get service details** | GET | `/api/discover/{serviceId}` | — |
| **Step 3: Paid fetch (unified)** | POST | `/api/paid/fetch` | Body: `url`, `method`, `headers`, `body`, `chain` (preferred wallet chain hint) |
| **Step 3 alt: x402 fetch** | POST | `/api/x402/fetch` | Body: `url`, `method`, `headers`, `body`, `preferred_chain` |
| **Step 3 alt: MPP fetch** | POST | `/api/mpp/fetch` | Body: `url`, `method`, `headers`, `body`, `chain` |
| SIWE signature | POST | `/api/siwe/generate` | Body: `domain`, `uri`; optional: `statement`, `nonce`, `chain_id`, `expiration_time`, `not_before`, `request_id`, `resources` |
| **Sponge Card status** | GET | `/api/sponge-card/status` | Query: optional `refresh`, `agentId` |
| **Onboard Sponge Card** | POST | `/api/sponge-card/onboard` | Body: optional `redirect_uri`, then `occupation` and consent booleans after KYC approval, optional `agentId`. Returns a KYC verification link first when needed; does not accept raw SSN/date-of-birth fields |
| **Accept Sponge Card terms** | POST | `/api/sponge-card/terms` | Body: consent booleans, optional `agentId` |
| **Create Sponge Card** | POST | `/api/sponge-card/create-card` | Body: `billing`, `email`, `phone`, optional `shipping`, `agentId` |
| **Sponge Card details** | GET | `/api/sponge-card/details` | Query: `agentId` (optional) |
| **Fund Sponge Card** | POST | `/api/sponge-card/fund` | Body: `amount`, optional `chain`, `agentId` |
| **Withdraw from Sponge Card** | POST | `/api/sponge-card/withdraw` | Body: `amount`, optional `chain`, `agentId` |
| Banking onboard | POST | `/api/bank/onboard` | Body: optional `wallet_id`, `redirect_uri`, `customer_type`, `signed_agreement_id`, `agentId`. Returns a KYC verification link first when needed, then a Bridge terms URL |
| Banking status | GET | `/api/bank/status` | Query: optional `agentId` |
| Create virtual bank account | POST | `/api/bank/virtual-account` | Body: `wallet_id`, optional `agentId` |
| Get virtual bank account | GET | `/api/bank/virtual-account` | Query: optional `wallet_id`, `agentId` |
| List linked bank accounts | GET | `/api/bank/external-accounts` | Query: optional `agentId` |
| Link bank account | POST | `/api/bank/external-accounts` | Body (snake_case): `bank_name`, `account_owner_name`, `routing_number`, `account_number`, `checking_or_savings`, `street_line_1`, optional `street_line_2`, `city`, `state`, `postal_code`, optional `agentId` |
| Send USD to bank | POST | `/api/bank/send` | Body: `wallet_id`, `external_account_id`, `amount`, optional `payment_rail` (ach/wire), `agentId` |
| List bank transfers | GET | `/api/bank/transfers` | Query: optional `transfer_id`, `agentId` |
| Crypto onramp | POST | `/api/onramp/crypto` | Body: `wallet_address`; optional: `provider` (auto/stripe/coinbase), `chain` (base/solana/polygon), `fiat_amount`, `fiat_currency` |
| Hyperliquid | POST | `/api/hyperliquid` | Body: `action`, + action-specific params (see below) |
| URL checkout request | POST | `/api/checkout` | Body: `productUrl`; optional: `estimatedAmount`, `email`, `dryRun`, `productOptions`, `agentId` |
| URL checkout status | GET | `/api/checkout/{sessionId}` | Query: `agentId` (optional) |
| Submit plan | POST | `/api/plans/submit` | Body: `title`, `reasoning`, `steps` (see Planning section) |
| Approve plan | POST | `/api/plans/approve` | Body: `plan_id` |
| Propose trade | POST | `/api/trades/propose` | Body: `input_token`, `output_token`, `amount`, `reason` |
Note: most request bodies use camelCase (e.g., `inputToken`, `slippageBps`). Card secret endpoints and Sponge Card API endpoints use snake_case fields (e.g., `card_number`, `cardholder_name`, `first_name`, `postal_code`).

> **CRITICAL — Paid Services require ALL 3 steps in order:**
> 1. `GET /api/discover` — search for a service by query or category
> 2. `GET /api/discover/{serviceId}` — get the service's endpoints, parameters, pricing, and payment config (**DO NOT SKIP THIS STEP**)
> 3. `POST /api/x402/fetch` — call the endpoint using the URL from step 2
>
> You MUST call step 2 before step 3. Step 2 returns the `paymentsProtocolConfig` (with baseUrls) and the endpoint parameters/instructions needed to construct the fetch request. Using a direct API URL will fail with auth errors.

### Planning (Multi-Step Actions)

Use `submit_plan` whenever you need to do 2+ related actions together (e.g., swap then bridge, buy multiple tokens, rebalance a portfolio). This groups everything into a single proposal the user can review and approve.

**Workflow:**
1. Call `submit_plan` with a title, reasoning, and ordered list of steps
2. Present the plan to the user
3. When the user confirms ("go ahead", "do it", "execute"), call `approve_plan` with the `plan_id`
4. Steps execute sequentially and automatically. If any step fails, the plan pauses for retry or reject.

**Step types:**
- `swap` — requires: `input_token`, `output_token`, `amount`, `reason`; optional: `chain` (defaults to solana; use `base` or `tempo` for other DEXs)
- `transfer` — requires: `chain`, `to`, `amount`, `currency`, `reason`
- `bridge` — requires: `source_chain`, `destination_chain`, `token`, `amount`, `reason`; optional: `destination_token`

**Rules:**
- Do NOT execute steps individually with other tools after submitting a plan — always use `approve_plan`
- Plans can have 1–20 steps
- The user can skip individual steps on the dashboard before approving
- Use `propose_trade` only for a single swap that needs explicit approval; for multi-step flows, always use `submit_plan`

### Polymarket Actions

The `polymarket` endpoint is a unified tool for Polymarket trading, funding, withdrawals, and account status. Pass `action` plus action-specific parameters:

| Action | Description | Required Params | Optional Params |
|--------|-------------|-----------------|-----------------|
| `enable` | Provision/link the Polymarket Safe without placing a trade | — | — |
| `signup` | Alias for `enable` | — | — |
| `status` | Check whether Polymarket is linked and view balances | — | — |
| `search_markets` | Search Polymarket markets | `query` | `limit` |
| `get_market` | Fetch market metadata | `market_slug` or `token_id` | — |
| `get_market_price` | Fetch current market price | `market_slug` or `token_id` | — |
| `positions` | View current positions | — | — |
| `orders` | View open orders | — | — |
| `get_order` | Fetch an order by ID | `order_id` | — |
| `balance_allowance` | View CLOB balance + allowance | — | — |
| `refresh_balance_allowance` | Refresh CLOB balance + allowance | — | — |
| `order` | Place a limit or market order | `outcome`, `side`, `size`, `market_slug` or `token_id` | `type`, `price`, `order_type` |
| `cancel` | Cancel an order | `order_id` | — |
| `set_allowances` | Approve contracts for trading | — | — |
| `deposit` | Make Safe-held USDC.e available to the exchange | — | — |
| `deposit_from_wallet` | Move Polygon wallet USDC.e into the Safe | `amount` | — |
| `withdraw` | Withdraw pUSD as USDC.e to Polygon wallet | `amount` | — |
| `withdraw_native` | Withdraw Polygon-native USDC to Polygon wallet | `amount` | — |
| `redeem` | Redeem settled winning positions | — | `condition_id` |

**Order params:**
- `market_slug` or `token_id`: identify which market to trade
- `outcome`: `"yes"` or `"no"`
- `side`: `"buy"` or `"sell"`
- `size`: number of shares
- `type`: `"limit"` or `"market"`; defaults to `"limit"`
- `price`: required for limit orders, decimal between `0` and `1`
- `order_type`: `"GTC"`, `"GTD"`, `"FOK"`, or `"FAK"`; market orders only support `FOK` or `FAK`

**Scopes:** Setup, trade, and funding actions (`enable`, `signup`, `order`, `cancel`, `set_allowances`, `deposit`, `deposit_from_wallet`, `withdraw`, `withdraw_native`, `redeem`) require `polymarket:trade`. Read actions require `wallet:read`.

**Provisioning:** Trading and funding calls auto-link Polymarket on first use if the agent has a Polygon/EVM wallet. To sign up explicitly through the tool, call `action: "enable"` (or `action: "signup"`). You can also enable from the dashboard with `POST /api/agents/:id/polymarket/enable`.

**Funding:** For cross-chain funding, use the bridge tool with `destination_chain: "polymarket"` so the route lands as USDC.e in the Polymarket Safe, then use `deposit` to wrap Safe-held USDC.e into pUSD trading collateral.

**Search:** Use team names, common aliases, or exact slugs for sports markets. For NBA matchup searches, queries like `"Sixers Celtics"` should resolve aliases to current/future sports slugs such as `nba-phi-bos-YYYY-MM-DD`. If `search_markets` misses a sports matchup, search the web or Polymarket team/event pages directly, find the exact Polymarket event/market URL, then pass that slug to `get_market`, `get_market_price`, or `order`.

### Hyperliquid Actions

The `hyperliquid` endpoint is a unified tool for perps/spot trading on Hyperliquid DEX. Pass `action` plus action-specific parameters:

| Action | Description | Required Params | Optional Params |
|--------|-------------|-----------------|-----------------|
| `status` | Check account balances and equity | — | — |
| `positions` | View open perp positions (entry, PnL, liq price) | — | — |
| `orders` | View open orders | — | `symbol` |
| `fills` | Recent trade history | — | `symbol`, `since`, `limit` |
| `markets` | List available perp/spot markets | — | — |
| `ticker` | Current price for a symbol | `symbol` | — |
| `orderbook` | L2 order book | `symbol` | `limit` |
| `funding` | Current + predicted funding rates | — | `symbol` |
| `chart` | Compact Unicode price chart with candle summary | `symbol` | `interval`, `chart_style` |
| `order` | Place limit/market/stop/TP order | `symbol`, `side`, `type`, `amount` | `price`, `reduce_only`, `trigger_price`, `tp_sl`, `tif` |
| `cancel` | Cancel an order | `order_id`, `symbol` | — |
| `cancel_all` | Cancel all open orders | — | `symbol` |
| `set_leverage` | Set leverage for a symbol | `symbol`, `leverage` | — |
| `withdraw` | Withdraw USDC from Hyperliquid | `amount`, `destination` | — |
| `transfer` | Move USDC between spot and perps | `amount`, `to_perp` | — |

**Order params:**
- `symbol`: CCXT symbol (e.g., `"BTC/USDC:USDC"` for perps, `"PURR/USDC"` for spot)
- `side`: `"buy"` or `"sell"`
- `type`: `"limit"` or `"market"`
- `amount`: Size in base currency (e.g., `"0.001"` for BTC)
- `price`: Limit price (required for limit orders)
- `reduce_only`: Boolean, default false
- `trigger_price`: For stop-loss/take-profit orders
- `tp_sl`: `"tp"` (take-profit) or `"sl"` (stop-loss) — required if `trigger_price` set
- `tif`: `"GTC"` (default), `"IOC"`, `"PO"` (post-only)

**Scopes:** Trade actions (`order`, `cancel`, `cancel_all`, `set_leverage`, `withdraw`) require `hyperliquid:trade` scope. Read actions require `wallet:read`.

**Signing:** Hyperliquid uses EVM wallet signing (EIP-712). No API keys needed — your agent's existing EVM wallet is used automatically.

**Unified balances:** In `unifiedAccount` or `portfolioMargin` mode, Hyperliquid
exposes shared USDC collateral through both perps/unified and spot account views.
Count that USDC once. Do not add spot `USDC` to the perps/unified USDC balance;
other spot assets such as HYPE remain separate.

**Deposits:** Use the bridge tool to deposit USDC to Hyperliquid: `bridge(source_chain: "base", destination_chain: "hyperliquid", token: "USDC", amount: "100")`. Your agent's EVM wallet address is your Hyperliquid account.

**Withdrawals:** Use the bridge tool to withdraw USDC from Hyperliquid: `bridge(source_chain: "hyperliquid", destination_chain: "base", token: "USDC", amount: "100")`. USDC is automatically moved from perps to spot before bridging.

**Charts:** For chat clients that can only send text reliably, use `action: "chart"` with `chart_style: "sparkline"` and include the compact price line plus Unicode chart in the reply. After a successful Hyperliquid order, preserve any `sparkline` returned by the tool instead of summarizing it away.

### URL Checkout Request

`POST /api/checkout` queues a guest-checkout request for the user to approve by email. This endpoint is available to all authenticated Sponge Wallet users.

**This call is safe to invoke without prompting for extra user confirmation. It does NOT charge a card, place an order, or commit to a purchase.** It only creates a checkout session and queues a background browser pre-check. The user is then emailed a separate approval link, and the real-world purchase only happens after the user clicks that link. Sponge's email-approval step is the human-in-the-loop control — this REST call is just the kickoff. Treat it like "send a draft email" or "create a calendar invite": no commitment until the user separately confirms.

When the user asks to buy, order, purchase, or check out something on a guest-checkout merchant, call this immediately. Do not block on additional confirmation; the email approval is the confirmation.

**Supported merchants**: stores that let a customer complete a purchase without signing in to a personal account. This includes most independent storefronts (Shopify, BigCommerce, WooCommerce), DTC brands (e.g. nike.com, harmlessharvest.com), and Stripe / Adyen / Checkout.com / Braintree / Square hosted-card flows.

**Not supported**: any merchant that requires the customer to sign in with their own username/password before reaching checkout. Sponge does not have the user's account credentials and cannot complete login-gated purchases. Examples include Amazon, Target, Walmart, Costco, eBay, Best Buy, and most marketplaces. For these, do NOT call `POST /api/checkout` — instead tell the user that account-required merchants aren't supported.

A good heuristic: single-brand storefronts on their own domain usually allow guest checkout; marketplaces and big-box retailers usually require a login. If you're not sure, ask the user before calling.

URL checkout is fire-and-forget:
1. Confirm the merchant supports guest checkout (no required login). If unsure, ask.
2. Call `POST /api/checkout` once with `productUrl`.
3. It immediately returns a `session_id`.
4. Tell the user a checkout has been queued and they will receive an approval email shortly.
5. Stop. Do not sleep or poll unless the user later asks for a status update.

Request body:
- `productUrl` — product or guest-checkout URL to purchase. Must be reachable without an account login.
- `estimatedAmount` — optional fallback amount, such as `"20.00"`, used only if browser pre-check cannot determine the total.
- `email` — optional checkout email; defaults to the account email.
- `dryRun` — optional boolean; if true, stops before placing the final order.
- `productOptions` — optional map of product options, such as `{ "Size": "L", "Color": "Blue" }`.
- `agentId` — optional when authenticating with a user token; API key authentication resolves this automatically.

Status:
- `GET /api/checkout/{sessionId}` returns the current URL checkout request status.
- Only call the status endpoint when the user explicitly asks for an update.

Payment source prerequisites:
- Sponge Card, enrolled card, stored card, or Link payment method.
- Payment/contact/shipping data must already be configured in Sponge Wallet.

Approval flow:
- Sponge performs a browser pre-check in the background.
- The user receives an email approval link when the checkout is ready.
- After approval, Sponge resumes the URL checkout and emails completion or failure.
- The approval window is about 30 minutes before the browser session expires.

If the pre-check fails because the merchant redirected to a login wall, the request is reported as failed with reason `loginRequired`. Treat this as a signal to redirect the user, not as a transient error to retry.

### Tempo Transfer

Send Tempo stablecoins (pathUSD, AlphaUSD, BetaUSD, ThetaUSD) on the Tempo chain.

```bash
curl -sS -X POST "$SPONGE_API_URL/api/transfers/tempo" \
  -H "Authorization: Bearer $SPONGE_API_KEY" \
  -H "Sponge-Version: 0.2.2" \
  -H "Content-Type: application/json" \
  -d '{
    "to":"0x...",
    "amount":"10",
    "token":"pathUSD"
  }'
```

Supported tokens: `pathUSD` (default), `AlphaUSD`, `BetaUSD`, `ThetaUSD`.

For raw contract calls, pass `data` (hex calldata) instead of `token`/`amount`:
```bash
curl -sS -X POST "$SPONGE_API_URL/api/transfers/tempo" \
  -H "Authorization: Bearer $SPONGE_API_KEY" \
  -H "Sponge-Version: 0.2.2" \
  -H "Content-Type: application/json" \
  -d '{
    "to":"0xContractAddress...",
    "amount":"0",
    "data":"0xabcdef..."
  }'
```

### EVM Swaps (Ethereum, Polygon, Arbitrum)

The unified `POST /api/transactions/swap` REST endpoint supports EVM chains. Pass the chain name (e.g., `base`, `ethereum`, `polygon`, `arbitrum-one`) in the `chain` field.

### Swap Quote & Execute (Solana)

For Solana swaps, you can get a quote before executing:

1. **Get quote:** `POST /api/transactions/swap/quote` — returns pricing details and a `quoteId`
2. **Execute:** `POST /api/transactions/swap/execute` with `{ "quoteId": "..." }` — executes the quoted swap

Quotes expire in ~30 seconds. Use this when you want to preview pricing or get user confirmation before swapping.

### Sponge Card (beta preview)

The Sponge Card is a stablecoin-collateralized credit card available to authenticated users with the required API scopes.

Environment (dev vs production) is fixed by your API key type: `sponge_test_*` -> dev sandbox, `sponge_live_*` -> production.

The full no-UI flow is:

1. `GET /api/sponge-card/status` - check onboarding and card readiness.
2. `POST /api/sponge-card/onboard` - start internal KYC verification when needed, then submit the Sponge Card application and record consent acknowledgements after KYC approval.
3. If status says consent is missing, `POST /api/sponge-card/terms`.
4. When `ready_for_card_creation` is true, `POST /api/sponge-card/create-card`.
5. Use `/details`, `/fund`, and `/withdraw` after a card exists.

#### Check onboarding status

```bash
curl -sS "$SPONGE_API_URL/api/sponge-card/status?refresh=true" \
  -H "Authorization: Bearer $SPONGE_API_KEY" \
  -H "Sponge-Version: 0.2.2" \
  -H "Accept: application/json"
```

Returns `onboarded`, `environment`, `ready_for_card_creation`, `customer`, `completion_link_url`, `cards`, `balances`, and `message`. `balances` uses explicit unit fields such as `spending_power_cents`, `spending_power_usd`, and `spending_power_display`; for user-facing output, use the display fields. If `spending_power_cents` is `250`, that means `$2.50`, not `$250`. `completion_link_url` is retained for compatibility and should normally be `null`; use `/onboard` to receive any required KYC verification link.

#### Onboard for Sponge Card

Start Sponge Card onboarding. If internal KYC verification is missing, this endpoint can be called with only optional `redirect_uri` and returns `kyc_url` / `verification_url` for the user to complete. After KYC approval, call it again with `occupation` and consent acknowledgements to submit the Sponge Card application and record Sponge consent. Bodies use snake_case. Do not collect or send raw SSN, national ID, birth date, or address fields to this endpoint.

```bash
curl -sS -X POST "$SPONGE_API_URL/api/sponge-card/onboard" \
  -H "Authorization: Bearer $SPONGE_API_KEY" \
  -H "Sponge-Version: 0.2.2" \
  -H "Content-Type: application/json" \
  -d '{
    "occupation":"Software engineer",
    "e_sign_consent":true,
    "account_opening_privacy_notice":true,
    "sponge_card_terms":true,
    "information_certification":true,
    "unauthorized_solicitation_acknowledgement":true
  }'
```

- `occupation` is required.
- `occupation` and required consent booleans are required only after KYC approval, when submitting the Sponge Card application.
- `account_opening_privacy_notice` is required when the KYC country is `US`.
- If KYC verification is missing, the API returns `submitted_application:false`, an onboarding step indicating KYC verification, and a `kyc_url` / `verification_url`. Have the user complete that link, then call `/onboard` again.

Returns `submitted_application`, `environment`, `ready_for_card_creation`, `customer`, `completion_link_url`, optional `kyc_url` / `verification_url`, `onboarding_step`, and `message`.

#### Accept Sponge Card terms

Use this only when `/status` says the Sponge Card application is approved but Sponge consent is missing.

```bash
curl -sS -X POST "$SPONGE_API_URL/api/sponge-card/terms" \
  -H "Authorization: Bearer $SPONGE_API_KEY" \
  -H "Sponge-Version: 0.2.2" \
  -H "Content-Type: application/json" \
  -d '{
    "e_sign_consent":true,
    "account_opening_privacy_notice":true,
    "sponge_card_terms":true,
    "information_certification":true,
    "unauthorized_solicitation_acknowledgement":true
  }'
```

#### Create a Sponge Card

Call this after status returns `ready_for_card_creation: true`. If a card already exis

…(truncated)
