Pay with USDC on Base
An agent can register a Headless Domain with real USDC on Base mainnet without
MetaMask, Rainbow, or a browser. The payer signs a bounded EIP-3009
authorization locally; machine.checkout.best relays it and pays gas.
In this route, MPP is the machine checkout workflow and x402 is the exact payment challenge format returned for Base. EIP-3009 is the USDC authorization the payer signs. These are layers of one checkout—not three separate payments.
This moves real money
Base mainnet is chain ID 8453. Base Sepolia (84532) is test-only. Set a
maximum charge, verify every challenge field, and stop on any mismatch.
Constants to verify
| Field | Required Base mainnet value |
|---|---|
| Network | eip155:8453 |
| USDC contract | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
| Transfer method | eip3009 |
| USDC decimals | 6 |
| Current Headless Domains receiver | 0x018d3DC9447D2455772cf1f61E0EB453D7103F0e |
The receiver may change through a controlled treasury rotation. Treat this page as an independent allowlist source, but always require the quote, registration response, and flat x402 challenge to agree. Stop if they do not.
1. Check runtime availability
curl -sS https://machine.checkout.best/.well-known/payment-methods | jq
Continue only if the Base method reports both:
{
"network": "eip155:8453",
"settlement_enabled": true
}
If it reports settlement_enabled: false, Base intake is closed. Use the
default Tempo route or wait; do not probe the registration endpoint by signing
or paying.
2. Authenticate the agent
If the agent does not have an API key, provision one:
curl -sS -X POST https://headlessdomains.com/api/v1/agents/provision \
-H 'Content-Type: application/json' \
-d '{}'
Store the returned API key securely and use it as X-API-Key. Do not print it
in logs or chat. Give the human the claim code only after the task is complete.
3. Get a non-mutating quote
curl -sS -X POST https://headlessdomains.com/api/v1/domains/quote \
-H "X-API-Key: $HEADLESSDOMAINS_API_KEY" \
-H 'X-Payment-Method: mpp' \
-H 'X-MPP-Network: base-mainnet' \
-H 'Content-Type: application/json' \
-d '{"domain":"myagent","namespace":"agent","years":1}'
Require all of the following before continuing:
- HTTP
200,status: quoted,binding: false, andmutation: none available: true,mpp_network: base, andchain_id: 8453price.currency: USDCandprice.amountat or below your approved maximumnext.method: POST, an HTTPSnext.url, andnext.creates_pending_order: true
The quote does not reserve the domain or create an order. Availability and price are checked again when the next request creates a checkout session.
4. Create one checkout challenge
Send the registration request once with the same authenticated identity and explicit Base selection:
curl -sS -X POST https://headlessdomains.com/api/v1/domains/register \
-H "X-API-Key: $HEADLESSDOMAINS_API_KEY" \
-H 'X-Payment-Method: mpp' \
-H 'X-MPP-Network: base-mainnet' \
-H 'Content-Type: application/json' \
-d '{
"domain":"myagent",
"namespace":"agent",
"years":1,
"payment_method":"mpp",
"mpp_network":"base-mainnet",
"agreed_to_terms":true
}'
Expect HTTP 402. Preserve the returned session_id, one-time
session_token, settle_url, receipt_url, and flat payment_challenges
object, plus the request/domain/quote binding, in an OS keychain or atomic
mode-0600 recovery record before parsing the challenge. Do not expect
payment_challenge.accepts[]. If parsing or transport then fails, recover this
same session; never create or sign a second payment because of the failure.
Convert the authenticated quote display amount to six-decimal minor units and
require exact equality with payment_challenges.amount before signing. A value
that is merely below the same maximum is not sufficient. After registration,
status, and public lookup all reach terminal success, overwrite the recovery
record to remove the session token, signed authorization, signature, client
reference, and settlement idempotency key. Retain only non-secret completion
evidence.
5. Validate before signing
The flat challenge must contain:
{
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0x018d3DC9447D2455772cf1f61E0EB453D7103F0e",
"amount": "520000",
"maxTimeoutSeconds": 600,
"extra": {
"assetTransferMethod": "eip3009",
"name": "USD Coin",
"version": "2"
}
}
The amount above is illustrative. Use the fresh quote and challenge and enforce
your own maximum. amount is in six-decimal USDC minor units, so 520000
means 0.52 USDC.
The asset ticker remains USDC, but Base mainnet USDC's EIP-712 signing-domain
name is USD Coin. Copy the fresh challenge's extra.name value exactly into
the typed-data domain; do not replace it with the ticker. Base Sepolia's test
token uses a different signing name, so examples are not interchangeable
between networks.
Reject the challenge if either URL is not HTTPS, any field conflicts with the quote, the receiver is not independently approved, the payer lacks funds, or the domain is no longer available.
For EIP-3009, validBefore - validAfter must not exceed
maxTimeoutSeconds. Any backdating allowance is part of that total span.
6. Settle exactly once
Create TransferWithAuthorization typed data, sign it locally with the payer
key, and POST the authorization and signature to settle_url with:
X-Checkout-Session-Token: <session_token>
Idempotency-Key: <stable value for this session and authorization>
Pass the returned flat payment_challenges object back unchanged as
accepted. Never send the payer private key. The service's relayer/gas key is
a separate operator secret and is not a customer credential.
Use the complete public Python reference client for typed-data construction and settlement.
7. Confirm, reconcile, and verify
confirmedmeans the settlement is confirmed.broadcast,prepared, orpendingmeans wait and pollreceipt_urlusing the same session token. Do not sign a replacement authorization.- A timeout or lost response is unknown state—not permission to pay again.
After confirmation, reconcile the existing checkout with Headless Domains:
curl -sS -X POST \
"https://headlessdomains.com/api/agent/register-status/$SESSION_ID/reconcile" \
-H "X-API-Key: $HEADLESSDOMAINS_API_KEY"
Then read its status:
curl -sS \
"https://headlessdomains.com/api/agent/register-status/$SESSION_ID" \
-H "X-API-Key: $HEADLESSDOMAINS_API_KEY"
Completion requires all of these:
- settlement status
confirmed - order status
paid - domain status
active - a domain ID and order ID
- a transaction hash for the expected Base transfer
Finally verify the public record:
curl -sS https://headlessdomains.com/api/v1/lookup/myagent.agent
Recovery and replay rules
If payment confirmed but the domain is not active, call reconcile for the
same session. An idempotent replay must return the same payment reference and
must not create another transfer, attempt, order, or domain.
Never reset a Base lock merely because a local clock expired. Lock release is
safe only after the checkout service proves payment_state: expired_unpaid.
If status is confirmed, broadcast, pending, malformed, or unavailable, stop and
preserve the session for recovery.
What an agent should report
Report only non-secret evidence: domain, final status, amount, currency, network, order ID, domain ID, and transaction hash. Never expose the API key, session token, private key, raw authorization, or signature.