Domain Registration
This guide walks through registering a brand-new domain as a tokenized name on Doma, end to end, using the Doma API plus a single on-chain payment.
Register vs. tokenize. Use this guide to register a domain you don't own yet (primary registration through a Doma-integrated registrar). If you already own a domain and want to bring it on-chain, see How to tokenize a domain instead.
How it works
Registration is split into an off-chain half (the Doma GraphQL API) and an on-chain half (the Marketplace payment contract), tied together by a backend-signed payment voucher:
Search & price the name (
availableDomains,domainPricing).Get a
registrantHandleby uploading WHOIS contact details — either through Doma's email verification or, with a privileged key, a pre-verified upload (ICANN requires verified WHOIS contacts).Create an order (
createOrder) — Doma prices it and returns a signed payment voucher.Pay on-chain by calling
pay(voucher, signature)on the Marketplace contract.Track the order until it completes and the name token is minted to your wallet.
You never sign EIP-712 data yourself. Doma signs the payment voucher; your wallet only submits the on-chain payment. The voucher returned by createOrder is the source of truth for what to pay (token + amount) and where (paymentContractAddress).
Full example. A complete, runnable Node.js implementation of this guide — viem + graphql-request, single file — is on GitHub: doma-register-example. It uses the email-verification path (Option A).
Prerequisites
An API key — create a testnet key at app-testnet.doma.xyz/account/developers (or a mainnet key at app.doma.xyz/account/developers) and send it in the
Api-Keyheader on every request.A funded EVM wallet — it receives the name token and pays. Fund it with a little native ETH for gas, plus the registration fee in your chosen payment currency (USDC or native ETH).
An email address you can access (Option A only) — Doma emails a verification code to the registrant. Not needed if you use a privileged key for pre-verified uploads (Option B).
Network details
GraphQL API
https://api-testnet.doma.xyz/graphql
https://api.doma.xyz/graphql
Chain ID
97476
97477
CAIP-2
eip155:97476
eip155:97477
RPC
https://rpc-testnet.doma.xyz
https://rpc.doma.xyz
Explorer
https://explorer-testnet.doma.xyz
https://explorer.doma.xyz
Native token
ETH
ETH
All examples below use testnet. Every API request is a POST to the GraphQL endpoint with your key:
API keys are per-network. A testnet key won't work against mainnet and vice-versa. Requests are rate-limited to 10/second.
Step 1 — Check availability and price
Search for the name and confirm it's available:
Then fetch the registration quote, which also tells you the registrar and the allowed term length:
Call it with operation: REGISTRATION. Pick a registrarIanaId from the returned registrars and choose a term between minYears and maxYears.
Step 2 — Verify contact details and get a registrant handle
ICANN requires verified WHOIS contact details for every registration. You upload a contact and receive a registrantHandle (inside a signed proof-of-contacts voucher) that createOrder consumes in the next step.
There are two ways to get a handle. Pick based on your API key:
Option A — Email verification
Option B — Pre-verified upload
API key permission
SUBGRAPH (standard)
SUBGRAPH + VERIFIED_CONTACTS_UPLOAD
Email round-trip
Yes — Doma emails a code to the registrant
No
Who verifies the email
Doma
You (the integrating registrar/partner)
Best for
Apps registering on behalf of end users
Registrars/partners who already collect and verify contact details
Both options return the same shape — a proofOfContactsVoucher containing a registrantHandle. Save it for Step 3.
Option A — Email verification (standard key)
Use this when you want Doma to verify the registrant's email. First send a verification code:
The registrant receives a 6-character code (valid 10 minutes). Submit it to get a verification proof (a token valid ~30 days):
Then upload the WHOIS contact together with the proof to receive the handle:
Example variables (the contact.email must match the address you verified):
Option B — Pre-verified upload (privileged key)
If you're an integrating registrar or partner that already collects and verifies your customers' contact details, request the VERIFIED_CONTACTS_UPLOAD permission on your API key and upload the contact in a single call — no email round-trip:
Example variables:
uploadVerifiedRegistrantContacts asserts that you have already verified the contact's email. Only request this permission if you handle that verification yourself; otherwise use Option A. The permission is granted per API key — contact the Doma team to enable it.
Step 3 — Create the order
Create the order. Doma prices the registration and returns a signed payment voucher.
Example input:
buyeris a CAIP-10 address — the network prefix plus your wallet. It receives the name token.selectedPaymentTokenAddressselects the payment currency. Set it to the USDC token address to pay in USDC, or omit it to pay in native ETH.selectedPaymentPriceandcouponCodeare optional.
The voucher field is a JSON string with the shape:
USDC amounts map 1:1 to the USD price ($12.49 → 12490000 at 6 decimals). Native-ETH amounts are the USD price converted at an exchange rate. The voucher is valid for ~24h — if it expires, just create a new order.
Step 4 — Pay on-chain
Submit the voucher to the Marketplace contract returned as paymentContractAddress. Parse the voucher JSON, then:
Paying in an ERC-20 (e.g. USDC):
approvethe Marketplace foramount, then callpaywithmsg.value = 0.Paying in native ETH (voucher
tokenis the zero address): callpaywithmsg.value = amount.
Using viem:
Step 5 — Track the order to completion
Once payment is observed on-chain, the order advances from VOUCHER_SIGNED → PAID → COMPLETED. Poll the order until it settles:
When status is COMPLETED, the name token has been minted to the buyer wallet. You can view it on the block explorer or query it via the Doma Multi-Chain Subgraph.
Renewing a domain
Renewing an existing name reuses the same order → pay → track machinery, with two simplifications:
No contact step. The registrant on file at the registry is unchanged, so renewals need no email verification and no
registrantHandle.No availability search. The name is already registered, so skip
availableDomainsand price it directly with aRENEWALquote.
You don't have to own the name to renew it — anyone can pay to extend a registration. The buyer wallet is just the payer; the renewal extends the existing registration without changing its registrant.
1. Get the renewal quote
2. Create the order
Identical to Step 3, but set the item type to RENEWAL and omit registrantHandle:
Choose years within the registrar's minYears–maxYears (some TLDs enforce a minimum renewal term).
3. Pay and track
Pay the returned voucher and poll the order exactly as in Step 4 and Step 5. When the order reaches COMPLETED, the name's expiry has been extended.
Going to mainnet
Swap the endpoint, chain, RPC, and your API key to the mainnet values in Network details, fund the wallet with real ETH/USDC, and use a registrant contact you control. Everything else is identical.
Reference
Authentication and Rate Limits
Doma Network Information
Doma Smart Contracts API
Doma Multi-Chain Subgraph
Last updated