the .usdc resolver api.
turn a .usdc name into the Arc address behind it with a single HTTP request. every read endpoint is public, CORS-open, cacheable and needs no authentication.
quickstart
$ curl https://usdcdomains.com/api/resolve/alice.usdc
a registered name returns 200 with the resolution payload. an unregistered one returns 404. that is the whole integration surface for most applications.
read endpoints
resolve a domain
returns the address a name points at, plus its owner and records. with or without the TLD: alice and alice.usdc are equivalent. addresses are lowercase 0x hex. cached 60s at the edge. 404 when unregistered.
{
"name": "alice",
"fqdn": "alice.usdc",
"tld": "usdc",
"owner": "0x8f2a…c41d",
"target": "0x93b7…7e02",
"records": { "x": "alice", "url": "https://alice.example" },
"registeredAt": "2026-09-17T09:12:44.180Z",
"txHash": "0x7d3e…",
"forSale": { "priceWei": "50000000000000000000", "priceUsdc": "50", "listedAt": "…" }
}check availability
validates the name and reports availability and price. always 200; read the available flag. a free result is a snapshot, not a hold: the database decides the winner at reservation time.
{
"available": true, "valid": true, "normalized": "alice", "fqdn": "alice.usdc",
"registered": false, "held": false,
"priceUsdc": "5", "priceWei": "5000000000000000000", "configured": true
}list the registry
every confirmed name, newest first. limit caps at 100; q is a case-insensitive substring filter.
{ "tld": "usdc", "total": 128, "limit": 50, "offset": 0, "domains": [ { "name": "alice", "fqdn": "alice.usdc", … } ] }domains by wallet
reverse lookup: everything a 0x address owns, newest first. case-insensitive. lists by owner, not target. served no-store.
{ "owner": "0x8f2a…c41d", "tld": "usdc", "count": 2, "names": [ … ] }registry stats
total registered names, price tiers, treasury address, chain id and explorer. configured is false while the treasury is unset and registrations are closed.
registration
reserve → pay → confirm. ownership is recorded only after the payment settles on Arc mainnet (chain id 5042).
reserve a name
body: { name, wallet }. inserts a pending reservation with a fresh reference and a ten-minute expiry. a unique index decides races: the loser gets 409 NAME_UNAVAILABLE. send memo as the transaction calldata (UTF-8 → hex) and wei as its value, to treasury. reserving the same name again from the same wallet returns the live reservation.
{
"reservationId": "…",
"memo": "usdc:reg:alice:1a2b3c4d",
"treasury": "0x072d…c14d",
"wei": "5000000000000000000",
"priceUsdc": "5",
"expiresAt": "…",
"chainId": 5042
}confirm payment
body: { reservationId, txHash }. the server fetches the transaction itself and verifies: the receipt succeeded, the chain id matches, the recipient is the treasury, the value covers the price, the calldata is exactly the reservation reference, and the sender is the reserving wallet. 425 TX_NOT_FOUND is retryable while the transaction is pending. a transaction is never credited twice.
{ "ok": true, "name": "alice", "fqdn": "alice.usdc", "owner": "0x8f2a…c41d", "target": "0x8f2a…c41d", "txHash": "0x7d3e…", "registeredAt": "…" }updates
repoint, set records, or transfer
body: { name, action, value, ts, signature }. action is set-target, set-records or transfer. for the address actions value is the new lowercase address; for records it is a JSON object string with sorted keys. ts is a millisecond timestamp within ten minutes of now. signature is a personal_sign by the current owner over exactly this text:
usdc registry action: set-target name: alice.usdc value: 0x93b7…7e02 ts: 1789470000000
no gas: the change is authenticated by signature and written to the registry immediately. a transfer also repoints the target to the new owner and removes any listing. record keys: avatar, url, description, x, github, telegram, discord, email, btc, sol, location, each at most 256 characters.
marketplace
peer-to-peer resale. the owner lists a price; a buyer pays the seller directly on Arc; the server verifies the transaction and moves ownership. no fee is taken and no funds pass through the registry.
names for sale
sort is newest, price-asc or price-desc; q is a substring filter. held is true while a buyer is completing a purchase. cached 15s.
{ "tld": "usdc", "total": 3, "listings": [ { "name": "alice", "fqdn": "alice.usdc", "seller": "0x8f2a…c41d", "priceWei": "50000000000000000000", "priceUsdc": "50", "listedAt": "…", "held": false } ] }one listing
the listing for a name, or 404 NOT_LISTED. registry and wallet payloads also carry a forSale field when a name is listed.
recent sales
completed purchases, newest first, each with the on-chain txHash of the payment. a sold name also exposes lastSale in its resolve payload.
list a name, or change its price
body: { name, priceWei, ts, signature }. priceWei is an integer string between 1 and 1,000,000 USDC (18 decimals). the owner signs this text with personal_sign; ts must be within ten minutes.
usdc market action: list name: alice.usdc price: 50000000000000000000 wei ts: 1789470000000
remove a listing
body: { name, ts, signature }. same message with action: unlist and price: 0 wei. refused with 409 BUY_IN_PROGRESS while a buyer holds the name; so are price changes and transfers.
reserve a listed name
body: { name, wallet }. ten-minute hold. returns the seller address to pay, the wei to send and the memo to put in the calldata. the seller cannot reserve their own name (400 SELF_PURCHASE).
{ "reservationId": "…", "memo": "usdc:buy:alice:1a2b3c4d", "seller": "0x8f2a…c41d", "wei": "50000000000000000000", "priceUsdc": "50", "expiresAt": "…", "chainId": 5042 }confirm the purchase
body: { reservationId, txHash }. the server reads the transaction from Arc and checks: mined and not reverted, to is the seller, value ≥ price, calldata equals the memo, from is the buyer, the listing is unchanged, the tx was never credited before. then owner and target move to the buyer, the listing is removed and the sale is recorded. 425 TX_NOT_FOUND while pending: retry.
name rules
- character set — a-z, 0-9 and hyphens. the ASCII check runs on the raw input before lowercasing, so Unicode look-alikes are refused outright.
- length — 3 to 63 characters, excluding the TLD.
- hyphens — no leading, trailing or consecutive hyphens.
- reserved — admin, www, api, support, help, mail, root, system, official, circle, arc, usdcdomains, registry, market, docs, domains, name, treasury and usdc itself.
amounts & chain
- USDC is the gas token on Arc. the native balance and the USDC ERC-20 at
0x3600…0000are the same funds shown two ways. - 18 decimals on the wire.
msg.valueand everyweifield use the 18-decimal native view: 5 USDC =5000000000000000000. the ERC-20 view uses 6 decimals; never mix them. - plain transfer. the payment is a native value transfer to an externally owned address with the memo as calldata — no contract call, no approval.
- fees. EIP-1559 with a 20 gwei floor; a registration costs about a cent in gas on top of the price. finality is deterministic and sub-second, so confirmation is usually instant.
- network. Arc mainnet, chain id
5042, rpchttps://rpc.mainnet.arc.io, explorerhttps://explorer.arc.io. bring USDC to Arc with Circle's CCTP bridge from Ethereum, Base, Solana and 20+ chains.
error codes
non-2xx responses carry a stable error.code plus a human-readable error.message. branch on the code.
| INVALID_NAME | 400 | the domain failed validation. |
| INVALID_WALLET | 400 | not a valid 0x address. |
| INVALID_TX | 400 | not a valid transaction hash. |
| INVALID_BODY | 400 | the request body is not a JSON object. |
| INVALID_ACTION | 400 | unknown update action. |
| INVALID_RECORDS | 400 | a record key or value is not allowed. |
| NOT_FOUND | 404 | the domain is not registered. |
| NAME_UNAVAILABLE | 409 | taken, or held by a live reservation. |
| NO_RESERVATION | 404 | the reservation id does not exist. |
| RESERVATION_EXPIRED | 409 | the ten-minute window closed and another reservation holds the name. |
| TX_NOT_FOUND | 425 | not mined yet. retryable. |
| TX_FAILED | 400 | the transaction landed but reverted. |
| WRONG_CHAIN | 400 | the transaction is on a different chain. |
| WRONG_RECIPIENT | 400 | the payment did not go to the expected recipient. |
| UNDERPAID | 400 | the value sent was below the price. |
| MEMO_MISMATCH | 400 | calldata is not this reservation's reference. |
| PAYER_MISMATCH | 400 | a different wallet sent the payment. |
| TX_ALREADY_USED | 409 | transaction already credited to a domain. |
| BAD_SIGNATURE | 401 | the update signature is not from the owner wallet. |
| STALE_SIGNATURE | 400 | the timestamp is outside the ten-minute window. |
| INVALID_PRICE | 400 | listing price is not an integer wei amount within bounds. |
| NOT_LISTED | 404 | the name is not for sale. |
| SELF_PURCHASE | 400 | the buyer already owns the name. |
| BUY_IN_PROGRESS | 409 | a buyer holds the name; listing changes, transfers and other buyers must wait. |
| LISTING_STALE | 409 | the listing or the owner changed since the reservation. |
| RATE_LIMITED | 429 | too many requests from this IP; honor Retry-After. |
| RPC_ERROR | 502 | the Arc RPC could not be reached. |
| NOT_CONFIGURED | 503 | deployment is missing its treasury or database. |
| DB_ERROR | 500 | the registry database could not be reached. |