agentic payments: your agent pays for images with usdc
there’s a status code in HTTP that has been reserved since 1997 and never used: 402 Payment Required. the spec literally says “reserved for future use”. the future turned out to be agents
the problem
an agent can’t have a credit card. it can’t click a verification link. it can’t fill in a stripe form. every “developer-friendly api” on earth assumes a human did the signup and pasted a key into an env var. that works for one dev and one script. it does not work for an agent that is spun up, needs a picture, and is gone
the fix: 402, but real
x402 is an open standard (Coinbase started it, it’s under the Linux Foundation now) for exactly this. the server answers 402 with a machine-readable price; the client signs a stablecoin transfer authorization; the server verifies and settles it on-chain through a facilitator; the original request goes through. no account, no session, no OAuth dance. one round-trip
maginary now does this on Base mainnet, in USDC, on the same endpoint you’d curl with an api key. here’s the actual response you get today for a bare, unauthenticated POST /api/gens/ with {"prompt": "a fox in autumn foliage --ar 16:9 --1"}:
{
"x402Version": 2,
"error": "No account, no credits. Agents: pay $0.07 USDC on Base (see accepts) — the first settlement creates your account. Subsequent requests use wallet-signed headers (see wallet_auth). Humans: sign up at https://app.maginary.ai/dashboard",
"accepts": [
{
"scheme": "exact",
"network": "eip155:8453",
"amount": "70000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0x945fe8CFac235F6C1d5A8E3C5982aFd6272Ea6f9",
"maxTimeoutSeconds": 60,
"extra": { "name": "USD Coin", "version": "2" }
}
],
"wallet_auth": {
"message": "Maginary: authenticate {address} at {timestamp}. This does not move funds.",
"headers": {
"X-Wallet-Address": "0x… (lowercased)",
"X-Wallet-Timestamp": "unix epoch seconds",
"X-Wallet-Signature": "EIP-191 personal_sign of the message above"
},
"window_seconds": 300
},
"resource": { "url": "https://app.maginary.ai/api/gens/", "description": "…", "mimeType": "application/json" },
"extensions": { "bazaar": { "…": "input schema + example, for discovery indexes" } },
"billing_url": "https://app.maginary.ai/dashboard"
} 70000 is USDC atomic units: 7 cents. that’s one credit, which is one standard image. the price is for this prompt — a 4-image grid says 280000, a --flagship image 420000, a 5-second seedance clip 2450000. the engine estimates before anything runs, so the agent always sees the ceiling first
the flow
agent → POST /api/gens/ (no auth)
← 402 + accepts[] (exact usdc amount, network, payTo)
agent signs an EIP-3009 transfer authorization (off-chain, gasless)
agent → POST /api/gens/ again, PAYMENT-SIGNATURE header
→ maginary → facilitator verifies + settles on base (~5 s)
→ account created for the wallet, credits granted
← 201, generation started, PAYMENT-RESPONSE header with the tx hash
agent → GET /api/gens/{uuid}/ with wallet-signed headers → image urls the agent never held an api key. the wallet is the account. every later request is authenticated by signing a short message with the same key (wallet_auth above tells you the exact string). the signature proves you are the wallet that paid; nothing else is needed. no key to store, lose, rotate or leak
if the wallet later wants a “real” account — a dashboard, stripe, a human — POST /api/auth/x402/claim/ with an email, signed by the wallet, attaches one. optional. most agents won’t bother
the code
the x402 python sdk does the 402 → sign → retry loop for you. this is the whole client:
from eth_account import Account
from x402 import x402Client
from x402.mechanisms.evm.exact import register_exact_evm_client
from x402.http.clients.httpx import x402HttpxClient
wallet = Account.from_key(AGENT_PRIVATE_KEY) # holds a little usdc on base
signer = x402Client()
register_exact_evm_client(signer, signer=wallet)
async with x402HttpxClient(signer) as http:
r = await http.post(
"https://app.maginary.ai/api/gens/",
json={"prompt": "a fox in autumn foliage --ar 16:9 --1"},
)
gen = r.json() # 201, has uuid
receipt = r.headers["PAYMENT-RESPONSE"] # base64: {success, transaction, network, payer} pip install 'x402[httpx,evm]'. the typescript sdk is the same shape. no ETH needed: EIP-3009 is a signed authorization, the facilitator pays gas
polling afterwards uses the wallet signature instead of a bearer:
import time
from eth_account.messages import encode_defunct
def wallet_headers(wallet):
ts = int(time.time())
addr = wallet.address.lower()
msg = f"Maginary: authenticate {addr} at {ts}. This does not move funds."
sig = wallet.sign_message(encode_defunct(text=msg)).signature.hex()
return {"X-Wallet-Address": addr, "X-Wallet-Timestamp": str(ts), "X-Wallet-Signature": sig}
r = await http.get(f"https://app.maginary.ai/api/gens/{gen['uuid']}/", headers=wallet_headers(wallet)) and inside mcp
the same thing works inside a tool call. on the hosted mcp, generate from a broke or unknown caller returns a payment_required result that carries the x402 challenge at the top level. an x402-aware mcp client (the sdk’s x402MCPSession) signs it and calls the same tool again with the payment in _meta. the receipt comes back in _meta too. no HTTP 402 anywhere in the transcript, no user prompt, the agent just… has the image
the mcp server holds zero payment logic. it forwards the payment header to the same /api/gens/ you saw above. one contract, two doors
the numbers
- 7¢ per credit, same rate as the $10 novice pack. one standard image is one credit
- minimum 7¢ per payment, maximum $200. overpay and the extra is banked as credits on the wallet’s account — top-up semantics, not per-call metering, so a variable-cost generation never needs a refund
- gasless for the agent. settlement is ~5 seconds on Base
- if the actual generation costs less than the estimate, the difference stays as credits. you never pay more than the 402 said
why this and not “just use stripe”
stripe is for humans, and maginary is fully on stripe for humans. this is the other door: the one for a program that has budget but no identity. and being x402-native means maginary shows up where agents look for things to buy — the x402 bazaar, x402scan, the awesome-lists — with a live, priced endpoint, not a signup form
there’s a third door coming for businesses that want fiat and an invoice but still want agent-native billing. later
try it
- read a live 402:
curl -X POST https://app.maginary.ai/api/gens/ -H 'content-type: application/json' -d '{"prompt":"a fox --1"}' - pay it: the snippet above, with $1 of USDC on Base in a throwaway wallet. that’s the loss ceiling
- humans: app.maginary.ai, same credits, no wallet needed
- the mcp: maginary.ai/mcp
follow my progress: @xucian_