For the complete documentation index, see llms.txt. This page is also available as Markdown.

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:

  1. Search & price the name (availableDomains, domainPricing).

  2. Get a registrantHandle by uploading WHOIS contact details — either through Doma's email verification or, with a privileged key, a pre-verified upload (ICANN requires verified WHOIS contacts).

  3. Create an order (createOrder) — Doma prices it and returns a signed payment voucher.

  4. Pay on-chain by calling pay(voucher, signature) on the Marketplace contract.

  5. 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).

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-Key header 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

Testnet
Mainnet

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:

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:

Step 3 — Create the order

Create the order. Doma prices the registration and returns a signed payment voucher.

Example input:

  • buyer is a CAIP-10 address — the network prefix plus your wallet. It receives the name token.

  • selectedPaymentTokenAddress selects the payment currency. Set it to the USDC token address to pay in USDC, or omit it to pay in native ETH.

  • selectedPaymentPrice and couponCode are optional.

The voucher field is a JSON string with the shape:

USDC amounts map 1:1 to the USD price ($12.4912490000 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): approve the Marketplace for amount, then call pay with msg.value = 0.

  • Paying in native ETH (voucher token is the zero address): call pay with msg.value = amount.

Using viem:

Step 5 — Track the order to completion

Once payment is observed on-chain, the order advances from VOUCHER_SIGNEDPAIDCOMPLETED. 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 availableDomains and price it directly with a RENEWAL quote.

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 minYearsmaxYears (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

Last updated