Ax402Docs
PLATFORM

Sandbox

Test mode on the Anvil fork — APIs, wallet, RPC, SDK, CLI, MCP

Sandbox (seller Test mode) lets you create payable APIs and settle x402 payments without real funds. Settlements run on an Anvil fork of Base mainnet (eip155:845320402), not public Base or Base Sepolia.

In the seller dashboard, switch the environment toggle to Sandbox. The same mode is available from SDKs, CLI, MCP, and the control plane API.

What you get

PieceDetail
EnvironmentControl plane development (UI label: Sandbox)
NetworkCAIP-2 eip155:845320402 (Anvil --chain-id 845320402)
Payment tokenBase mainnet USDC at 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 (forked state)
FacilitatorDev facilitator (facilitator-dev) — not the public testnet facilitator
BillingDevelopment traffic is typically fee-exempt
PersistenceFork state is ephemeral — restarts wipe balances

Sandbox is for iterating APIs, endpoints, and payment flows. Use Production for live mainnet settlement.

Chain and RPC (chain-dev)

Ax402 runs chain-dev: Anvil fork + chain-manager faucet API + facilitator-dev.

Public endpoints (hosted)

ServiceURL
Anvil RPChttps://rpc-dev.ax402.io
Chain managerhttps://chain-manager-dev.ax402.io:8099
Dev facilitatorhttps://facilitator-dev.ax402.io

Add the network in MetaMask (or another wallet):

  • Network name: Ax402 Sandbox Fork
  • RPC URL: https://rpc-dev.ax402.io (or the rpc_url from GET /config/local-dev on your control plane)
  • Chain ID: 845320402
  • Currency symbol: ETH
  • USDC: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913

Control plane config for clients:

GET /config/local-dev

Returns enabled, chain_id, network, rpc_url, faucet_enabled, and the sandbox payment token. SDKs: client.config.localDev() (TypeScript), LocalDevConfig (Go), config.local_dev() (Python).

Local full stack

With the ax402 compose stack:

ServiceHost URL
Anvil RPChttp://127.0.0.1:18545
Chain managerhttp://127.0.0.1:8099
Dev facilitatorhttp://127.0.0.1:8089
cd ax402 && make up
make restart-chain   # fresh Anvil fork

Auth and environment selection

MethodHow Sandbox is selected
DashboardEnvironment toggle → Sandbox (X-Ax402-Environment: development)
API keyax402_sandbox_... keys (created while in Sandbox) — environment is baked into the key; use AX402_BASE_URL=https://api.staging.ax402.io
JWTHeader X-Ax402-Environment: development (or CLI --sandbox)
Env varsAX402_ENVIRONMENT=development or AX402_SANDBOX=1 (JWT / CLI only — not for API keys)

