Demo mode: a reference storefront for eSIM operators. Prices are live, payments are off. Run your own operator →
Travel eSIM · demo
English

Cases: an AI agent buys an eSIM, then gets its money back

Contents

Real runs of the kit, with their transactions, so you can check every step on chain. Case 2 (section 10) is the same agent buying our capability through the hub. The first one is the whole agent path in both directions: an AI agent finds an offer, pays for it in USDC over x402, hands the eSIM to its owner, and later asks for a refund, which the seller node pays back without anyone touching a wallet.

1. What happened

On 15 October 2026 the owner of ARGUS, a personal AI agent, wrote to it in Telegram: buy me the cheapest eSIM for Turkey. ARGUS asked the kit's seller node for offers, picked Turkey 500 MB for 7 days at 0.31 USDC, asked its owner for approval, and paid from its own wallet. Seconds later the owner had the QR code and the activation code in the chat. ARGUS itself never saw them.

Thirty-four minutes later the owner asked ARGUS for a refund. The eSIM was not installed, so the seller node cancelled it at the wholesaler and sent the 0.31 USDC back to the agent's wallet. Every wallet and the wholesale balance ended where they started.

The seller was the kit's private test stand: an ordinary seller node, selling at cost, reachable only from the agent's host. Nothing in the run was simulated.

2. Who took part

PartyWhat it isAddress or place
Ownerthe person ARGUS works for; approves every payment in Telegrama private Telegram chat
ARGUSthe agent (buyer); pays from its own wallet0x428f2Be4327908948e3B1915C55ddF8fA6bf73cc
Seller nodethe kit's rails/ node (seller) with automatic refunds onhttps://esim.modelmarket.dev/stand
Seller walletreceives payments and pays refunds (PAY_TO and REFUND_KEY_FILE are the same wallet)0xF47161fBB56D0D910a17e503C8292903F41BbAE2
FacilitatorCoinbase's x402 facilitator: verifies each signed transfer and puts it on chain, paying the gasCDP
WholesalereSIM Access: issues and cancels the profilethe operator's account

3. The whole chain

sequenceDiagram
    autonumber
    actor O as Owner (Telegram)
    participant A as ARGUS (agent)
    participant S as Seller node
    participant F as x402 facilitator
    participant W as eSIM Access
    O->>A: buy me the cheapest eSIM for Turkey
    A->>S: POST /quote {"country":"TR"}
    S-->>A: TR_0.5_7, 500 MB, 7 days, 0.31 USDC (signed)
    A->>O: approve esim_buy?
    O-->>A: /yes
    A->>S: POST /order {"offer_id":"TR_0.5_7"}
    S-->>A: 402 Payment Required: 0.31 USDC to the seller wallet
    A->>S: the same order with PAYMENT-SIGNATURE (EIP-3009, signed by ARGUS)
    S->>F: verify
    S->>W: order one profile (idempotent by the payment)
    W-->>S: profile 26101505200012
    S->>F: settle
    F-->>S: tx 0x617efd7a…3ae0
    S-->>A: delivery: activation code, QR, status token
    A->>O: QR and activation code, sent around the model
    Note over O,W: 34 minutes later
    O->>A: refund the last eSIM, I did not install it
    A->>O: approve esim_refund?
    O-->>A: /yes
    A->>S: POST /refund {esim_tran_no, status_token}
    S->>W: is it installed? any data used?
    W-->>S: GOT_RESOURCE, not installed, 0 bytes
    S->>W: cancel the profile
    S->>F: verify and settle 0.31 USDC, seller wallet to ARGUS
    F-->>S: tx 0x6dcfb7f0…ed4b
    S-->>A: refunded
    A->>O: refunded 0.31 USDC, tx 0x6dcfb7f0…ed4b

4. The transactions

All on Base mainnet, in USDC (0x8335…2913). Open any of them on basescan.org.

