Skip to main content
LearnGuidesTest agent payments without real money
GUIDE

Test agent payments without real money.

12 minutes
SHORT ANSWER

Swap your API key for a sk_test_ key - that alone puts you on Base Sepolia. Fund the agent's wallet from the public Base Sepolia USDC faucet, make a real test payment with payments.create (test funds, no real money), and tunnel your local webhook through ngrok. The response shapes match live, so a flow that passes in test passes in production. Exercise the failure paths, not just the happy one.

PREREQUISITES

Before you start.

  • A working integration on live (or at least live-shaped) - see add-payments-to-agent.
  • A sk_test_ API key and matching test signing secret from the dashboard.
  • ngrok (or any HTTPS tunnel) for development-time webhook delivery.
  • A separate development environment - distinct env vars, distinct database (or at least distinct tables), distinct webhook URL.
  • Comfort with the webhook patterns guide - this guide assumes you have a handler to test against.
STEP 1 OF 5

Switch to a test key.

A sk_test_ key transacts on Base Sepolia; a sk_live_ key transacts on Base mainnet. The prefix picks the network - there is no separate network env var, and a test key cannot move mainnet funds. So all you change for a dev environment is the key (and the test webhook secret).

# .env.development
# A sk_test_ key picks Base Sepolia automatically - there is no network env var.
BLOCKCHAIN0X_API_KEY=sk_test_01J9...
BLOCKCHAIN0X_WEBHOOK_SECRET=...   # the test webhook's secret, from the dashboard
STEP 2 OF 5

Fund the agent's wallet from the faucet.

Test USDC has no monetary value but otherwise behaves like live USDC: same response shapes, same balance tracking. There is no SDK call that mints it - you fund the agent's wallet address from the public Base Sepolia USDC faucet. Find the address in the dashboard or on the agent's public page (or read the agent with the SDK), then paste it into the faucet.

TypeScript
import { createClient } from "@blockchain0x/node";

const client = createClient({ apiKey: process.env.BLOCKCHAIN0X_API_KEY! }); // sk_test_

// Look up the agent; its wallet address is shown in the dashboard and on the
// agent's public page. Fund THAT address from the Base Sepolia USDC faucet -
// there is no SDK call that mints test funds.
const agent = await client.agents.get("agt_123");
console.log(agent.id);
Python
from blockchain0x import Client

client = Client()  # reads BLOCKCHAIN0X_API_KEY (sk_test_)

# The agent's wallet address is in the dashboard / on its public page.
# Paste it into the public Base Sepolia USDC faucet to fund it.
agent = client.agents.get("agt_123")
print(agent["id"])
STEP 3 OF 5

Make a real test payment.

With the wallet funded, call payments.create on your sk_test_ key. It is a real transfer on Base Sepolia using test funds, and it fires the payment.received webhook exactly as mainnet would - so you exercise the actual code path, not a simulation. Watch the event land at your tunnelled handler.

TypeScript
// On a sk_test_ key this is a REAL transfer on Base Sepolia (test funds, no
// real money). It fires the payment.received webhook just like mainnet does.
const tx = await client.payments.create({
  agentId: "agt_123",
  to: "0xRecipientOnSepolia",
  amountWei: "10000", // 0.01 USDC
});

console.log(tx); // watch payment.received arrive at your webhook
Python
tx = client.payments.create(body={
    "agentId": "agt_123",
    "to": "0xRecipientOnSepolia",
    "amountWei": "10000",  # 0.01 USDC
})

print(tx)  # watch payment.received arrive at your webhook

Three scenarios to exercise at minimum: a payment that lands (the happy path, payment.received), a payment that never lands (point the webhook at a dead URL and confirm your reconcile sweep catches the stuck job - the path most teams ignore), and a webhook retry (force a 500 the first time and 200 the second, then verify your idempotency skipped the duplicate work).

STEP 4 OF 5

Tunnel webhooks to your local handler.