ax402_live_... keys are production-only (https://api.ax402.io). Combining them with --sandbox / development fails fast. API key prefix and base URL must match — wrong pairing returns 401 invalid token.

Sandbox wallet routes require a JWT (same as keys commands), not an API key.

Sandbox wallet (private key)

Each seller account can have a platform-managed EVM keypair for Sandbox payments. GET /sandbox-wallet get-or-creates the wallet, returns the private key, and kicks an async faucet (ETH + USDC on the fork).

export AX402_BASE_URL=https://api.ax402.io   # JWT + --sandbox works on prod or staging
export AX402_JWT=$(ax402 auth login --email you@example.com --password '...')

# Full private key in JSON; --wait polls until balances look funded
export AX402_EVM_PRIVATE_KEY=$(
  ax402 --sandbox sandbox-wallet get --json --wait | jq -r .private_key
)

ax402 pay url --url "https://your-api.dev….ax402.io/v1/hello"
ax402 --sandbox sandbox-wallet get [--wait|--no-wait] [--json]
ax402 --sandbox sandbox-wallet reset [--wait|--no-wait] [--json]

Human output redacts the key; --json includes private_key. --wait is on by default for human mode and off for --json unless you pass --wait.

TypeScript SDK

import { Ax402Client } from "@ax402/sdk";

const client = new Ax402Client({
  baseUrl: process.env.AX402_BASE_URL!,
  token: process.env.AX402_JWT!,
  environment: "development",
});

const wallet = await client.sandboxWallet.get();
// wallet.address, wallet.private_key, wallet.private_key_redacted

const balances = await client.config.devBalances(wallet.address);
// balances.eth_wei, balances.usdc_base (hex)

await client.config.devFaucet(wallet.address, "all"); // optional manual refill

Go

cl := ax402.NewClient(baseURL, ax402.Credentials{Token: jwt},
  ax402.WithEnvironment("development"))
wallet, err := cl.GetSandboxWallet(ctx)
balances, err := cl.DevBalances(ctx, wallet.Address)

Python

from ax402_sdk import Ax402Client

client = Ax402Client(base_url, token=jwt, environment="development")
wallet = client.sandbox_wallet.get()
balances = client.config.dev_balances(wallet["address"])

Plain HTTP

GET /sandbox-wallet
Authorization: Bearer <JWT>
X-Ax402-Environment: development
POST /sandbox-wallet/reset
Authorization: Bearer <JWT>
X-Ax402-Environment: development
POST /dev/balances
Authorization: Bearer <JWT>
X-Ax402-Environment: development
Content-Type: application/json

{"address":"0x..."}
POST /dev/faucet
Authorization: Bearer <JWT>
X-Ax402-Environment: development
Content-Type: application/json

{"address":"0x...","asset":"all"}

Response shape for wallet routes: address, private_key, private_key_redacted.

Create APIs and endpoints in Sandbox

Development APIs must use accepts on eip155:845320402 only. Default SDK/CLI helpers use Base USDC on that network.

CLI

export AX402_BASE_URL=https://api.staging.ax402.io   # required for ax402_sandbox_... keys
export AX402_API_KEY=ax402_sandbox_...   # or AX402_JWT + --sandbox on either host

ax402 --sandbox apis create \
  --name "Demo" --slug demo \
  --upstream https://api.example.com \
  --pay-to 0xYourWallet --json

ax402 --sandbox endpoints create \
  --api-id API_ID --path "/v1/*" --price-usdc 0.10 --json

--sandbox (or AX402_ENVIRONMENT / AX402_SANDBOX) sets the development environment and defaults endpoint pricing to the fork. Explicit --network / --accepts still win.

TypeScript

import {
  Ax402Client,
  buildSandboxUsdcAccept,
  usdcToAtomic,
} from "@ax402/sdk";

const client = new Ax402Client({
  baseUrl: process.env.AX402_BASE_URL!,
  apiKey: process.env.AX402_API_KEY!, // ax402_sandbox_...
  environment: "development",
});

const api = await client.apis.create({
  name: "Demo",
  slug: "demo",
  upstream_base_url: "https://api.example.com",
  pay_to_mode: "user_wallet",
  pay_to_address: "0x...",
});

await client.endpoints.create(api.id, {
  method: "GET",
  path_pattern: "/v1/*",
  accepts: [buildSandboxUsdcAccept("exact", usdcToAtomic("0.10"))],
});

Go

cl := ax402.NewClient(baseURL, ax402.Credentials{APIKey: testKey},
  ax402.WithEnvironment("development"))
api, _ := cl.CreateAPI(ctx, ax402.CreateAPIInput{ /* ... */ })
accept, _ := ax402.BuildSandboxUsdcAccept("exact", "100000")
cl.CreateEndpoint(ctx, api.ID, ax402.CreateEndpointInput{
  Method: "GET", PathPattern: "/v1/*", Accepts: []ax402.AcceptOption{accept},
})

Python

from ax402_sdk import Ax402Client, build_sandbox_usdc_accept

client = Ax402Client.from_env()  # AX402_ENVIRONMENT=development or ax402_sandbox_ key
api = client.apis.create({...})
client.endpoints.create(api.id, {
    "method": "GET",
    "path_pattern": "/v1/*",
    "accepts": [build_sandbox_usdc_accept("100000")],
})

MCP

Set AX402_API_KEY to an ax402_sandbox_... key and AX402_BASE_URL=https://api.staging.ax402.io (API key environment is fixed by prefix — AX402_ENVIRONMENT does not apply). Default ax402_add_endpoint / OpenAPI import pricing uses sandbox-fork USDC accepts.

MCP is API-key only — it cannot call /sandbox-wallet. Use the CLI + JWT for the funded private key, then set AX402_EVM_PRIVATE_KEY for ax402_pay_url.

Plain API

POST /apis
X-API-Key: ax402_sandbox_...
# or Authorization: Bearer <JWT>
#     X-Ax402-Environment: development
Content-Type: application/json

{"name":"Demo","slug":"demo","upstream_base_url":"https://api.example.com","pay_to_mode":"user_wallet","pay_to_address":"0x..."}
POST /apis/{id}/endpoints
Content-Type: application/json

{
  "method": "GET",
  "path_pattern": "/v1/*",
  "accepts": [{
    "scheme": "exact",
    "network": "eip155:845320402",
    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "amount": "100000",
    "max_timeout_seconds": 300,
    "extra": {"name": "USDC", "version": "2", "assetTransferMethod": "eip3009"}
  }]
}

Pay a Sandbox gateway URL

  1. Create API + endpoint in Sandbox (above).
  2. Load the sandbox wallet private key into AX402_EVM_PRIVATE_KEY.
  3. Ensure the wallet has fork ETH + USDC (sandbox-wallet get --wait or faucet).
  4. Pay the gateway hostname (not the control plane):
ax402 pay url --url "https://demo.dev.<platform-domain>/v1/hello" --json

Or TypeScript @ax402/sdk/buyer, Go/Python buyer modules, or MCP ax402_pay_url.

Shared demo gateway (when provisioned): check the seller dashboard Sandbox onboarding flow — useful for smoke tests; create your own API for real work.

Environment variables

VariablePurpose
AX402_BASE_URLhttps://api.staging.ax402.io with ax402_sandbox_... keys; https://api.ax402.io for ax402_live_... or JWT + --sandbox
AX402_API_KEYax402_sandbox_... for Sandbox automation (pair with staging URL)
AX402_JWTRequired for sandbox-wallet and keys
AX402_ENVIRONMENTdevelopment selects Sandbox
AX402_SANDBOX1 / true — alias for development
AX402_EVM_PRIVATE_KEYBuyer signer (export from sandbox-wallet get)

Limitations

  • Only the sandbox fork network — no Hedera / Base Sepolia / mainnet accepts on development APIs
  • Fork balances reset when chain-dev restarts
  • Shared staging Anvil is multi-tenant — do not rely on sticky balances
  • Batch-settlement is not supported on the sandbox fork helpers (exact only)
  • Sandbox wallet is JWT-only (not available via MCP API keys)

On this page