StepTransactionBlock, time (UTC)From → toAmountGas paid by
Funding the agent0x225afba5cd651e3ccdf70593fe985d1e24ddc14836270e83b13f546b6d01757852624834owner's wallet 0x097e…475a → ARGUS0.50the owner
Purchase0x617efd7abe4b4c6a80b54046617a73415aa54df7ada5f002ce7260cf68ac3ae052626137, 05:20:21ARGUS → seller wallet0.31facilitator 0x93f6…be44
Refund0x6dcfb7f075ad7fb6308064ac53e310dfb6adfd9b65d9f44b3c9a6dd39ca9ed4b52627173, 05:54:53seller wallet → ARGUS0.31facilitator 0x4c93…0205

Both the purchase and the refund are EIP-3009 transferWithAuthorization calls: the paying wallet signs, the facilitator submits. Neither ARGUS nor the seller wallet held any ETH, before, during or after.

5. Money and balances

flowchart LR
    A["ARGUS wallet"] -- "purchase: 0.31 USDC (x402)" --> S["Seller wallet"]
    S -- "refund: 0.31 USDC (EIP-3009 via the facilitator)" --> A
    N["Seller node"] -- "order 0.30 USD, then cancel" --> W["eSIM Access balance"]
    F["x402 facilitator"] -. "gas for both transfers" .-> B["Base"]
BeforeAfter the purchaseAfter the refund
ARGUS wallet0.50 USDC0.19 USDC0.50 USDC
Seller wallet00.31 USDC0
eSIM Access balance$20.00$19.70$20.00
ETH spent by ARGUS or the seller000

The stand sold at cost (MARGIN_PCT=0, MIN_MARGIN_USD=0.01): $0.30 wholesale, 0.31 USDC to the agent. With the kit's default margin the same plan sells for $1.30.

6. The refund rules

stateDiagram-v2
    [*] --> delivered: paid and settled
    delivered --> in_use: installed or data used
    in_use --> [*]: 409 in_use, no refund
    delivered --> cancelled: POST /refund with the status token
    cancelled --> refunded: refund wallet pays the price back
    cancelled --> due: no refund wallet, or the payout failed
    due --> [*]: the operator pays it by hand
    refunded --> [*]

7. Turning refunds on for your node

Refunds are off until you give the node a wallet to pay them from. Without it, POST /refund still cancels the unused eSIM and records the refund as due, and you get the agent_order.refund_due webhook to pay it yourself.

# a wallet that holds USDC on Base for refunds: PAY_TO's own wallet, so refunds return the money
# that came in, or a separate float. It needs no ETH: the facilitator pays the gas.
cd ~/esim-operator-kit
umask 077
printf 'Refund wallet private key: '; read -rs v; printf %s "$v" > secrets/refund_key; echo; unset v
sudo chown 1000:1000 secrets/refund_key && sudo chmod 600 secrets/refund_key
echo "REFUND_KEY_FILE=/run/secrets/esim/refund_key" >> .env
echo "REFUND_MAX_USD=20" >> .env
echo "REFUND_DAILY_MAX_USD=100" >> .env
docker compose up -d rails
curl -s https://<your domain>/agents/healthz    # "refunds": "automatic"
SettingWhat it does
REFUND_KEY_FILEThe key of the wallet refunds come from. The only private key the kit ever reads, and only when you opt in.
REFUND_MAX_USDThe most one refund pays.
REFUND_DAILY_MAX_USDThe most all refunds pay in a day.
CDP_KEY_FILEAlready set for payments; refunds go through the same facilitator.

Keep only a float on the refund wallet if it is not your main wallet: a key on a server is a key a break-in can read.

8. Asking for a refund

An agent needs the two values it got with the delivery:

curl -s -X POST https://<seller>/agents/refund -H 'content-type: application/json' \
  -d '{"esim_tran_no":"26101505200012","status_token":"…"}'
{"success": true, "result": {"esim_tran_no": "26101505200012", "status": "refunded", "amount_usd": "0.31",
 "to": "0x428f2be4327908948e3b1915c55ddf8fa6bf73cc", "at": "2026-10-15T05:54:50.399Z",
 "transaction": "0x6dcfb7f075ad7fb6308064ac53e310dfb6adfd9b65d9f44b3c9a6dd39ca9ed4b"}}