Test payments send real webhooks to whatever URL you configured for the test webhook. For local development, give it an HTTPS tunnel to your laptop. ngrok is the simplest option; any reverse-tunnel tool works.

# Tunnel your local webhook endpoint to a public HTTPS URL.
$ ngrok http 3000

# Forwarding   https://abc123.ngrok.app -> http://localhost:3000

# Paste the URL in the dashboard under Webhooks for your test
# environment - test and live keep separate webhook config.

Test and live use separate keys and separate webhook configuration, so you can leave production pointed at your real endpoint while your local tunnel handles test events.

STEP 5 OF 5

Fail fast on misconfigured keys.

The single most common production incident around test/live keys is silent: a deploy lands with a test key, no payments come through, alerts only fire after the next business day. Block this at boot: refuse to start if the env and the key prefix do not match.

TypeScript
// Fail fast if test/live get mixed up.
const apiKey = process.env.BLOCKCHAIN0X_API_KEY!;
const env = process.env.NODE_ENV;

if (env === "production" && apiKey.startsWith("sk_test_")) {
  throw new Error("Test key in production environment - aborting boot.");
}
if (env !== "production" && apiKey.startsWith("sk_live_")) {
  throw new Error("Live key in non-production environment - aborting boot.");
}
Python
import os, sys

api_key = os.environ["BLOCKCHAIN0X_API_KEY"]
env = os.environ.get("ENV", "development")

if env == "production" and api_key.startswith("sk_test_"):
    sys.exit("Test key in production environment - aborting boot.")
if env != "production" and api_key.startswith("sk_live_"):
    sys.exit("Live key in non-production environment - aborting boot.")
COMMON PITFALLS

Five testing mistakes that bite later.

Forgetting Base Sepolia is its own chain

A sk_test_ key transacts on Base Sepolia, not Base mainnet. The block explorers, the wallet addresses, and the gas tokens are all separate. A common confusion is to copy a real Base address into a test, watch it fail, and think the API is broken. Fund the agent's wallet address from the Base Sepolia USDC faucet and pay addresses that exist on that chain.

Not testing the failure paths

Most teams test the happy path - a payment that fires payment.received - then ship and find out later that their not-paid path is broken. Exercise it: point the webhook at a dead URL and confirm your reconcile sweep catches the stuck job, force a 500 from your handler and verify the retry is idempotent, and check that payments.create's 503 (chain adapter not wired) is handled. Test environments are cheap; production debugging is expensive.

Webhook URL still pointing at ngrok in production

Switching key prefixes is easy to remember; updating the webhook URL is easy to forget. If you go live with the URL still pointed at the ngrok tunnel from your laptop, the first production payment fires a webhook into the void. Treat the webhook URL change as part of the deploy checklist, not a one-time setting.

Trusting testnet timing as a proxy for live timing

Base Sepolia does not behave identically to Base mainnet - block timing and congestion differ. Do not use testnet to load-test mainnet throughput, and do not assume your testnet latency is what you will see in production. When you need real numbers, run a small-amount mainnet smoke test with a sk_live_ key.

Leaving test fixtures in shared databases

If your dev and prod environments share a database (don't), test events land in the same table as live events and break your idempotency dedupe (the event ID prefix is different but the row is real). At minimum, isolate the webhook_events table per environment. Better: separate DBs entirely. This is one of those rules that seems excessive until it bites once.

NEXT STEPS

Once the test loop is in your dev cycle.

With a healthy test loop in place, the remaining work is mostly hardening: dependable webhook handling under load, a final security checklist, and migrations from any prior payment provider you may be running alongside.

Full reference at docs.blockchain0x.com. Testnet details: Base chain glossary. Product surface: Payment API.

Last reviewed: 2026-05-15. Published under CC BY 4.0.

Test it before you ship it.

Full sandbox: test keys, Base Sepolia, simulatable lifecycle. Free.