LaunaDocs
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)

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

Public endpoints (hosted)

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

Add the network in MetaMask (or another wallet):

  • Network name: Launa Sandbox Fork
  • RPC URL: https://rpc-dev.launa.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 launa 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 launa && make up
make restart-chain   # fresh Anvil fork

Auth and environment selection

MethodHow Sandbox is selected
DashboardEnvironment toggle → Sandbox (X-Launa-Environment: development)
API keylauna_sandbox_... keys (created while in Sandbox) — environment is baked into the key; use LAUNA_BASE_URL=https://api.staging.launa.io
JWTHeader X-Launa-Environment: development (or CLI --sandbox)
Env varsLAUNA_ENVIRONMENT=development or LAUNA_SANDBOX=1 (JWT / CLI only — not for API keys)

launa_live_... keys are production-only (https://api.launa.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 LAUNA_BASE_URL=https://api.launa.io   # JWT + --sandbox works on prod or staging
export LAUNA_JWT=$(launa auth login --email you@example.com --password '...')

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

launa pay url --url "https://your-api.dev….launa.io/v1/hello"
launa --sandbox sandbox-wallet get [--wait|--no-wait] [--json]
launa --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 { LaunaClient } from "@launa/sdk";

const client = new LaunaClient({
  baseUrl: process.env.LAUNA_BASE_URL!,
  token: process.env.LAUNA_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 := launa.NewClient(baseURL, launa.Credentials{Token: jwt},
  launa.WithEnvironment("development"))
wallet, err := cl.GetSandboxWallet(ctx)
balances, err := cl.DevBalances(ctx, wallet.Address)

Python

from launa_sdk import LaunaClient

client = LaunaClient(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-Launa-Environment: development
POST /sandbox-wallet/reset
Authorization: Bearer <JWT>
X-Launa-Environment: development
POST /dev/balances
Authorization: Bearer <JWT>
X-Launa-Environment: development
Content-Type: application/json

{"address":"0x..."}
POST /dev/faucet
Authorization: Bearer <JWT>
X-Launa-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 LAUNA_BASE_URL=https://api.staging.launa.io   # required for launa_sandbox_... keys
export LAUNA_API_KEY=launa_sandbox_...   # or LAUNA_JWT + --sandbox on either host

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

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

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

TypeScript

import {
  LaunaClient,
  buildSandboxUsdcAccept,
  usdcToAtomic,
} from "@launa/sdk";

const client = new LaunaClient({
  baseUrl: process.env.LAUNA_BASE_URL!,
  apiKey: process.env.LAUNA_API_KEY!, // launa_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 := launa.NewClient(baseURL, launa.Credentials{APIKey: testKey},
  launa.WithEnvironment("development"))
api, _ := cl.CreateAPI(ctx, launa.CreateAPIInput{ /* ... */ })
accept, _ := launa.BuildSandboxUsdcAccept("exact", "100000")
cl.CreateEndpoint(ctx, api.ID, launa.CreateEndpointInput{
  Method: "GET", PathPattern: "/v1/*", Accepts: []launa.AcceptOption{accept},
})

Python

from launa_sdk import LaunaClient, build_sandbox_usdc_accept

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

MCP

Set LAUNA_API_KEY to an launa_sandbox_... key and LAUNA_BASE_URL=https://api.staging.launa.io (API key environment is fixed by prefix — LAUNA_ENVIRONMENT does not apply). Default launa_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 LAUNA_EVM_PRIVATE_KEY for launa_pay_url.

Plain API

POST /apis
X-API-Key: launa_sandbox_...
# or Authorization: Bearer <JWT>
#     X-Launa-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 LAUNA_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):
launa pay url --url "https://demo.dev.<platform-domain>/v1/hello" --json

Or TypeScript @launa/sdk/buyer, Go/Python buyer modules, or MCP launa_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
LAUNA_BASE_URLhttps://api.staging.launa.io with launa_sandbox_... keys; https://api.launa.io for launa_live_... or JWT + --sandbox
LAUNA_API_KEYlauna_sandbox_... for Sandbox automation (pair with staging URL)
LAUNA_JWTRequired for sandbox-wallet and keys
LAUNA_ENVIRONMENTdevelopment selects Sandbox
LAUNA_SANDBOX1 / true — alias for development
LAUNA_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