status is refunded with the transaction, or due when the seller pays by hand. /listing.json says which: its refund.policy names the rule, and /healthz shows "refunds": "automatic" or "operator".

9. What we learned

10. Case 2: through the hub, from our public node

In case 1 the agent was given the seller's address. In case 2 it found the seller itself: ARGUS searched the modelmarket.dev hub for eSIM sellers, got our capability esim.quote@v1 of esim-modelmarket, asked it for offers through the hub, and paid the seller's own /order over x402. The hub routed the free quote and never touched the money.

For this test our public node was switched from demo to selling, and switched back to demo right after. The node behind esim-modelmarket (modelmarket.dev/providers/esim) sold for 52 minutes, from 06:56:55 to 07:48:33 UTC on 15 October 2026, at cost (MARGIN_PCT=0, MIN_MARGIN_USD=0.01), paying into the same seller wallet as the stand and refunding from it automatically (at most $2 per refund, $5 a day). It has refused every order with demo_mode again since 07:48:33.

sequenceDiagram
    autonumber
    actor O as Owner (Telegram)
    participant A as ARGUS (agent)
    participant H as modelmarket.dev hub
    participant S as Seller node (modelmarket.dev/providers/esim)
    participant F as x402 facilitator
    O->>A: buy me the cheapest eSIM for Turkey
    A->>H: GET /ai-market/v2/search?intent=esim
    H-->>A: esim.quote@v1 of esim-modelmarket (via esim.modelmarket.dev)
    A->>H: POST /ai-market/v2/invoke esim.quote@v1 (free)
    H->>S: relays the quote
    S-->>A: seller demo: false · TR_0.5_7 0.31 USDC · order at …/providers/esim/order
    A->>O: [SELLS] TR_0.5_7 0.31 USDC · approve esim_buy?
    O-->>A: /yes
    A->>S: x402 straight to the seller: 402, then PAYMENT-SIGNATURE (EIP-3009)
    S->>F: verify, then settle
    F-->>S: tx 0xeea12962…881e
    S-->>A: delivery
    A->>O: QR and activation code, around the model
    O->>A: refund the last eSIM
    A->>O: approve esim_refund?
    O-->>A: /yes
    A->>S: POST /refund {esim_tran_no, status_token}
    S->>F: cancel at eSIM Access, then 0.31 USDC back
    F-->>S: tx 0x45dc5ce6…ec88
    S-->>A: refunded
Time (UTC)What happened
06:56:55The public node switches from demo to selling. Through the hub, its quote now says demo: false and 0.31 USDC
07:21First attempt: ARGUS sees the offers but refuses to buy. The node was still called "modelmarket.dev demo seller", and the agent judged by the name
07:23Fix deployed: the node's name follows its mode, and ARGUS labels every offer [SELLS] or [DEMO] from the seller's own answer
07:47:35ARGUS buys TR_0.5_7 after the owner's /yes; eSIM 26101507470034 is delivered to the owner's Telegram
07:48:02The owner asks for the refund, approves with /yes; the node cancels the unused eSIM and pays back
07:48:33The public node is switched back to demo
StepTransactionBlock, time (UTC)From → toUSDCGas paid by
Purchase0xeea12962c4f494e55acc59a376203bd4b704804f19a84b4b8f47a96b265a881e52630556, 07:47:39ARGUS → seller wallet 0xF471…bAE20.31facilitator 0x68a9…7863
Refund0x45dc5ce6579d8e889750442d63773586c481a11b30ddf44805960860f3bbec8852630569, 07:48:05seller wallet → ARGUS0.31facilitator 0x625d…ac39

ARGUS was the only buyer in the window: the node's records hold one order, refunded. At the end ARGUS held 0.50 USDC again, the seller wallet 0, the eSIM Access balance $20.00, the eSIM was in state CANCEL, and no wallet had spent ETH.

What this case showed:

The kit on GitHub →