Running an agent's email flow against mainnet for the first time is a bad place to discover a bug in your signing code — a mistake there costs real USDC. Base Sepolia exists for exactly this: the same contracts, the same EIP-3009 flow, the same DimeBox API, settled in test USDC that costs nothing to replace. Base Sepolia vs Base mainnet covers the network-level differences; this post walks through actually exercising DimeBox's full send/receive cycle against the test network before trusting any of it with real money.
Every quickstart in this pillar already defaults to this network — eip155:84532 — rather than mainnet's eip155:8453, specifically so copying a snippet doesn't accidentally sign against real funds:
# Python - from the quickstart
client.register("eip155:84532", ExactEvmScheme(signer=signer)) # Base Sepolia
// Node - from the quickstart
schemes: [{ network: "eip155:84532", client: new ExactEvmScheme(account) }]
See the Python quickstart or the Node quickstart for the full client setup around either snippet.
You'll need a funded signer before any paid call succeeds. Base documents its own current testnet faucets for Sepolia ETH and test USDC — follow whatever Base's own documentation says today rather than a specific URL pinned here, since faucet endpoints and providers change more often than the API they're funding calls against.
Provisioning is free and network-agnostic — no payment is involved, so it behaves identically on test and mainnet:
curl -X POST https://dimebox.app/api/inboxes
# → { "data": { "slug": "...", ... }, "read_token": "inbox_..." }
Sending is where the test network actually matters — this is the call that signs against whichever network your client registered:
curl -X POST https://dimebox.app/api/inboxes/<slug>/send \
-H 'Content-Type: application/json' \
-H 'PAYMENT-SIGNATURE: <signed payment>' \
-d '{"to":"[email protected]","purpose":"end-to-end test"}'
# → 202 Accepted - queued for delivery
Reading it back confirms delivery without needing to check an actual mailbox — queued delivery covers why the 202 above doesn't yet mean "sent," only "accepted":
curl https://dimebox.app/api/inboxes/<slug>/messages \
-H 'X-Inbox-Token: inbox_...'
If your use case involves another agent paying to deliver mail directly — the priority channel, rather than ordinary SMTP — exercise that path against Sepolia the same way, since it's a separately priced, separately signed call from the send above, not something send-side testing automatically covers.
It's easy to assume everything about the flow is network-dependent once you're thinking in terms of eip155:84532 versus eip155:8453, but most of it isn't. Provisioning an inbox, reading a mailbox, and drafting an email with /api/emails/generate's AI step are unaffected by which chain you're signing against for payment — the network only matters for the signature itself. Inbound mail arriving over ordinary SMTP is also unrelated to any of this; it depends on a third-party mail relay accepting and forwarding the message, not on anything you've configured for testing.
A small shell script wrapping the three calls above, checking status codes instead of eyeballing output, is enough for a smoke test you can run in CI before a deploy:
#!/usr/bin/env bash
set -euo pipefail
SLUG=$(curl -s -X POST https://dimebox.app/api/inboxes | jq -r '.data.slug')
TOKEN=$(curl -s -X POST https://dimebox.app/api/inboxes | jq -r '.read_token')
STATUS=$(curl -s -o /dev/null -w '%{http_code}' -X POST \
"https://dimebox.app/api/inboxes/${SLUG}/send" \
-H 'Content-Type: application/json' \
-H "PAYMENT-SIGNATURE: ${SIGNED_PAYMENT}" \
-d '{"to":"[email protected]","purpose":"ci smoke test"}')
if [ "$STATUS" != "202" ]; then
echo "expected 202, got ${STATUS}" >&2
exit 1
fi
echo "send accepted"
Generating SIGNED_PAYMENT itself still needs a real signer — the Python or Node quickstart covers that; this script is just the assertion layer wrapped around it.
Check status codes rather than response bodies alone: a 202 on send, a 201 on a paid priority-receive or claim call, a 402 on anything sent without a valid payment, and a 422 on a deliberately malformed body. The full status code table is the authoritative reference for what each code means and whether it's charged.
Once the cycle above runs clean, the only changes for production are the network id (eip155:8453) and a signer actually funded with real USDC on Base — the endpoints, request shapes, and retry logic are identical either way. Resist the temptation to also change something else "while you're in there"; test the exact code path you intend to ship, not a close approximation of it. If the Sepolia run above passed cleanly, the mainnet run almost certainly will too — the two differ only in which network your signer talks to, never in what DimeBox itself does with the request.