# Doma Protocol

## Introducing Doma

The Doma Protocol introduces a novel approach to domain tokenization by creating a standardized bridge between traditional domain registrars and blockchain technology. By establishing a single integration point through Doma Chain, we eliminate the fragmentation that has historically prevented domains from being truly portable across different platforms and ecosystems.

On the customer side, the domains are now fully on-chain and available across all supported blockchains. This means both users and developers have a standard way of interacting with their domains across all registrars.

Unlike previous approaches to blockchain domains (such as [alt-roots](https://en.wikipedia.org/wiki/Alternative_DNS_root) or single registrar tokenization), Doma works *with* the existing DNS infrastructure rather than replacing it. Every domain tokenized through Doma remains fully DNS-compliant, working exactly like a traditional .com or .ai domain while gaining programmable on-chain functionality and ownership models.

### Bridge between Web2 and Web3

Doma Protocol occupies a unique position in the domain ecosystem, serving as the **standardized bridge** between traditional domain registrars and blockchain technology. Rather than replacing existing infrastructure, Doma integrates with it. Registrars continue managing their customer relationships and DNS operations while gaining access to new blockchain-powered capabilities.

The protocol establishes a single integration point through **Doma Chain**, a blockchain purpose-built for domain operations. This architecture eliminates the fragmentation that has historically prevented domains from being portable across platforms and blockchain ecosystems.

<figure><img src="/files/oAqcGaymmvFQxlUtQltZ" alt=""><figcaption></figcaption></figure>

### Why Registrars Are Central

Doma Protocol is designed around registrars for fundamental reasons:

1. **Exclusive Customer Relationship:** Registrars are the only entities that interact directly with end users.
2. **Sales Authority:** All domain registrations and transfers must flow through accredited registrars.
3. **Usage Control:** Registrars are responsible for domain usage, DNS configuration, and ICANN compliance.
4. **Trust Layer:** Registrars provide verification and compliance for legitimate domain tokenization.

## Three Layer Architecture

Doma's architecture consists of three interconnected layers:

<figure><img src="/files/FfjPokFdXLehk0FfzcsK" alt=""><figcaption></figcaption></figure>

{% stepper %}
{% step %}

#### Registrar Bridge and Toolkit

APIs and integration tools that allow registrars to connect their existing systems with Doma Chain. This includes event synchronization, compliance modules, and blockchain bridges. Registrars synchronize domain events bidirectionally. Actions on-chain update registrar systems, and registrar actions update onchain state.
{% endstep %}

{% step %}

#### Domain Web3 Infrastructure

Doma Chain serves as the authoritative record for all tokenized domains, maintaining a consistent state across multiple blockchains using ERC7786 cross-chain messaging. It provides SVM and EVM compatibility while optimizing for domain-specific operations.
{% endstep %}

{% step %}

#### On-Chain Domain Capabilities

Smart contracts enable domain tokenization, synthetic token issuance, and DomainFi applications across supported blockchains, including Base, Solana, Avalanche, and Ethereum.
{% endstep %}
{% endstepper %}

## Integrate Once, Distribute Everywhere

For registrars, Doma functions as an "integrate once, distribute everywhere" layer. Domains remain registered through traditional DNS infrastructure but gain tokenized representations that can be traded, collateralized, or bridged across chains. Discovery and settlement happen on-chain while Doma orchestrates registrar updates through purpose-built APIs.


# Protocol Overview

## Introducing Doma

The Doma Protocol introduces a novel approach to domain tokenization by creating a standardized bridge between traditional domain registrars and blockchain technology. By establishing a single integration point through Doma Chain, we eliminate the fragmentation that has historically prevented domains from being truly portable across different platforms and ecosystems.

On the customer side, the domains are now fully on-chain and available across all supported blockchains. This means both users and developers have a standard way of interacting with their domains across all registrars.

### Bridge between Web2 and Web3

Doma Protocol occupies a unique position in the domain ecosystem, serving as the **standardized bridge** between traditional domain registrars and blockchain technology. Rather than replacing existing infrastructure, Doma integrates with it. Registrars continue managing their customer relationships and DNS operations while gaining access to new blockchain-powered capabilities.

The protocol establishes a single integration point through **Doma Chain**, a blockchain purpose-built for domain operations. This architecture eliminates the fragmentation that has historically prevented domains from being portable across platforms and blockchain ecosystems.

<figure><img src="/files/oAqcGaymmvFQxlUtQltZ" alt=""><figcaption></figcaption></figure>

### Why Registrars Are Central

Doma Protocol is designed around registrars for fundamental reasons:

1. **Exclusive Customer Relationship:** Registrars are the only entities that interact directly with end users.
2. **Sales Authority:** All domain registrations and transfers must flow through accredited registrars.
3. **Usage Control:** Registrars are responsible for domain usage, DNS configuration, and ICANN compliance.
4. **Trust Layer:** Registrars provide verification and compliance for legitimate domain tokenization.

## Three Layer Architecture

Doma's architecture consists of three interconnected layers:

<figure><img src="/files/FfjPokFdXLehk0FfzcsK" alt=""><figcaption></figcaption></figure>

{% stepper %}
{% step %}

#### Registrar Bridge and Toolkit

APIs and integration tools that allow registrars to connect their existing systems with Doma Chain. This includes event synchronization, compliance modules, and blockchain bridges. Registrars synchronize domain events bidirectionally. Actions on-chain update registrar systems, and registrar actions update onchain state.
{% endstep %}

{% step %}

#### Domain Web3 Infrastructure

Doma Chain serves as the authoritative record for all tokenized domains, maintaining a consistent state across multiple blockchains using ERC7786 cross-chain messaging. It provides SVM and EVM compatibility while optimizing for domain-specific operations.
{% endstep %}

{% step %}

#### On-Chain Domain Capabilities

Smart contracts enable domain tokenization, synthetic token issuance, and DomainFi applications across supported blockchains, including Base, Solana, Avalanche, and Ethereum.
{% endstep %}
{% endstepper %}

## Integrate Once, Distribute Everywhere

For registrars, Doma functions as an "integrate once, distribute everywhere" layer. Domains remain registered through traditional DNS infrastructure but gain tokenized representations that can be traded, collateralized, or bridged across chains. Discovery and settlement happen on-chain while Doma orchestrates registrar updates through purpose-built APIs.


# How the Internet Works Today

## 🌐 The Domain Name System (DNS)

The Domain Name System (DNS) serves as the Internet's foundational directory service, translating human-readable domain names like "[example.com](http://example.com/)" into the numerical IP addresses that computers use to locate and communicate with each other. When a user enters a domain into their browser, DNS servers initiate a hierarchical query process that resolves the domain name to its corresponding IP address within milliseconds.

This resolution process involves multiple coordinated layers:

1. **Root Servers:** The authoritative starting point for DNS queries, directing requests to appropriate TLD servers.
2. **TLD Servers:** Servers responsible for top-level domains (.com, .org, .ai, .xyz) that point queries to specific registrar nameservers.
3. **Authoritative Nameservers:** Typically maintained by registrars, these servers hold the actual DNS records mapping domains to their destinations.

## Domain Ecosystem

<figure><img src="/files/qEicj2bi86LWTO15gz5B" alt=""><figcaption></figcaption></figure>

### ICANN (Internet Corporation for Assigned Names and Numbers)

ICANN serves as the central coordinating body for the internet's naming system. It oversees the allocation of IP addresses, manages top-level domains, accredits registrars, and contracts with registries. ICANN itself does not sell domains directly but establishes the policies and standards that ensure the stability, security, and global interoperability of the DNS.

### Registries

Registries manage the technical infrastructure for specific Top-Level Domains (TLDs). For example, Verisign operates the registry for .com and .net domains. Key responsibilities include:

* Maintaining the master database of all registrations within their TLD
* Operating at the wholesale level
* Required to treat all accredited registrars equally (preventing monopolistic practices)
* Ensuring policy consistency across all registrars

## Registrars

Registrars are the customer-facing entities that sell domains directly to end users. Companies like Interstellar, Namecheap, InterNetX, NicNames, and thousands of others worldwide must obtain ICANN accreditation to operate. This separation of responsibilities ensures that end users can freely transfer between registrars without losing domain ownership, fostering competition and innovation.

By taking on the responsibility for maintaining solid and reliable internet infrastructure at the registry level, this division of labor effectively frees up Registrars to concentrate their efforts and resources on distribution, marketing, and customer service. Additionally, this structural arrangement serves as an important safeguard that prevents Registries from engaging in monopolistic practices or exerting undue control over the market, as they are required to treat all accredited registrars equally and fairly.

## Domain Structure

Every domain name consists of distinct components:

<figure><img src="/files/8vFWyo20KacXCEYa9HmZ" alt=""><figcaption></figcaption></figure>

<table data-header-hidden><thead><tr><th width="246.69140625">Component</th><th>Description</th></tr></thead><tbody><tr><td><strong>TLD (Top-Level Domain)</strong></td><td>The extension (.com, .ai, .xyz) — managed by registries</td></tr><tr><td><strong>SLD (Second-Level Domain)</strong></td><td>The main identifier (e.g., "doma" in doma.xyz) — owned by the end user</td></tr><tr><td><strong>Subdomains</strong></td><td>Optional prefixes (e.g., "docs" in docs.doma.xyz) — controlled by the end user</td></tr></tbody></table>

## Limitations of the Current System

Despite its robust architecture, the traditional domain ecosystem faces significant challenges:

* **Illiquidity:** The secondary domain market remains highly fragmented. In 2024, only \~$185 million in domain resales were recorded across 144,700 transactions. High-value domains require weeks-long escrow processes and broker intermediaries.
* **High Barriers to Entry:** ICANN accreditation requires significant capital, technical infrastructure, and compliance overhead—preventing new market participants from facilitating domain transactions.
* **Lack of Programmability:** Domains exist as static assets without standardized developer interfaces. There's no way to standard way to programmatically interact with domain capabilities across registrars, requiring costly individual one-off integrations.
* **Opaque Pricing:** Valuations are determined through private negotiations rather than transparent market mechanisms.
* **Lack of Standardization**: Each registrar has their own sandbox and proprietary APIs/standards. This means even standard mechanisms like transfers are completely custom and depend on registrar implementation


# Glossary

## Domains

A domain (or domain name) is a human-readable address traditionally used to access websites on the internet. However, with the rise of blockchain and web3, domains can also be used as human-readable identifiers for crypto wallet addresses without users having to memorize a complex string of alphanumerics.

Example:

* Domain Name: example.com
* Resolves to IP Address: 192.0.2.1
* Resolves to wallet address: 0x742d35Cc6634C0532925a3b844Bc454e4438f44e

Structure of a Domain Name:

* Top-Level Domain (TLD): The extension at the end (e.g., .com, .org, .net, .gov).
* Second-Level Domain (SLD): The main part of the domain (e.g., example in example.com).
* Subdomain (Optional): A prefix added before the main domain (e.g., blog.example.com).

Domains are purchased and registered through an ICANN-accredited registrar or website using reseller APIs provided by an ICANN-accredited registrar.

Domains can be registered for 1 to 10 years at a time (which the exception of some ccTLDs such as .uk, .de, .ca which have different maximum registration periods). Domains can continue to be renewed indefinitely as long as registration fees are paid.

Registrars are required to collect contacts details of the domain registrant to satisfy ICANN regulations. Contact info typically includes PII such as Full lName, Email Address, Phone Number and Mailing address. Most registrars offers free privacy protection so this contact information is redacted in public records such as WHOIS and RDAP.

***

## Registrants

A registrant is the legal owner of a domain name. This can be an individual, business, or organization that registers and holds the rights to a domain.

Registrant Responsibilities include:

* Maintaining Ownership: The registrant must ensure the domain is renewed on time.
* Updating Contact Information: Keeping registrant details accurate to avoid suspension.
* Managing Domain Settings: Configuring DNS, email, website settings, and wallet mappings.
* Transferring or Selling the Domain: The registrant has the right to transfer the domain to another owner or registrar.

***

## Registrars

A registrar is a company accredited to sell, register, and manage domain names on behalf of individuals and businesses. Registrars act as intermediaries between registrants (domain owners) and registries (organizations that manage TLDs like .com or .org).

Role of a Registrar:

* Domain Registration: Allows users to search, purchase, and register domain names.
* DNS Management: Provides tools to manage domain settings (e.g., pointing a domain to a website).
* WHOIS & Privacy Protection: Maintains registrant details and offers privacy protection services.
* Renewals & Transfers: Manages domain renewals, transfers between registrars, and expiration reminders.

***

## Registries

A registry is the organization responsible for managing and maintaining a Top-Level Domain (TLD), such as .com, .org, or country-code TLDs like .uk or .ca.

Role of a Registry:

* Maintains the official database of all registered domains under its TLD.
* Sets policies and pricing for domain registrations.
* Provides the infrastructure for domain name resolution (translating domain names to IP addresses).
* Works with registrars (like GoDaddy, Namecheap) to sell domains, but does not sell directly to consumers in most cases.


# Core Modules

Doma Protocol provides a comprehensive suite of APIs and smart contracts that enable ICANN-accredited registrars and registries to tokenize domains on the blockchain while maintaining full DNS compliance. Every tokenized domain continues to function exactly like a traditional .com or .ai domain while gaining programmable functionality on-chain.

<figure><img src="/files/QwbuMTV8jVJLtTPYU0i1" alt=""><figcaption></figcaption></figure>

## Registrar Modules

Registrar Modules are only available to ICANN accredited registrars that have gone through Doma Certification.

### Tokenization Module

When a domain is tokenized, a **Domain Ownership Token** is minted on the blockchain selected by the registrant. Through signature verification, Doma ensures domains can only be tokenized by the hosting registrar, preventing unauthorized tokenizations.

Each domain is represented as an ERC-721 NFT on EVM-compatible chains. All NFTs across all registrars and TLDs belong to a unified collection, with metadata including registrar information, expiration date, and TLD embedded as token traits.

### Compliance Module

Doma supports off-chain legal processes such as UDRP (Uniform Domain-Name Dispute-Resolution Policy), ensuring consistent governance across both traditional and blockchain environments. Enforcement mechanisms include:

* **Transfer Lock:** Prevents transfer of all associated tokens during active investigations
* **Forced Detokenization:** Burns all domain tokens, returning control to registrar for mandated transfers

### State Synchronization Module

The protocol implements **bidirectional synchronization** between on-chain assets and traditional DNS infrastructure:

* Domain lifecycle events (transfers, renewals, expirations) sync across chains and registrar systems
* DNS configuration changes propagate in real-time
* Consistent domain metadata across Web2 and Web3 environments

## Blockchain Modules

### Bridging Module

Leveraging **ERC-7786** for cross-chain interoperability, the Bridging Module enables Domain Ownership Tokens to move seamlessly across all supported L1 and L2 blockchains. Doma Chain maintains authoritative records, ensuring consistent states across networks.

### Custodian Module

The Custodian Module integrates domain registration with blockchain tokenization while maintaining ICANN compliance. It provides **Reseller APIs** enabling non-ICANN accredited entities to register and tokenize domains. When Domain Ownership Tokens transfer on-chain, the module notifies registrars to maintain regulatory compliance (since wallets don't inherently provide ICANN-required registrant information). It also offers a unified interface that Registrars can opt into for listing availability and pricing. This enables developers to discover optimal pricing for their customers and expands the distribution of Registrar inventory through new DomainFi applications.

When a Domain Ownership Token is transferred on-chain, signifying a change in domain ownership, the Custodian Module manages the compliance process. Since blockchain wallets don't inherently provide the valid Registrant contact information required by ICANN, the Registrar is notified of these transfer events and temporarily moves the domain to a Doma Proxy Registrant. This proxy acts as an escrow holder until the new wallet owner completes the claim process. The Module verifies the wallet's ownership of the Domain Ownership Token and securely collects the required Registrant information through off-chain storage. Once this information is verified, it's forwarded to the Registrar, which then completes the final transfer from the Doma Proxy to the new Registrant.

### Composer Module

The Composer Module transforms domains into programmable digital assets by enabling the decomposition of Domain Ownership Tokens into multiple Synthetic Tokens. Each System Token represents specific rights to the domain, creating granular control and new utility.

For example, a Domain Ownership Token can be divided into two distinct Synthetic Tokens:

* A Synthetic Token granting exclusive rights to control the domain's DNS settings
* A Synthetic Token retaining all other domain permissions and rights except NameServer management

When decomposition occurs, the Registrar receives notification of the change in permission structure. The original Registrant's access to the split permissions becomes restricted, and these permissions can only be exercised programmatically through Doma Record Contracts by the respective Synthetic Token holders.

This permission-based architecture enables developers to build innovative DomainFi applications that unlock the underlying value of domains through programmable smart contracts.

### Fractionalization Module

Enables partial ownership of tokenized domains by converting them into fungible ERC-20 tokens. These tokens are OFT (Omnichain Fungible Token) compliant for cross-chain portability. The module manages token tranches, distribution, vesting schedules, and buyout mechanisms.

## Registrar Integration Process

Registrars adopt Doma Protocol through a structured integration:

1. **API Integration:** Implement Doma's Registrar Toolkit to connect their existing domain management systems
2. **Event Configuration:** Configure domain events to synchronize on-chain (transfers, renewals, DNS changes)
3. **Compliance Setup:** Establish workflows for handling on-chain transfers and ICANN requirements
4. **Capability Exposure:** Optionally expose registrar-specific capabilities as programmable on-chain assets
5. **Multi-Chain Access:** Domains automatically become accessible across all supported blockchains


# DomainFi

<figure><img src="/files/yNKO8Hk2knL8fFJkE43s" alt=""><figcaption></figcaption></figure>

## Introduction

Doma Protocol is the world's first DNS compliant blockchain purpose-built to transform over 370 million traditional internet domains into programmable, tokenized real-world assets (RWAs). By bridging existing DNS infrastructure with major blockchain ecosystems,including Solana, Base, Avalanche, and Ethereum, Doma powers **DomainFi**: a new economic paradigm that unlocks liquidity, financial utility, and innovative ownership models for the $360+ billion domain industry.

Unlike previous approaches to blockchain domains (such as [alt-roots](https://en.wikipedia.org/wiki/Alternative_DNS_root) or single registrar tokenization), Doma works *with* the existing DNS infrastructure rather than replacing it. Every domain tokenized through Doma remains fully DNS-compliant, working exactly like a traditional .com or .ai domain while gaining programmable on-chain functionality and ownership models.

## What is DomainFi

DomainFi represents a new economic paradigm for the domain industry, addressing significant challenges in the current $340B+ domain ecosystem. Traditional domain management faces several critical limitations:

* The domain industry has historically maintained high barriers to entry through strict ICANN accreditation requirements
* Secondary market trading suffers from opacity and high friction, often requiring intermediary escrow services and long transfer times
* Domains lack programmability and standardized developer interfaces

> The Doma Protocol revolutionizes this landscape by transforming domains into programmable, blockchain-based assets.

This transformation enables DomainFi through several key innovations:

{% stepper %}
{% step %}
**Trusted Domain Tokenization**

* Provides a secure onramp for any Registrar or Registry to tokenize domains onto the blockchain
* Maintains full compliance with ICANN regulations
* Ensures seamless integration with existing domain infrastructure
  {% endstep %}

{% step %}
**State Synchronization**

* Implements bi-directional synchronization between on-chain assets and ICANN registries
* Maintains consistent domain metadata and state across web2 and web3 environments
* Provides real-time updates for domain state changes
  {% endstep %}

{% step %}
**Composable Domain Rights**

* Enables splitting domains into synthetic tokens representing specific rights and permissions
* Facilitates granular control over domain management capabilities
* Supports innovative financial instruments based on domain rights
  {% endstep %}

{% step %}
**DomainFi Application Infrastructure**

The protocol provides comprehensive APIs and smart contracts enabling developers to build innovative applications such as:

* Instant-settlement secondary marketplaces
* Fractional domain ownership structures
* Domain-collateralized lending platforms
* Automated domain rental and leasing systems
* On-chain domain parking yield generation
  {% endstep %}
  {% endstepper %}

Through these capabilities, DomainFi transforms traditional domains into dynamic, programmable assets that can participate in the broader web3 financial ecosystem.

***

## Why Build on Doma

The Doma Protocol creates substantial value for multiple stakeholders across the domain and blockchain ecosystems:

:office: **For Registrars and Registries**:

* **Secure Tokenization Infrastructure**: A fully compliant system for converting traditional domains into blockchain assets, with built-in ICANN requirement handling and automated compliance checks
* **Multi-Chain Integration**: Native support for major Layer1 and Layer2 blockchains, enabling registrars to offer domain tokenization across multiple popular networks without additional infrastructure
* **Enhanced Revenue Streams**: New revenue opportunities through tokenization services, domain rights tokenization, and integration with DomainFi applications
* **Automated State Management**: Streamlined handling of tokenized domains with automated synchronization between blockchain and traditional DNS infrastructure

:chains: **For Blockchain Ecosystems**:

* **Real-World Asset Integration**: Direct integration of the $300B+ domain market as on-chain assets, bringing significant real-world value onto blockchain networks
* **Transaction Volume Growth**: Increased network activity through DomainFi transactions, including trading, lending, and permission management
* **Cross-Chain Opportunities**: Enhanced interoperability through standardized domain asset handling across different blockchain networks
* **DomainFi Ecosystem Expansion**: New opportunities for dApps and protocols to incorporate domain-based financial products and services

:woman\_technologist: **For Developers**:

* **Reseller APIs:** Enable domain sales through NFT marketplaces and dApps without the need for ICANN accreditation.
* **Smart Contract Libraries**: Pre-built, audited smart contracts for common domain management operations
* **Flexible Permission System**: Programmable domain primitives enabling creative applications of domain rights and permissions
* **Subgraph Integration**: GraphQL-based subgraph for efficient querying of domain metadata and transaction history
* **Cross-Chain Development Tools**: Infrastructure for building applications that work seamlessly across multiple blockchain networks

:person\_in\_tuxedo: **For Domain Holders**:

* **Financial Utility**: Multiple options for leveraging domain value, including fractional ownership, collateralized lending, and yield generation
* **Enhanced Liquidity**: Instant access to secondary markets without traditional escrow services or lengthy transfer processes
* **Granular Permission Control**: Ability to split domain rights into specific permissions for different use cases while maintaining overall ownership
* **Web3 Integration**: Seamless integration with web3 applications, including use as digital identity and wallet resolution
* **Revenue Generation**: Multiple paths for generating revenue from domain assets through parking, leasing, and permission trading


# Programmable DNS

Programmable DNS represents a fundamental evolution in how domain functionality can be accessed and controlled. Traditional DNS provides static mappings between domain names and IP addresses, with configuration limited to whoever has registrar account credentials. **Programmable DNS extends this by exposing domain capabilities as on-chain primitives** that can be accessed, delegated, and composed through smart contracts.

### Domain Ownership Tokens

Domain Ownership Tokens are minted on a target chain specified by the Registrant during the domain tokenization process. These tokens grant the owner full ownership of the domain and transfer of the Ownership Token requires the new owner to go through the claim process specified above. Domain Ownership Tokens can be burned and split into multiple Synthetic Tokens for finer-grained access rights and utility within DomainFi apps.

### Domain Synthetic Tokens

Domain Synthetic Tokens represent specific management rights extracted from a Domain Ownership Token through a controlled decomposition process. Each Synthetic Token grants its holder authority over a particular domain function—such as DNS management or subdomain creation—without conferring full ownership. Synthetic Tokens can be independently traded on NFT marketplaces, used within DomainFi applications, or recombined to reconstruct the original Domain Ownership Token. The Doma Protocol maintains records of all Synthetic Tokens derived from each domain, ensuring that permissions remain mutually exclusive and collectively represent full ownership rights of the domain.

<figure><img src="/files/pCvU3yvBXbkojArR2Yg4" alt=""><figcaption></figcaption></figure>

## Domain Capabilities

Domains are inherently composed of **utility** and **ownership**. Through Doma, these can be decomposed into discrete, programmable capabilities.

Some examples of domain capabilities include:

<table data-header-hidden><thead><tr><th width="240.50390625">Capability</th><th>Description</th></tr></thead><tbody><tr><td><strong>DNS Record Management</strong></td><td>Control over A records, CNAME records, MX records, TXT records, and other DNS configurations</td></tr><tr><td><strong>Subdomain Creation</strong></td><td>Authority to create and manage subdomains under the parent domain</td></tr><tr><td><strong>Content Routing</strong></td><td>Control over where the domain resolves and what content it serves</td></tr><tr><td><strong>Email Configuration</strong></td><td>Management of mail server records and email routing</td></tr><tr><td><strong>Renewal Authority</strong></td><td>Rights to renew the domain registration</td></tr><tr><td><strong>Registry Records</strong></td><td>Control over parent zone (Registry Owned Zone)</td></tr><tr><td><strong>Records Update</strong></td><td>Control over NS and DS Records</td></tr></tbody></table>

## Domain Synthetic Tokens

**Domain Synthetic Tokens** represent specific management rights extracted from a Domain Ownership Token through a controlled decomposition process. When a domain owner wishes to delegate specific capabilities, the domain NFT is locked, and synthetic tokens representing those capabilities are issued.

## Onchain DNS

These capabilities can be exposed through Domain Ownership Tokens or Synthetic Tokens via smart contracts, enabling programmatic access.

For example, a smart contract can be granted permission to update DNS records based on predefined conditions, or a DAO can collectively manage subdomain allocation for a community-owned domain. This programmability enables entirely new use cases such as conditional DNS routing, automated failover systems, and dynamic content delivery based on onchain events.

For example, an ENS claim typically requires visiting your registrar to set up DNS entries, then completing an on-chain claim. Programmable and onchain DNS eliminates this friction. Users can create the required DNS entries and complete all claims fully on-chain, enabling all registrars to offer a seamless, programmable ENS claim flow.

Similarly, subdomains issued through staking use the domain ownership token to generate synthetic subdomain tokens for users. These subdomain tokens give users full control over their subdomain DNS, including ENS claims.

This programmable approach transforms domains from static assets into dynamic, composable primitives that can power sophisticated DomainFi applications while maintaining full DNS compliance.

## Fully Onchain Ownership

Key properties of Owner Synthetic Tokens:

* **Independent Trading:** Can be traded on NFT marketplaces or used in DomainFi applications
* **Recombination:** Can be recombined to reconstruct the original Domain Ownership Token
* **Mutual Exclusivity:** The protocol ensures permissions remain mutually exclusive
* **Collective Completeness:** Together, all synthetic tokens represent full ownership rights

## Why Programmable DNS is Transformative

Programmable DNS fundamentally transforms domains from static digital real estate into **dynamic, composable digital assets**:

1. **Granular Access Control:** Delegate specific capabilities without surrendering ownership. A company can grant a marketing agency subdomain rights while retaining DNS management.
2. **Smart Contract Integration:** Domain capabilities can be programmatically accessed, enabling automated management and DeFi integration.
3. **Cross-dApp Interoperability:** Use domains across all compatible dApps regardless of registrar.
4. **Revenue Generation:** Monetize specific capabilities—lease subdomains, sell DNS rights, create yield positions.
5. **Composability:** Domain capabilities become building blocks for novel applications.
6. **Security**: An additional layer of onchain cryptographic verification (for example, using multi-sigs) to do any domain operations.


# Unique Ownership Models

## Beyond Traditional Domain Ownership

Traditional domain ownership follows a simple model: one registrant owns and controls the domain entirely. Doma Protocol enables **entirely new ownership paradigms** that have never existed in the domain industry, unlocking financial utility and accessibility for a previously illiquid asset class.

## Shared Ownership

The launchpad contracts, along with the composer module, enable shared ownership by converting domains into fungible ERC-20 tokens:

* **Accessible Investment:** Premium domains worth $100K+ become accessible through fractional shares
* **Transparent Price Discovery**: True market driven price discovery can be achieved.
* **Liquidity Without Selling:** Access capital without relinquishing valuable domain assets
* **Portfolio Management:** Trade domain tokens like other fungible assets
* **DeFi Integration:** Fractional tokens can participate in liquidity pools and lending protocols

## Rights-Based Ownership

Through synthetic tokens, ownership can be decomposed by capability rather than percentage. Different parties can own different rights to the same domain:

* One entity owns subdomain creation rights
* Another owns DNS management capabilities
* A third owns email configuration authority
* Primary ownership token holder retains renewal and transfer rights


# Shared Ownership: Liquid Domains

The Doma Launchpad exemplifies the power of combining programmable DNS with tokenization, enabling shared ownership and subdomain access through staking. The Launchpad transforms how domains come to market, introducing true market pricing and fractional accessibility.

## Why Liquid Domains?

The Liquid Domains concept addresses fundamental limitations:

1. **True Market Pricing:** Valuations through transparent on-chain price discovery, not opaque negotiations
2. **Decomposed Value:** Premium domain value broken into accessible units
3. **Expanded Market Access:** New ways to own, trade, and access domains

<figure><img src="/files/NTWSJVdRwvt1vqNvYnCG" alt=""><figcaption></figcaption></figure>

### Key Features

* Configurable: Not all domains are created equal. This allows owners to configure how they bring their names onchain.
* **Transparent:** Every parameter is on-chain and immutable
* **Fair Price Discovery:** Market participants determine pricing through bonding curve mechanisms
* **Immediate Liquidity:** Seamless transition from price discovery to AMM trading

### Criteria

1. Ensure market participates in initial pricing. The domain shouldn’t come on the market if there is not enough demand.
2. Align people who provide initial liquidity with longer-term success (lower entry price). This means early participants, who take the most risk, get the best pricing.
3. The owner must seed liquidity to value the market pricing. This ensures given the market participation, there is a liquid market post bonding.

## Launch Mechanism

The Launchpad consists of integrated components:

* **Token Factory:** Creates ERC-20 tokens (OFT compliant) representing fractional ownership
* **Distribution Manager:** Configures launch parameters, manages fees, orchestrates AMM and vesting
* **Asset Vault:** Holds locked domain NFT and manages buyout mechanisms
* **Rights Access Manager:** Handles rule-based issuance of domain capabilities like subdomains
* **AMM**: Continuous Market Pricing and Liquidity

<figure><img src="/files/ML91fysGSUVu5OHjWMDp" alt=""><figcaption></figcaption></figure>

### Launch Process

#### Phase 1: Domain Locking

The domain owner locks their NFT into the Launchpad contract and proposes a buyout price as a show of good faith. The domain cannot be unlocked unless this price is paid. This creates **three token tranches**:

* **Initial Sale Supply:** Sold during the bonding curve auction
* **Migration to AMM Supply:** Deployed to the liquidity pool
* **Vested Supply:** Released over time to the domain owner

The domain owner can configure most params including supply pricing, vesting etc during this step.

#### Phase 2: Initial Price Discovery

The owner proposes a pricing range for initial price discovery. The Initial Sale Supply is offered at this range, which can be flat or follow a curve where **early participants receive better pricing** for taking early risk. Buyers purchase with USDC.

If the entire Initial Sale Supply sells within the specified range, the domain is considered "**bonded”,** proving sufficient market demand. If bonding fails within the required timeframe, the domain returns to the owner, and buyers receive refunds.

#### Phase 3: AMM Migration

Upon successful bonding, at least 50% of the raised liquidity plus the Migration Supply is deployed to a **Uniswap V3 pool**. The position uses a full spread to allow continued price discovery and is locked for a minimum of one year. Domain owners can choose to migrate more supply or provide a longer lock-up on their LP to build trust. The AMM enables continuous market pricing, with trading fees flowing to the domain owner and liquidity providers.

#### Phase 4: Vesting and Continuous Trading

Vesting begins for the Vested Supply, typically following **a 7 day cliff and daily vesting over one year**. This ensures the domain owner has access to their tokens, while also protecting initial buyers.

<figure><img src="/files/jx4ACepWASb0ztZ5JSvP" alt=""><figcaption></figcaption></figure>

### Buyer and Seller Success

* **Better Pricing**: Domain must successfully "bond" (sell all initial supply) to proceed. This ensures a poorly priced domain isn’t brought on the market. This ensures the seller proposes good initial pricing, and enough buyers agree to that pricing.
* **Seed Liquidity**: 50% of initial liquidity automatically migrates to AMM with Migration Supply. This ensures a healthy liqudity pool, which kick starts trading.
* **Protect Buyers**: Seller’s LP position locked for at least 1 year to ensure initial liquidity stability
* **Controlled Growth**: Vesting starts after bonding with typically a 7-day cliff + 1-year daily schedule. This means more supply can come on the market without large tranches being unlocked.
* **Skin in the game**: Buyout always protected by floor price or current market FDV. This means everyone who owns tokens has skin in the game to make the domain succeed.

## The Buyout Mechanism

Because the domain is continuously priced on the market, a transparent buyout mechanism exists.

### Features

* **Dynamic Buyout Price:** MAX(Original Floor Price, TWAP, Current AMM Price) allows the domains to increase in value based onchain activity, while still providing a floor
* **Simple Acquisition:** Buyer deposits buyout amount in USDC, domain transfers to buyer. There is no voting or wait period.
* **Shared Upside:** Exit funds distributed proportionally to all token holders. If you own 1% of supply, you get 1% of the buyout.
* **Token Credit:** If the buyer already holds tokens, buyout costs are reduced by the percentage owned. But the other holders must be paid the fair buyout price.

<figure><img src="/files/ceeAtNiwrf7KzI64gaFH" alt=""><figcaption></figcaption></figure>

## Shared Ownership via Staking

A unique feature of Liquid Domains is the ability to **access subdomains through token staking**. Token holders lock tokens to receive synthetic NFTs, granting subdomain rights:

1. Token holder requests specific subdomain (e.g., [user.premium.com](http://user.premium.com))
2. Required token amount staked in Rights Access Manager
3. Synthetic NFT representing subdomain rights minted to holder
4. Holder gains DNS control over their subdomain
5. Tokens remain staked for the duration of subdomain ownership

This mechanism aligns subdomain usage with token ownership, creating utility value beyond speculative trading while maintaining fractional ownership integrity.


# How to tokenize a domain

This guides provides a step-by-step guide on how to use Doma Protocol for domain tokenization. [Testnet Registrar](https://testnet.interstellar.xyz/) is provided by D3 to test the protocol. However, registrar-specific steps can be performed on any registrar that supports the Doma Protocol (exact flow may vary between registrars).

1. Navigate to [https://testnet.interstellar.xyz](https://testnet.interstellar.xyz/) and Login/Register.
2. Search for an `.io` name:<br>

   <figure><img src="/files/Z6oClzNhMxwHi1AaMOF2" alt=""><figcaption></figcaption></figure>
3. Add it to cart and purchase. 2 options for purchase are available:<br>

   1. Fiat. Any Stripe [test credit card](http://docs.stripe.com/testing#cards) can be used, no real money required. For example, `4242424242424242`.
   2. Crypto. Testnet tokens on a supported payment chain are required.

   <figure><img src="/files/kc3l67AiFsAUmpWBw0s4" alt=""><figcaption></figcaption></figure>
4. Once the purchase is completed, navigate to the Portfolio and click Tokenize Domain:

   <figure><img src="/files/tnCNrC89ALwewa4F8qNf" alt=""><figcaption></figcaption></figure>
5. Choose a target chain for tokenization:<br>

   <figure><img src="/files/bLKUe2vLEhEld2mM2tzJ" alt=""><figcaption></figcaption></figure>
6. Sign transaction using your wallet to confirm tokenization. You might need to connect your wallet, if you registered without it. This will initiate tokenization process:

   <figure><img src="/files/wvST3fvvXvZzmIFCBgV8" alt=""><figcaption></figcaption></figure>
7. Tokenization process might take several minutes. Once it's tokenized, clicking on `Tokenized` link will show token on blockchain explorer.<br>

   <figure><img src="/files/nrUWEJDPzLOyGAZiy6UB" alt=""><figcaption></figcaption></figure>

   <figure><img src="/files/D1W8yFntXiea9Lr1Fjse" alt=""><figcaption></figcaption></figure>
8. Once name it's tokenized, it can be managed on [Doma Dashboard](https://dashboard-testnet.doma.xyz/), which shows tokenized names from all registrars on Doma Protocol. First of all, login into dashboard using your email address.<br>

   <figure><img src="/files/XkFrr13hwyBSS852rsCD" alt=""><figcaption></figcaption></figure>
9. Link wallet that has been used for tokenization on Step 6.<br>

   <figure><img src="/files/c7Psbovy3ZkfxfU0nrdl" alt=""><figcaption></figcaption></figure>
10. After wallet is linked, tokenized domain will be visible in portfolio:<br>

    <figure><img src="/files/tocfA0HoCpfEprPbS3hD" alt=""><figcaption></figcaption></figure>

### (Optional) Bridge Name Token to another chain

Name token can be moved to another chain using Doma Dashboard. This is useful to access liquidity or apps that are available only on one of the supported chains.

1. Select a domain you want to bridge and click a "Bridge" button.<br>

   <figure><img src="/files/olDGroNgU0EqhFm0JXw1" alt=""><figcaption></figcaption></figure>
2. Select target chain and wallet address<br>

   <figure><img src="/files/hDEjeHhCRqTY1lZ7J6nq" alt=""><figcaption></figcaption></figure>
3. Confirm bridge and wait for it to complete. "Bridge In-progress" status will be displayed:<br>

   <figure><img src="/files/ByWL8cd2J4aiTBrotJYy" alt=""><figcaption></figcaption></figure>

### (Optional) Claim Domain after transferring

When name token is transferred on-chain, Domain is put into a temporary custody until a new owner can provide their contacts information. Process of providing this contact information is called a Domain Claim, and is required for obtaining control over a domain on a Registrar app.

{% hint style="info" %}
To trigger a claim process, simply transfer NFT to another wallet. This can be done manually, or by listing/buying on some NFT marketplace (like OpenSea or MagicEden).
{% endhint %}

1. Make sure you have an account on a Registrar app, that's tied to your Doma Dashboard user account email.<br>

   <figure><img src="/files/JlYCVnvFG9enNEU4KjPZ" alt=""><figcaption></figcaption></figure>
2. Find a domain you want to claim in Portfolio and click a "Claim" button.<br>

   <figure><img src="/files/JlYCVnvFG9enNEU4KjPZ" alt=""><figcaption></figcaption></figure>
3. Provide your contacts information and sign a transaction.<br>

   <figure><img src="/files/iG0Chtm5FAdUJkkJnIct" alt=""><figcaption></figcaption></figure>
4. Wait for the claim request be approved or rejected by a registrar. While in progress, "Claim Requested" status is displayed:<br>

   <figure><img src="/files/bebyyNUdYaw8Pm6DIteY" alt=""><figcaption></figcaption></figure>


# Building on Doma

This guide outlines the steps to start building on Doma Protocol:

1. Tokenize your first domain ([this guide will help](/getting-started)).
2. Create API keys using [Doma App](https://app.doma.xyz/account/developers) `Developers` page.
3. Get familiar with Doma APIs (choose the best one for your use case):
   1. [Doma Subgraph](/api-reference/doma-multi-chain-subgraph) - easiest way to access Doma Protocol data.
   2. [Poll API](/api-reference/poll-api) - if you need to react to events or build your own database.
   3. [Doma Marketplace](/doma-marketplace) - if you want to trade tokenized domains.
   4. [Smart Contracts API](/api-reference/doma-smart-contracts-api) - if you need low-level no-middlemen access to Doma Protocol.
4. Join [our Discord](https://discord.com/invite/doma) to ask questions, provide suggestions, or report bugs.


# Doma Marketplace

<figure><img src="/files/MyW3m3PBt4eQBdu3GyUe" alt=""><figcaption></figcaption></figure>

Doma Marketplace is provided to simplify trading of tokenized names on-chain. It has following components:

* [Orderbook Rest API](/api-reference/orderbook-api) for on-chain trading using [SeaPort protocol](https://github.com/ProjectOpenSea/seaport).
* [Poll API](/api-reference/poll-api) for marketplace-related events.
* Marketplace-related data (listings, offers) in [Doma Subgraph](/api-reference/doma-multi-chain-subgraph).
* Syndicated listings and offers from external marketplaces (OpenSea).
* [`@doma-protocol/orderbook-sdk`](https://www.npmjs.com/package/@doma-protocol/orderbook-sdk) to simplify integrations of Doma Marketplace.

### How to Use Orderbook API

* It's highly recommended to use [`@doma-protocol/orderbook-sdk`](https://www.npmjs.com/package/@doma-protocol/orderbook-sdk) to interact with [Orderbook API](/api-reference/orderbook-api), since it abstracts underlying complexities of working with Seaport Protocol, granting on-chain approvals, and computing fees.
* When not using [`@doma-protocol/orderbook-sdk`](https://www.npmjs.com/package/@doma-protocol/orderbook-sdk), [`seaport-js`](https://github.com/ProjectOpenSea/seaport-js) library is recommended (which is used by SDK under the hood).

### Marketplace Fees

Doma Orderbook API enforces inclusion of following fees into consideration items:

* Doma Protocol Fee
  * Receiver Address:
    * Testnet: `0x2E7cC63800e77BB8c662c45Ef33D1cCc23861532`
    * Mainnet: `0x5f1F2b4ad2E3a7587276B226c9B58Cb9227177bB`
  * Percentag&#x65;*: 0.5%*
* Name Token Royalties. Can be fetched by calling [`royaltyInfo`](https://eips.ethereum.org/EIPS/eip-2981) method on an [Ownership Token Smart contract](/api-reference/doma-smart-contracts-api#ownership-token-contract).
* OpenSea Fee (only when creating a listing/offer on OpenSea orderbook). Collection fee value can be fetched using [Get Collection API](https://docs.opensea.io/reference/get_collection) (required fee values from response should be included).
  * Since OpenSea API will also include royalty items, they should be filtered out to prevent double inclusion into considerations.

To simplify fees calculation, [Fee Information API](/api-reference/orderbook-api#get-v1-orderbook-fee-orderbook-chainid-contractaddress) is provided.

{% hint style="success" %}
When using [`@doma-protocol/orderbook-sdk`](https://www.npmjs.com/package/@doma-protocol/orderbook-sdk), fees are calculated automatically.
{% endhint %}

### Supported Currencies

Supported currencies can be fetched programmatically using [Currencies API](/api-reference/orderbook-api#get-v1-orderbook-currencies-chainid-contractaddress-orderbook).

{% hint style="success" %}
[`@doma-protocol/orderbook-sdk`](https://www.npmjs.com/package/@doma-protocol/orderbook-sdk) provides a helper [`getSupportedCurrencies`](https://www.npmjs.com/package/@doma-protocol/orderbook-sdk#user-content-get-supported-currencies) method, which returns list of all supported currencies.
{% endhint %}

### Making ETH Offers

Since SeaPort doesn't support making offers in ETH (as it's a native gas token, not an ERC-20), ETH should be wrapped to wETH using a wrapper contract. Supported wrapper contract addresses can be obtained using [Currencies API](/api-reference/orderbook-api#get-v1-orderbook-currencies-chainid-contractaddress-orderbook) - wrapper contracts have a `nativeWrapper` flag set in the response.

{% hint style="success" %}
When using [`@doma-protocol/orderbook-sdk`](https://www.npmjs.com/package/@doma-protocol/orderbook-sdk), wrapping is performed automatically.
{% endhint %}


# API Reference


# Authentication and Rate Limits

### Authorization

Doma APIs are protected using key-based authentication. To obtain API key, visit [Doma App Developers](https://app.doma.xyz/account/developers) page.

Key must be provided for each request in an `Api-Key` header.

### Rate Limits

API requests are limited to 10 requests per second. If you need higher limits, please contact [Doma Support](https://d3inc.atlassian.net/servicedesk/customer/portal/3).


# 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.

{% hint style="info" %}
**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.
{% endhint %}

### 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.

{% hint style="info" %}
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`).
{% endhint %}

{% hint style="success" %}
**Full example.** A complete, runnable Node.js implementation of this guide — `viem` + `graphql-request`, single file — is on GitHub: [**doma-register-example**](https://github.com/d3-inc/doma-register-example). It uses the email-verification path (Option A).
{% endhint %}

### Prerequisites

* **An API key** — create a testnet key at [app-testnet.doma.xyz/account/developers](https://app-testnet.doma.xyz/account/developers) (or a mainnet key at [app.doma.xyz/account/developers](https://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:

```bash
curl https://api-testnet.doma.xyz/graphql \
  -H 'Content-Type: application/json' \
  -H 'Api-Key: <YOUR_API_KEY>' \
  -d '{"query": "...", "variables": { }}'
```

{% hint style="warning" %}
API keys are per-network. A testnet key won't work against mainnet and vice-versa. Requests are rate-limited to 10/second.
{% endhint %}

### Step 1 — Check availability and price

Search for the name and confirm it's available:

```graphql
query AvailableDomains($name: String!) {
  availableDomains(name: $name, take: 10) {
    items {
      fullName   # e.g. "myname.com"
      tld        # "com"
      status     # AVAILABLE | OWNED | UNAVAILABLE
      price      # list price in USD
    }
  }
}
```

Then fetch the registration quote, which also tells you the registrar and the allowed term length:

```graphql
query DomainPricing($domains: [String!]!, $operation: QuoteOperation) {
  domainPricing(domains: $domains, operation: $operation) {
    fullName
    status
    registrars {
      registrarIanaId   # use this in createOrder
      registrarName
      available
      minYears
      maxYears
      registerPrice     # USD / year
      renewalPrice
    }
  }
}
```

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:

```graphql
mutation InitEmail($email: String!) {
  initiateEmailVerification(email: $email)
}
```

The registrant receives a 6-character code (valid 10 minutes). Submit it to get a verification **proof** (a token valid \~30 days):

```graphql
mutation CompleteEmail($email: String!, $code: String!) {
  completeEmailVerification(email: $email, code: $code)
}
```

Then upload the WHOIS contact together with the proof to receive the handle:

```graphql
mutation UploadContact(
  $contact: RegistrantContactInput!
  $emailVerificationProof: String!
  $networkId: String!
  $registrarIanaId: Int!
) {
  uploadRegistrantContacts(
    contact: $contact
    emailVerificationProof: $emailVerificationProof
    networkId: $networkId
    registrarIanaId: $registrarIanaId
  ) {
    proofOfContactsVoucher {
      registrantHandle
    }
  }
}
```

Example variables (the `contact.email` **must** match the address you verified):

```json
{
  "contact": {
    "name": "Ada Lovelace",
    "email": "ada@example.com",
    "phone": "+1.2025550100",
    "street": "123 Main Street",
    "city": "San Francisco",
    "state": "CA",
    "postalCode": "94105",
    "countryCode": "US"
  },
  "emailVerificationProof": "<proof from completeEmailVerification>",
  "networkId": "eip155:97476",
  "registrarIanaId": 3784
}
```

#### 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:

```graphql
mutation UploadVerifiedContact(
  $contact: RegistrantContactInput!
  $networkId: String!
  $registrarIanaId: Int!
) {
  uploadVerifiedRegistrantContacts(
    contact: $contact
    networkId: $networkId
    registrarIanaId: $registrarIanaId
  ) {
    proofOfContactsVoucher {
      registrantHandle
    }
  }
}
```

Example variables:

```json
{
  "contact": {
    "name": "Ada Lovelace",
    "email": "ada@example.com",
    "phone": "+1.2025550100",
    "street": "123 Main Street",
    "city": "San Francisco",
    "state": "CA",
    "postalCode": "94105",
    "countryCode": "US"
  },
  "networkId": "eip155:97476",
  "registrarIanaId": 3784
}
```

{% hint style="warning" %}
`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.
{% endhint %}

### Step 3 — Create the order

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

```graphql
mutation CreateOrder($input: CreateOrderInput!) {
  createOrder(input: $input) {
    __typename
    ... on CreateOrderSuccess {
      orderId
      totalPayment             # amount to pay, in the token's base units
      voucher                  # JSON PaymentVoucher (stringified)
      signature                # Doma's signature over the voucher
      paymentContractAddress   # the Marketplace contract to pay
      voucherExpiresAt
    }
    ... on CreateOrderValidationError {
      errors { domain reason }
    }
  }
}
```

Example `input`:

```json
{
  "input": {
    "buyer": "eip155:97476:0xYourWalletAddress",
    "domains": [{ "domain": "myname.com", "type": "REGISTRATION", "years": 1 }],
    "registrarIanaId": 3784,
    "registrantHandle": "<handle from step 2>",
    "selectedPaymentNetworkId": "eip155:97476",
    "selectedPaymentTokenAddress": "0x8725f6FDF6E240C303B4e7A60AD13267Fa04d55C"
  }
}
```

* `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:

```json
{
  "paymentId": "...",
  "orderId": "...",
  "buyer": "0x...",
  "token": "0x...",        // zero address = native ETH
  "amount": "12490000",     // base units (USDC has 6 decimals)
  "voucherExpiration": 1730000000
}
```

{% hint style="info" %}
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.
{% endhint %}

### 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](https://viem.sh):

```javascript
import { createWalletClient, createPublicClient, http, parseAbi, getAddress, zeroAddress, erc20Abi } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

const MARKETPLACE_ABI = parseAbi([
  'function pay((address buyer, address token, uint256 amount, uint256 voucherExpiration, string paymentId, string orderId) voucher, bytes signature) payable',
]);

const voucher = JSON.parse(order.voucher);
const token = getAddress(voucher.token);
const amount = BigInt(voucher.amount);
const isNative = token === zeroAddress;
const marketplace = getAddress(order.paymentContractAddress);

const voucherStruct = {
  buyer: getAddress(voucher.buyer),
  token,
  amount,
  voucherExpiration: BigInt(voucher.voucherExpiration),
  paymentId: voucher.paymentId,
  orderId: voucher.orderId,
};

// ERC-20 only: approve the Marketplace to pull `amount`.
if (!isNative) {
  const { request } = await publicClient.simulateContract({
    account, address: token, abi: erc20Abi, functionName: 'approve', args: [marketplace, amount],
  });
  await walletClient.writeContract(request);
}

// Pay.
const { request } = await publicClient.simulateContract({
  account,
  address: marketplace,
  abi: MARKETPLACE_ABI,
  functionName: 'pay',
  args: [voucherStruct, order.signature],
  value: isNative ? amount : 0n,
});
const txHash = await walletClient.writeContract(request);
```

### 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:

```graphql
query Order($orderId: String!) {
  order(orderId: $orderId) {
    status   # VOUCHER_SIGNED | PAID | COMPLETED | PARTIALLY_COMPLETED | FAILED
    items { sld tld status failureReason }
  }
}
```

When `status` is `COMPLETED`, the name token has been minted to the `buyer` wallet. You can view it on the [block explorer](https://explorer-testnet.doma.xyz) 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.

{% hint style="info" %}
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.
{% endhint %}

#### 1. Get the renewal quote

```graphql
query RenewalPricing($domains: [String!]!) {
  domainPricing(domains: $domains, operation: RENEWAL) {
    fullName
    registrars {
      registrarIanaId
      available        # true if the name can be renewed
      minYears
      maxYears
      renewalPrice     # USD / year
    }
  }
}
```

#### 2. Create the order

Identical to Step 3, but set the item `type` to `RENEWAL` and omit `registrantHandle`:

```json
{
  "input": {
    "buyer": "eip155:97476:0xYourWalletAddress",
    "domains": [{ "domain": "myname.com", "type": "RENEWAL", "years": 1 }],
    "registrarIanaId": 3784,
    "selectedPaymentNetworkId": "eip155:97476",
    "selectedPaymentTokenAddress": "0x8725f6FDF6E240C303B4e7A60AD13267Fa04d55C"
  }
}
```

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

* [Reference implementation: doma-register-example](https://github.com/d3-inc/doma-register-example)
* Authentication and Rate Limits
* Doma Network Information
* Doma Smart Contracts API
* Doma Multi-Chain Subgraph


# Doma Smart Contracts API

## Protocol Overview

Below is a brief overview of the Smart Contracts that form Doma Protocol.

<figure><img src="/files/e2MRd1Nar0GZfIhNWHAL" alt=""><figcaption></figcaption></figure>

* **Doma Record:** Main contract that holds information about a domain and issues Name Tokens. Serves as a coordination point for cross-chain operations. Exposes a Registrar-facing API that provides a full suite of operations to manage domains.
* **Doma Forwarder:** [EIP-2771](https://eips.ethereum.org/EIPS/eip-2771) Trusted Forwarder, that relays meta transactions from a Registrar to Doma Record contract. Optional, since Registrars can submit transactions directly to the Doma Record contract.
* **Doma Gateway:** [ERC-7786](https://erc7786.org/) Gateway Sourc&#x65;**,** deployed on each supported chain. This contract allows sending messages to contracts on other chains.
* **Proxy Doma Record:** Supporting contract, facilitates communication between users and contracts on each tokenization chain with the Doma Record contract. Used to abstract Doma Chain from end-users and provides core domain-management operations (like claiming and bridging).
* **Ownership Token:** Regular [ERC-721 ](https://eips.ethereum.org/EIPS/eip-721)NFT contract (or equivalent on non-EVM chains), with some modifications to support expiration and compliance operations:
  * Additional `expirationOf` function is provided to check expiration date. After expiration, token will become non-transferrable, and could either be renewed or deleted by the Registrar.
  * Additional `registrarOf` function is provided to get sponsoring Registrar's IANA ID.
  * Token could be burned by the Registrar, if conditions are met (domain is claimed by a current token owner).
  * Registrar retains the right to burn the token even if conditions are not met (domain is not claimed by a current token owner) for compliance reasons (e.g. in case of lost UDRP dispute over the domain).
  * Registrar can lock token transfer for compliance reasons (e.g. in case of UDPR dispute in progress).
  * [ERC-2981](https://eips.ethereum.org/EIPS/eip-2981) standard is used to configure royalties information.

## EVM-compatible chains

* [Proxy Doma Record Contract API](/api-reference/doma-smart-contracts-api/evm-proxy-doma-record-api) - documentation for user-facing methods.
* [Name Token Contract API](#ownership-token-contract) - documentation for non-standard methods of Ownership Token Contract.

## Solana

On Solana, Doma Protocol operates as Solana Records Service ([SRS](https://records.solana.com/)) program to issue and tokenize domains.

<figure><img src="/files/el8Brcdv47HwAoL6q1gk" alt=""><figcaption></figcaption></figure>

* Doma Protocol owns a Permissioned Class on the SRS program, which is used to issue and manage tokenized domains.
* [Token 22](https://spl.solana.com/token-2022) is used as an underlying NFT standard.
* SRS Program retains full control over minted NFTs (since it can sign on behalf of mint account, which has full authority delegation), so compliance operations are performed through SRS, using Proxy Doma Record PDA as a Class Authority to authorize operations.
* Doma Gateway exists as part of Proxy Doma Record Program.


# (EVM) Proxy Doma Record API

User-facing facet for the Proxy DOMA record contract on tokenization chains.

*This facet handles user interactions including tokenization requests, ownership claims, cross-chain bridging, and domain record management. It validates vouchers, manages fees, and integrates with EIP-712 for signature verification. Supports both ownership tokens and capability tokens with appropriate access controls.*

{% file src="/files/8LNnQE2vkOb1LFyVt982" %}

## Structs

#### TokenizationVoucher

Tokenization voucher, obtained from a Registrar.

**Parameters**

| Name         | Type                           | Description                                    |
| ------------ | ------------------------------ | ---------------------------------------------- |
| names        | struct IDomaRecord.NameInfo\[] | List of names to tokenize.                     |
| nonce        | uint256                        | Unique nonce to prevent replay attacks.        |
| expiresAt    | uint256                        | Expiration timestamp (UNIX seconds)            |
| ownerAddress | address                        | Address to receive the minted ownership token. |

```solidity
struct TokenizationVoucher {
  struct IDomaRecord.NameInfo[] names;
  uint256 nonce;
  uint256 expiresAt;
  address ownerAddress;
}
```

#### ProofOfContactsVoucher

Proof of Contacts voucher, obtained from a Registrar or Doma-provided storage.

**Parameters**

| Name             | Type                                   | Description                             |
| ---------------- | -------------------------------------- | --------------------------------------- |
| registrantHandle | uint256                                | The registrant's handle identifier.     |
| proofSource      | enum IDomaRecord.ProofOfContactsSource | Source of the proof (REGISTRAR or DOMA) |
| nonce            | uint256                                | Unique nonce to prevent replay attacks. |
| expiresAt        | uint256                                | Expiration timestamp (UNIX seconds)     |

```solidity
struct ProofOfContactsVoucher {
  uint256 registrantHandle;
  enum IDomaRecord.ProofOfContactsSource proofSource;
  uint256 nonce;
  uint256 expiresAt;
}
```

#### HostDNSInput

A host with its associated DNS record sets, used for batch DNS operations.

**Parameters**

<table data-header-hidden><thead><tr><th></th><th width="263.2421875"></th><th></th></tr></thead><tbody><tr><td>Name</td><td>Type</td><td>Description</td></tr><tr><td>host</td><td>string</td><td>The hostname relative to the token (eg. "www" or "" for apex). Must be lowercase.</td></tr><tr><td>recordSets</td><td>struct IDomaRecord.DNSRecordSet[]</td><td>Array of DNS record sets for this host.</td></tr></tbody></table>

```solidity
struct HostDNSInput {                                                                                                                                                                                                                       
    string host;                                     
    struct IDomaRecord.DNSRecordSet[] recordSets;
  }
```

#### DNSRecordSet

A single DNS record set containing a record type, TTL and values.

**Parameters**

<table data-header-hidden><thead><tr><th></th><th width="263.2421875"></th><th></th></tr></thead><tbody><tr><td>Name</td><td>Type</td><td>Description</td></tr><tr><td>recordType</td><td>string</td><td>DNS record type (eg "A", "AAAA", "CNAME").</td></tr><tr><td>ttl</td><td>uint32</td><td>Time to live in seconds.</td></tr><tr><td>records</td><td>string[]</td><td>Array of record values. Empty array deletes the RRSet.</td></tr></tbody></table>

```solidity
struct DNSRecordSet {
    string recordType;                                                                                                                                                                                                                        
    uint32 ttl;                                      
    string[] records;                                                                                                                                                                                                                         
  }   
```

## Errors

#### InsufficientCapabilities

```solidity
error InsufficientCapabilities(uint256 tokenId, uint256 requiredCapability)
```

Thrown when a token lacks the required capabilities for an operation.

#### Parameters

| Name               | Type    | Description                              |
| ------------------ | ------- | ---------------------------------------- |
| tokenId            | uint256 | The ID of the token being checked        |
| requiredCapability | uint256 | The capability bitmask that was required |

#### NameAlreadyTokenized

```solidity
error NameAlreadyTokenized(string sld, string tld)
```

Thrown when attempting to tokenize a name that is already tokenized.

**Parameters**

| Name | Type   | Description                         |
| ---- | ------ | ----------------------------------- |
| sld  | string | The second-level domain of the name |
| tld  | string | The top-level domain of the name    |

#### InvalidRegistrar

```solidity
error InvalidRegistrar(uint256 ianaId, uint256 expectedIanaId)
```

Thrown when a registrar ID doesn't match the expected registrar for an operation.

**Parameters**

| Name           | Type    | Description                    |
| -------------- | ------- | ------------------------------ |
| ianaId         | uint256 | The provided registrar IANA ID |
| expectedIanaId | uint256 | The expected registrar IANA ID |

#### UnsupportedTargetChain

```solidity
error UnsupportedTargetChain(string chainId)
```

Thrown when attempting to bridge to an unsupported target chain.

**Parameters**

| Name    | Type   | Description                               |
| ------- | ------ | ----------------------------------------- |
| chainId | string | The unsupported chain ID in CAIP-2 format |

#### TransferLocked

```solidity
error TransferLocked(uint256 tokenId)
```

Thrown when attempting to transfer a token that is locked for transfers.

**Parameters**

| Name    | Type    | Description                |
| ------- | ------- | -------------------------- |
| tokenId | uint256 | The ID of the locked token |

#### NameTokenHasExpired

```solidity
error NameTokenHasExpired(uint256 tokenId, uint256 expiresAt)
```

Thrown when attempting to operate on an expired name token.

**Parameters**

| Name      | Type    | Description                           |
| --------- | ------- | ------------------------------------- |
| tokenId   | uint256 | The ID of the expired token           |
| expiresAt | uint256 | The expiration timestamp of the token |

#### InvalidSigner

```solidity
error InvalidSigner(address signer)
```

Thrown when a voucher signature is invalid or from an unauthorized signer.

**Parameters**

| Name   | Type    | Description                                       |
| ------ | ------- | ------------------------------------------------- |
| signer | address | The address that was recovered from the signature |

#### VoucherExpired

```solidity
error VoucherExpired(uint256 expiresAt, uint256 currentTime)
```

Thrown when a voucher has expired and cannot be used.

**Parameters**

| Name        | Type    | Description                             |
| ----------- | ------- | --------------------------------------- |
| expiresAt   | uint256 | The expiration timestamp of the voucher |
| currentTime | uint256 | The current block timestamp             |

#### PriceFeedStalePrice

```solidity
error PriceFeedStalePrice(uint80 roundId, uint256 updatedAt)
```

Thrown when the price feed data is stale and cannot be trusted.

**Parameters**

| Name      | Type    | Description                                   |
| --------- | ------- | --------------------------------------------- |
| roundId   | uint80  | The round ID from the price feed              |
| updatedAt | uint256 | The timestamp when the price was last updated |

#### InvalidPrice

```solidity
error InvalidPrice(int256 price)
```

Thrown when the price feed returns an invalid price value.

**Parameters**

| Name  | Type   | Description                      |
| ----- | ------ | -------------------------------- |
| price | int256 | The invalid price value returned |

#### TransferFailed

```solidity
error TransferFailed()
```

Thrown when a native currency transfer fails.

#### InvalidFee

```solidity
error InvalidFee(uint256 expectedFee, uint256 providedFee)
```

Thrown when the provided fee doesn't match the expected fee for an operation.

**Parameters**

| Name        | Type    | Description                      |
| ----------- | ------- | -------------------------------- |
| expectedFee | uint256 | The expected fee amount in wei   |
| providedFee | uint256 | The fee amount actually provided |

#### FeeNotRequired

```solidity
error FeeNotRequired(bytes32 operation, uint256 nameCount)
```

Thrown when a fee is provided for an operation that doesn't require payment.

**Parameters**

| Name      | Type    | Description                         |
| --------- | ------- | ----------------------------------- |
| operation | bytes32 | The operation identifier            |
| nameCount | uint256 | The number of names being processed |

#### NonceAlreadyUsed

```solidity
error NonceAlreadyUsed(uint256 nonce)
```

Thrown when attempting to reuse a nonce that has already been consumed.

**Parameters**

| Name  | Type    | Description                     |
| ----- | ------- | ------------------------------- |
| nonce | uint256 | The nonce that was already used |

#### FeeCollected

```solidity
event FeeCollected(uint256 feeWei, uint256 feeUsdCents, string correlationId)
```

Emitted when a fee is collected for an operation.

**Parameters**

| Name          | Type    | Description                                   |
| ------------- | ------- | --------------------------------------------- |
| feeWei        | uint256 | The fee amount collected in wei               |
| feeUsdCents   | uint256 | The equivalent fee amount in USD cents        |
| correlationId | string  | The correlation ID for tracking the operation |

#### SubdomainHostInUse

```solidity
error SubdomainHostInUse(string host)  
```

Thrown when attempting to set DNS records on a host that has an operational subdomain token.

#### Parameters

| Name | Type   | Description                                               |
| ---- | ------ | --------------------------------------------------------- |
| host | string | The host lable that conflicts with an existing subdomain. |

#### EmptyArray

```solidity
error EmptyArray()
```

Thrown when an empty array is passed to a batch operation.

## Methods

#### requestTokenization

```solidity
function requestTokenization(struct ProxyDomaRecordUserFacet.TokenizationVoucher voucher, bytes signature) external payable
```

Request tokenization of domain names using a signed voucher from a registrar.

*Validates the voucher signature, collects fees, and initiates cross-chain tokenization. The voucher must be signed by an authorized registrar and not expired. Names are validated for proper format and checked for existing tokenization on this chain.*

**Parameters**

| Name      | Type                                                | Description                                                         |
| --------- | --------------------------------------------------- | ------------------------------------------------------------------- |
| voucher   | struct ProxyDomaRecordUserFacet.TokenizationVoucher | The tokenization voucher containing names and authorization details |
| signature | bytes                                               | The registrar's signature over the voucher data                     |

#### claimOwnership

```solidity
function claimOwnership(uint256 tokenId, struct ProxyDomaRecordUserFacet.ProofOfContactsVoucher proofOfContactsVoucher, bytes signature) external payable
```

Claim ownership of a domain name using proof of contacts voucher.

*Allows token owners to establish claim over their domain by providing signed proof from either the registrar or DOMA system. Validates voucher signature, collects fees, and initiates cross-chain ownership claim process.*

**Parameters**

| Name                   | Type                                                   | Description                                             |
| ---------------------- | ------------------------------------------------------ | ------------------------------------------------------- |
| tokenId                | uint256                                                | The ID of the ownership token being claimed             |
| proofOfContactsVoucher | struct ProxyDomaRecordUserFacet.ProofOfContactsVoucher | The voucher containing proof of contact information     |
| signature              | bytes                                                  | The signature over the voucher (from registrar or DOMA) |

#### bridge

```solidity
function bridge(uint256 tokenId, string targetChainId, string targetOwnerAddress) external payable
```

Bridge a domain token to another supported chain.

*Burns the token on this chain and initiates minting on the target chain. Validates target chain support, transfer lock status, and token expiration. Collects fees and relays the bridge request to the DOMA chain.*

**Parameters**

| Name               | Type    | Description                                  |
| ------------------ | ------- | -------------------------------------------- |
| tokenId            | uint256 | The ID of the ownership token to bridge      |
| targetChainId      | string  | The CAIP-2 chain ID of the destination chain |
| targetOwnerAddress | string  | The owner address on the target chain        |

#### requestDetokenization

```solidity
function requestDetokenization(uint256 tokenId) external
```

Request detokenization of a domain name by the owner.

*Initiates the detokenization process on the DOMA chain, which will remove the domain from the protocol and return it to traditional DNS management. Only the token owner can request detokenization.*

**Parameters**

| Name    | Type    | Description                                 |
| ------- | ------- | ------------------------------------------- |
| tokenId | uint256 | The ID of the ownership token to detokenize |

#### setNameservers

```solidity
function setNameservers(uint256 tokenId, string[] nameservers) external payable
```

Update nameservers for a domain.

*Requires CAPABILITY\_REGISTRY\_RECORDS\_MANAGEMENT. Relays change to Doma Chain.*

**Parameters**

| Name        | Type      | Description                   |
| ----------- | --------- | ----------------------------- |
| tokenId     | uint256   | The ownership token ID.       |
| nameservers | string\[] | List of nameserver hostnames. |

#### setDSKeys

```solidity
function setDSKeys(uint256 tokenId, struct IDomaRecord.DSKey[] dsKeys) external payable
```

Update DNSSEC DS keys for a domain.

*Requires CAPABILITY\_REGISTRY\_RECORDS\_MANAGEMENT. Relays change to Doma Chain.*

**Parameters**

| Name    | Type                        | Description              |
| ------- | --------------------------- | ------------------------ |
| tokenId | uint256                     | The ownership token ID.  |
| dsKeys  | struct IDomaRecord.DSKey\[] | Array of DS key records. |

#### setDNSRRSet

```solidity
function setDNSRRSet(uint256 tokenId, string host, string recordType, uint32 ttl, string[] records) external payable
```

Set DNS resource record set for a domain. Works for ownership tokens, synthetic root tokens and synthetic subdomain tokens. For subdomain tokens, the host is relative to the subdomain (use empty string for apex).

*Requires CAPABILITY\_DNS\_RECORDS\_MANAGEMENT. Relays change to Doma Chain.*

**Parameters**

| Name       | Type      | Description                                            |
| ---------- | --------- | ------------------------------------------------------ |
| tokenId    | uint256   | The ownership token ID.                                |
| host       | string    | The hostname (e.g., "www" or "@" for apex).            |
| recordType | string    | DNS record type (e.g., "A", "AAAA", "CNAME").          |
| ttl        | uint32    | Time-to-live in seconds.                               |
| records    | string\[] | Array of record values. Empty array deletes the RRSet. |

#### setDNSRRSetBatch

```solidity
function setDNSRRSetBatch(uint256 tokenId, HostDNSInput[] hostInputs) external payable
```

Sets multiple DNS record sets across multiple hosts in a single transaction. Works for ownership tokens, synthetic root tokens and synthetic subdomain tokens. For subdomain tokens, hosts are relative to the subdomain (use empty string for apex). Each host entry must have at least one record set. Empty records array within a record set deletes that RRSet.

*Requires CAPABILITY\_DNS\_RECORDS\_MANAGEMENT. Relays change to Doma Chain.*

**Parameters**

| Name       | Type      | Description                                            |
| ---------- | --------- | ------------------------------------------------------ |
| tokenId    | uint256   | The ownership token ID.                                |
| host       | string    | The hostname (e.g., "www" or "@" for apex).            |
| recordType | string    | DNS record type (e.g., "A", "AAAA", "CNAME").          |
| ttl        | uint32    | Time-to-live in seconds.                               |
| records    | string\[] | Array of record values. Empty array deletes the RRSet. |

#### feesUSDCents

```solidity
function feesUSDCents(bytes32 operation) public view returns (uint256)
```

Get the fee in USD cents for a specific operation.

**Parameters**

| Name      | Type    | Description                       |
| --------- | ------- | --------------------------------- |
| operation | bytes32 | The operation identifier to query |

**Return Values**

| Name | Type    | Description                 |
| ---- | ------- | --------------------------- |
| \[0] | uint256 | The fee amount in USD cents |

#### isTargetChainSupported

```solidity
function isTargetChainSupported(string targetChainId) public view returns (bool)
```

Check if a target chain is supported for bridging operations.

**Parameters**

| Name          | Type   | Description                          |
| ------------- | ------ | ------------------------------------ |
| targetChainId | string | The CAIP-2 chain identifier to check |

**Return Values**

| Name | Type | Description                           |
| ---- | ---- | ------------------------------------- |
| \[0] | bool | Whether the target chain is supported |

#### getNativePrice

```solidity
function getNativePrice(uint256 feeUSDCents) public view returns (uint256 nativeFee)
```

Convert a USD cent amount to the equivalent native token amount.

*Uses Chainlink price feed to get current exchange rate. Validates price feed data.*

**Parameters**

| Name        | Type    | Description                 |
| ----------- | ------- | --------------------------- |
| feeUSDCents | uint256 | The fee amount in USD cents |

**Return Values**

| Name      | Type    | Description                            |
| --------- | ------- | -------------------------------------- |
| nativeFee | uint256 | The equivalent fee in native token wei |

#### getBridgeFeeInNative

```solidity
function getBridgeFeeInNative(string targetChainId) public view returns (uint256)
```

Get the bridge fee in native tokens for a target chain.

Uses per-chain bridge config when present, otherwise falls back to the default BRIDGE\_OPERATION fee

**Parameters**

| Name          | Type   | Description                         |
| ------------- | ------ | ----------------------------------- |
| targetChainId | string | The CAIP-2 target chain identifier. |

**Return Values**

| Name | Type    | Description                         |
| ---- | ------- | ----------------------------------- |
| \[0] | uint256 | The fee amount in native token wei. |

#### getOperationFeeInNative

```solidity
function getOperationFeeInNative(bytes32 operation) public view returns (uint256)
```

Get the fee in native tokens for a specific operation.

*Combines USD cent fee lookup with current price conversion.*

**Parameters**

| Name      | Type    | Description                       |
| --------- | ------- | --------------------------------- |
| operation | bytes32 | The operation identifier to query |

**Return Values**

| Name | Type    | Description                                                    |
| ---- | ------- | -------------------------------------------------------------- |
| \[0] | uint256 | The fee amount in native token wei (0 if operation has no fee) |

#### convertToSynthetic

```solidity
function convertToSynthetic(uint256 tokenId) external payable
```

Convert an ownership token to a synthetic ownership token. Burns the ownership token and initiates minting of a synthetic token on the same chain via Doma Chain.

The synthetic token maintains the same tokenId, expiration and capabilities as the original.

**Parameters**

| Name    | Type    | Description                               |
| ------- | ------- | ----------------------------------------- |
| tokenId | uint256 | The ID of the ownership token to convert. |

#### convertToOwnership

```solidity
function convertToOwnership(uint256 tokenId) external payable
```

Convert a synthetic ownership token back to a regular ownership token. Burns the synthetic token and initiates minting of an ownership token on the same chain via Doma Chain

Requires that the synthetic token has no active subdomains.

**Parameters**

| Name    | Type    | Description                               |
| ------- | ------- | ----------------------------------------- |
| tokenId | uint256 | The ID of the synthetic token to convert. |

#### mintSyntheticSubdomain

```solidity
function mintSyntheticSubdomain(uint256 parentTokenId, string host, uint256 capabilities, uint256 expiresAt, bool revocable, uint256 groupId, address receiver) external payable returns (uint256)   
```

Mint a synthetic subdomain token under a synthetic root token. The subdomain token grants DNS management rights over a specific subdomain. Can only be called by the parent token owner.

**Parameters**

| Name          | Type    | Description                                                              |
| ------------- | ------- | ------------------------------------------------------------------------ |
| parentTokenId | uint256 | The ID of the parent synthetic token.                                    |
| host          | string  | The subdomain host label (eg "sam", "dev", "app").                       |
| capabilities  | uint256 | Capability flags (must include CAPABILITY\_DNS\_RECORDS\_MANAGEMENT).    |
| expiresAt     | uint256 | Expiration timestamp for the subdomain token (0 to inherit from parent). |
| revocable     | bool    | Whether the parent token owner can revoke this subdomain.                |
| groupId       | uint256 | Group identifier for batch operations (batch revoke).                    |
| receiver      | address | The address to receive the minted subdomain token.                       |

#### **Return Values**

| Name | Type    | Description                                   |
| ---- | ------- | --------------------------------------------- |
| \[0] | uint256 | The newly minted synthetic subdomain token ID |

#### renewSubdomain

```solidity
function renewSubdomain(uint256 tokenId, uint256 expiresAt) external payable
```

Renew a synthetic subdomain token. Can only be called by the parent token owner. New expiration must extend the current expiration and must not exceed the parent token's expiration.

**Parameters**

| Name      | Type    | Description                             |
| --------- | ------- | --------------------------------------- |
| tokenId   | uint256 | The ID of the subdomain token to renew. |
| expiresAt | uint256 | The new expiration timestamp.           |

#### renounceSynthetic

```solidity
function renounceSynthetic(uint256 tokenId) external  
```

Renounce ownership of a synthetic subdomain token. Burns the subdomain token and makes it available for re-minting. Can only be called by the subdomain token owner. The subdomain must not be in a locked group.

**Parameters**

| Name    | Type    | Description                                |
| ------- | ------- | ------------------------------------------ |
| tokenId | uint256 | The ID of the subdomain token to renounce. |

#### revokeSynthetic

```solidity
function revokeSynthetic(uint256 tokenId) external
```

Revoke a specific synthetic subdomain token. Burns the subdomain token and makes it available for re-minting. Can only be called by the parent token owner. The subdomain must be revocable and not in a locked group.

**Parameters**

| Name    | Type    | Description                              |
| ------- | ------- | ---------------------------------------- |
| tokenId | uint256 | The ID of the subdomain token to revoke. |

#### revokeGroup

```solidity
function revokeGroup(uint256 parentTokenId, uint256 groupId) external
```

Revoke a group of subdomains, preventing transfers and marking them for bulk operations. Can only be called by the parent token owner. Reduces the parent's subdomain count by the number of tokens in the group.

**Parameters**

| Name          | Type    | Description                           |
| ------------- | ------- | ------------------------------------- |
| parentTokenId | uint256 | The ID of the parent synthetic token. |
| groupId       | uint256 | The group identifier to revoke.       |

#### version

```solidity
function version() external pure returns (string)
```

Get the contract version.

**Return Values**

| Name | Type   | Description                         |
| ---- | ------ | ----------------------------------- |
| \[0] | string | The version string of this contract |

#### REQUEST\_TOKENIZATION\_OPERATION

```solidity
function REQUEST_TOKENIZATION_OPERATION() external pure returns (bytes32)
```

Get the REQUEST\_TOKENIZATION\_OPERATION constant

**Return Values**

| Name | Type    | Description                                 |
| ---- | ------- | ------------------------------------------- |
| \[0] | bytes32 | The operation hash for request tokenization |

#### CLAIM\_OWNERSHIP\_OPERATION

```solidity
function CLAIM_OWNERSHIP_OPERATION() external pure returns (bytes32)
```

Get the CLAIM\_OWNERSHIP\_OPERATION constant

**Return Values**

| Name | Type    | Description                            |
| ---- | ------- | -------------------------------------- |
| \[0] | bytes32 | The operation hash for claim ownership |

#### BRIDGE\_OPERATION

```solidity
function BRIDGE_OPERATION() external pure returns (bytes32)
```

Get the BRIDGE\_OPERATION constant

**Return Values**

| Name | Type    | Description                   |
| ---- | ------- | ----------------------------- |
| \[0] | bytes32 | The operation hash for bridge |

#### SET\_NAMESERVERS\_OPERATION

```solidity
function SET_NAMESERVERS_OPERATION() external pure returns (bytes32)
```

Get the SET\_NAMESERVERS\_OPERATION constant

**Return Values**

| Name | Type    | Description                            |
| ---- | ------- | -------------------------------------- |
| \[0] | bytes32 | The operation hash for set nameservers |

#### SET\_DS\_KEYS\_OPERATION

```solidity
function SET_DS_KEYS_OPERATION() external pure returns (bytes32)
```

Get the SET\_DS\_KEYS\_OPERATION constant

**Return Values**

| Name | Type    | Description                        |
| ---- | ------- | ---------------------------------- |
| \[0] | bytes32 | The operation hash for set DS keys |

#### SET\_DNS\_RRSET\_OPERATION

```solidity
function SET_DNS_RRSET_OPERATION() external pure returns (bytes32)
```

Get the SET\_DNS\_RRSET\_OPERATION constant

**Return Values**

| Name | Type    | Description                          |
| ---- | ------- | ------------------------------------ |
| \[0] | bytes32 | The operation hash for set DNS RRSet |

#### CONVERT\_TO\_SYNTHETIC\_OPERATION

```solidity
function CONVERT_TO_SYNTHETIC_OPERATION() external pure returns (bytes32)
```

Get the CONVERT\_TO\_SYNTHETIC\_OPERATION constant

**Return Values**

| Name | Type    | Description                                  |
| ---- | ------- | -------------------------------------------- |
| \[0] | bytes32 | The operation hash for convert to synthetic. |

#### CONVERT\_TO\_OWNERSHIP\_OPERATION

```solidity
function CONVERT_TO_OWNERSHIP_OPERATION() external pure returns (bytes32)
```

Get the CONVERT\_TO\_OWNERSHIP constant

**Return Values**

| Name | Type    | Description                                  |
| ---- | ------- | -------------------------------------------- |
| \[0] | bytes32 | The operation hash for convert to ownership. |


# (EVM) Name Token Contract API

Abstract base contract for name NFTs in the DOMA protocol.

*This contract implements an ERC721 NFT that represents tokenized names. Features include expiration tracking, transfer locking, name capabilities management, royalty support (ERC2981), and integration with proxy DOMA record contracts.*

{% file src="/files/CyAubej6l0EGbpZGcHNo" %}
OwnershipToken Contract ABI
{% endfile %}

## Errors

#### TransferLocked

```solidity
error TransferLocked(uint256 tokenId)
```

Thrown when attempting to transfer a token that is locked for transfers.

**Parameters**

| Name    | Type    | Description                |
| ------- | ------- | -------------------------- |
| tokenId | uint256 | The ID of the locked token |

#### OwnerBurnNotSupported

```solidity
error OwnerBurnNotSupported()
```

Thrown when owner attempts to use burn functionality that is not supported.

#### ProxyDomaRecordNotSet

```solidity
error ProxyDomaRecordNotSet()
```

Thrown when proxy DOMA record address is not set but required for operation.

#### ZeroAddress

```solidity
error ZeroAddress()
```

Thrown when a zero address is provided where a valid address is required.

#### InvalidRoyaltyReceiver

```solidity
error InvalidRoyaltyReceiver()
```

Thrown when an invalid royalty receiver address is provided.

#### InvalidRoyaltyFraction

```solidity
error InvalidRoyaltyFraction(uint96 feeNumerator, uint96 feeDenominator)
```

Thrown when royalty fraction exceeds the maximum allowed value.

**Parameters**

| Name           | Type   | Description                                  |
| -------------- | ------ | -------------------------------------------- |
| feeNumerator   | uint96 | The provided fee numerator                   |
| feeDenominator | uint96 | The fee denominator (10000 for basis points) |

#### ArrayLengthMismatch

```solidity
error ArrayLengthMismatch()
```

Thrown when array parameters have mismatched lengths.

## Events

#### OwnershipTokenMinted

```solidity
event OwnershipTokenMinted(uint256 tokenId, uint256 registrarIanaId, address to, string sld, string tld, uint256 expiresAt, string correlationId)
```

Emitted when an ownership token is minted. Emitted together with standard ERC-721 Transfer event, but contains additional information.

**Parameters**

| Name            | Type    | Description                                                                                   |
| --------------- | ------- | --------------------------------------------------------------------------------------------- |
| tokenId         | uint256 | The ID of the ownership token.                                                                |
| registrarIanaId | uint256 | The IANA ID of a sponsoring registrar.                                                        |
| to              | address | The address that received the ownership token.                                                |
| sld             | string  | The second-level domain of the name. E.g. "example" in "example.com".                         |
| tld             | string  | The top-level domain of the name. E.g. "com" in "example.com".                                |
| expiresAt       | uint256 | The expiration date of the name (UNIX seconds).                                               |
| correlationId   | string  | Correlation id associated with a mint event. Used by registrars to track on-chain operations. |

#### NameTokenRenewed

```solidity
event NameTokenRenewed(uint256 tokenId, uint256 expiresAt, string correlationId)
```

Emitted when name token is renewed.

**Parameters**

| Name          | Type    | Description                                                                                      |
| ------------- | ------- | ------------------------------------------------------------------------------------------------ |
| tokenId       | uint256 | The ID of the name token.                                                                        |
| expiresAt     | uint256 | The expiration date of the name token (UNIX seconds).                                            |
| correlationId | string  | Correlation id associated with a renewal event. Used by registrars to track on-chain operations. |

#### NameTokenBurned

```solidity
event NameTokenBurned(uint256 tokenId, address owner, string correlationId)
```

Emitted when name token is burned. Similar to ERC721 `Transfer` event with zero `to`, but with an additional correlation id included.

**Parameters**

| Name          | Type    | Description                                                                                   |
| ------------- | ------- | --------------------------------------------------------------------------------------------- |
| tokenId       | uint256 | The ID of the name token.                                                                     |
| owner         | address | Owner address at the time of burning.                                                         |
| correlationId | string  | Correlation id associated with a burn event. Used by registrars to track on-chain operations. |

#### LockStatusChanged

```solidity
event LockStatusChanged(uint256 tokenId, bool isTransferLocked, string correlationId)
```

Emitted when name token is locked or unlocked.

**Parameters**

| Name             | Type    | Description                                                                                                 |
| ---------------- | ------- | ----------------------------------------------------------------------------------------------------------- |
| tokenId          | uint256 | The ID of the name token.                                                                                   |
| isTransferLocked | bool    | Whether token transfer is locked or not.                                                                    |
| correlationId    | string  | Correlation id associated with a lock status change event. Used by registrars to track on-chain operations. |

#### MetadataUpdate

```solidity
event MetadataUpdate(uint256 tokenId)
```

Emitted when metadata is updated for a token. Can happen when token is renewed. Follows IERC4906 Metadata Update Extension.

#### DomainCapabilitiesUpdated

```solidity
event DomainCapabilitiesUpdated(uint256 tokenId, uint256 capabilities, string correlationId)
```

Emitted when domain-level capabilities are updated for a token.

**Parameters**

| Name          | Type    | Description                             |
| ------------- | ------- | --------------------------------------- |
| tokenId       | uint256 | The ID of the name token.               |
| capabilities  | uint256 | The new domain capabilities bitmask.    |
| correlationId | string  | Correlation id for tracking operations. |

#### burn

```solidity
function burn(uint256) public virtual
```

User-initiated burn is not supported.

*This override prevents direct token burning by users. Only the bulkBurn function should be used by authorized minters to ensure proper cleanup and event emission. Base class (ERC721BurnableUpgradeable) is kept for compatibility with previous storage layout.*

#### expirationOf

```solidity
function expirationOf(uint256 id) external view returns (uint256)
```

Returns expiration date for a token. After this date, token transfer will be blocked.

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id   | uint256 | Token ID.   |

**Return Values**

| Name | Type    | Description                        |
| ---- | ------- | ---------------------------------- |
| \[0] | uint256 | uint256 Unix timestamp in seconds. |

#### registrarOf

```solidity
function registrarOf(uint256 id) external view returns (uint256)
```

Returns registrar IANA ID for a token.

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id   | uint256 | Token ID.   |

**Return Values**

| Name | Type    | Description                |
| ---- | ------- | -------------------------- |
| \[0] | uint256 | uint256 Registrar IANA ID. |

#### lockStatusOf

```solidity
function lockStatusOf(uint256 id) external view returns (bool)
```

Returns transfer lock status for a token. If 'true', token cannot be transferred.

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id   | uint256 | Token ID.   |

#### domainCapabilitiesOf

```solidity
function domainCapabilitiesOf(uint256 id) external view returns (uint256)
```

Returns domain-level capabilities for a token. These are the capabilities supported by this specific domain.

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id   | uint256 | Token ID.   |

**Return Values**

| Name | Type    | Description                          |
| ---- | ------- | ------------------------------------ |
| \[0] | uint256 | uint256 Domain capabilities bitmask. |

#### hasCapability

```solidity
function hasCapability(uint256 id, uint256 requiredCapability) external view returns (bool)
```

Check if a token has a specific capability.

**Parameters**

| Name               | Type    | Description                       |
| ------------------ | ------- | --------------------------------- |
| id                 | uint256 | Token ID.                         |
| requiredCapability | uint256 | The required capability bit mask. |

**Return Values**

| Name | Type | Description                                |
| ---- | ---- | ------------------------------------------ |
| \[0] | bool | bool True if the token has the capability. |

#### exists

```solidity
function exists(uint256 id) external view returns (bool)
```

Returns true if a token with the given ID exists.

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id   | uint256 | Token ID.   |

#### royaltyInfo

```solidity
function royaltyInfo(uint256, uint256 salePrice) external view virtual returns (address receiver, uint256 royaltyAmount)
```

Get royalty information for a token sale (ERC2981).

*Returns the royalty receiver and amount for a given sale price. Royalty is calculated as (salePrice \* royaltyFraction) / 10000.*

**Parameters**

| Name      | Type    | Description                 |
| --------- | ------- | --------------------------- |
|           | uint256 |                             |
| salePrice | uint256 | The sale price of the token |

**Return Values**

| Name          | Type    | Description                                         |
| ------------- | ------- | --------------------------------------------------- |
| receiver      | address | The address that should receive the royalty payment |
| royaltyAmount | uint256 | The royalty amount to be paid                       |


# Doma Multi-Chain Subgraph

{% hint style="info" %}
API is subject to [Authentication and Rate Limit](/api-reference/authentication-and-rate-limits) rules.
{% endhint %}

Doma Subgraph could be used to get consolidated data about names tokenized on Doma Protocol. This data includes information about names, name tokens, and associated activities. Also, it includes aggregated marketplaces offers and listing information.

Endpoints:

* Testnet: <https://api-testnet.doma.xyz/graphql>
* Mainnet: <https://api.doma.xyz/graphql>


# Poll API

{% hint style="info" %}
API is subject to [Authentication and Rate Limit](/api-reference/authentication-and-rate-limits) rules.
{% endhint %}

Poll API provides a stream of Doma Protocol events. These events could be used to build your own representation of Doma protocol state, analytics, or any kind of processing.

API works using Poll -> Acknowledge system. It means that to receive new events, older events should be acknowledged. Sample integration could look like this:

1. Call [Poll API](#get-v1-poll) to get new events. Optional `limit` and `eventTypes` could be specified.
2. Process events (store in database, send to message queue, etc.).
3. Call [Poll Ack API](#post-v1-poll-ack-lasteventid) to acknowledge last received event (you can use `lastId` from response). This would acknowledge all of the events from polled batch.

Tips:

* To process events one-by-one, specify `limit=1` in Poll API call.
* To reprocess already received events (e.g. if new event type is added), [Poll Reset API](#post-v1-poll-reset-eventid) can be used to rewind polling cursor to a lower id. To reset cursor to the beginning, `eventId` should be set to `0`.
* Each returned event has a `uniqueId` field, that can be used as an idempotency key for events processing.

## Poll for new Doma Protocol events

> Returns blockchain events that have occurred since the last acknowledged event.

```json
{"openapi":"3.0.0","info":{"title":"Doma Registrar API","version":"1.0"},"servers":["https://api.doma.xyz","https://api-testnet.doma.xyz"],"security":[{"Api-Key":[]}],"components":{"securitySchemes":{"Api-Key":{"type":"apiKey","in":"header","name":"Api-Key"}},"schemas":{"EventsPaginatedResponse":{"type":"object","properties":{"events":{"description":"List of events.","type":"array","items":{"$ref":"#/components/schemas/EventResponse"}},"lastId":{"type":"number","description":"Last returned event id. Should be used with `/poll/ack` API to acknowledge events receive."},"hasMoreEvents":{"type":"boolean","description":"If true, more events might be available. They could be retrieved by acknowledging the last event id, or by increasing the `limit`."}},"required":["events"]},"EventResponse":{"type":"object","properties":{"id":{"type":"number","description":"Unique event id."},"name":{"type":"string","description":"Associated name. Returned only for name-specific events."},"eoi":{"type":"boolean","description":"Whether this is an Expression of Interest (EOI) name."},"tokenId":{"type":"string","description":"Associated name token id. Returned only for token-specific events."},"type":{"description":"Event type.","allOf":[{"$ref":"#/components/schemas/PublicEventType"}]},"uniqueId":{"type":"string","description":"Globally unique event id. Generated from a public on-chain data. Could be used to track same events across different systems."},"relayId":{"type":"string","description":"Relay ID that was used to submit a transaction that triggered this event. Will only be present for events triggered by the Relay API."},"eventData":{"description":"Event-specific data.","oneOf":[{"$ref":"#/components/schemas/NameTokenMintedEventResponse"},{"$ref":"#/components/schemas/NameTokenBurnedEventResponse"},{"$ref":"#/components/schemas/NameTokenTransferredEventResponse"},{"$ref":"#/components/schemas/NameTokenApprovedForAllEventResponse"},{"$ref":"#/components/schemas/NameTokenLockStatusChangeEventResponse"},{"$ref":"#/components/schemas/PaymentFulfilledEventResponse"},{"$ref":"#/components/schemas/NameTokenTransferApprovedEventResponse"},{"$ref":"#/components/schemas/NameTokenTransferApprovalRevokedEventResponse"},{"$ref":"#/components/schemas/NameTokenRenewedEventResponse"},{"$ref":"#/components/schemas/NameTokenizedEventResponse"},{"$ref":"#/components/schemas/NameUpdatedEventResponse"},{"$ref":"#/components/schemas/NameRenewedEventResponse"},{"$ref":"#/components/schemas/NameClaimedEventResponse"},{"$ref":"#/components/schemas/NameDetokenizedEventResponse"},{"$ref":"#/components/schemas/NameTokenizationRequestedEventResponse"},{"$ref":"#/components/schemas/NameTokenizationRejectedEventResponse"},{"$ref":"#/components/schemas/CommandCreatedEventResponse"},{"$ref":"#/components/schemas/CommandSucceededEventResponse"},{"$ref":"#/components/schemas/CommandFailedEventResponse"},{"$ref":"#/components/schemas/CommandUpdatedEventResponse"},{"$ref":"#/components/schemas/NameClaimRequestedEventResponse"},{"$ref":"#/components/schemas/NameClaimApprovedEventResponse"},{"$ref":"#/components/schemas/NameClaimRejectedEventResponse"},{"$ref":"#/components/schemas/NameNameServersUpdateRequestedEventResponse"},{"$ref":"#/components/schemas/NameDsKeysUpdateRequestedEventResponse"},{"$ref":"#/components/schemas/NameDNSRRSetUpdateRequestedResponse"},{"$ref":"#/components/schemas/SyntheticTokenMintedEventResponse"},{"$ref":"#/components/schemas/NameTokenListedEventResponse"},{"$ref":"#/components/schemas/NameTokenOfferReceivedEventResponse"},{"$ref":"#/components/schemas/NameTokenListingCancelledEventResponse"},{"$ref":"#/components/schemas/NameTokenOfferCancelledEventResponse"},{"$ref":"#/components/schemas/NameTokenPurchasedEventResponse"}]}},"required":["id","eoi","type","eventData"]},"PublicEventType":{"type":"string","enum":["NAME_TOKENIZATION_REQUESTED","NAME_TOKENIZATION_REJECTED","NAME_TOKENIZED","NAME_UPDATED","NAME_RENEWED","NAME_CLAIMED","NAME_CLAIM_REQUESTED","NAME_CLAIM_REJECTED","NAME_CLAIM_APPROVED","NAME_DETOKENIZED","NAME_TOKEN_MINTED","NAME_TOKEN_TRANSFERRED","NAME_TOKEN_RENEWED","NAME_TOKEN_BURNED","NAME_TOKEN_APPROVED_FOR_ALL","NAME_TOKEN_TRANSFER_APPROVED","NAME_TOKEN_TRANSFER_APPROVAL_REVOKED","NAME_TOKEN_LOCK_STATUS_CHANGED","NAME_TOKEN_BRIDGED","PAYMENT_FULFILLED","NAME_TOKEN_BOUGHT_OUT","NAME_NAMESERVERS_UPDATE_REQUESTED","NAME_DS_KEYS_UPDATE_REQUESTED","NAME_DNS_RR_SET_UPDATE_REQUESTED","SYNTHETIC_TOKEN_MINTED","NAME_TOKEN_LISTED","NAME_TOKEN_OFFER_RECEIVED","NAME_TOKEN_LISTING_CANCELLED","NAME_TOKEN_OFFER_CANCELLED","NAME_TOKEN_PURCHASED","NAME_TOKENS_BULK_LISTED","COMMAND_CREATED","COMMAND_SUCCEEDED","COMMAND_FAILED","COMMAND_UPDATED"],"description":"Event type."},"NameTokenMintedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"tokenAddress":{"type":"string","description":"NFT contract address"},"tokenId":{"type":"string","description":"Token ID"},"type":{"description":"Blockchain event type","allOf":[{"$ref":"#/components/schemas/NameTokenMintEventType"}]},"registrarIanaId":{"type":"number","description":"Registrar IANA ID."},"owner":{"type":"string","description":"Token owner."},"name":{"type":"string","description":"Associated name."},"expiresAt":{"type":"string","description":"Expiration date. ISO8601 format.","format":"date-time"},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."}},"required":["networkId","finalized","blockNumber","tokenAddress","tokenId","type","registrarIanaId","owner","name","expiresAt"]},"NameTokenMintEventType":{"type":"string","enum":["NAME_TOKEN_MINTED"],"description":"Blockchain event type"},"NameTokenBurnedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"tokenAddress":{"type":"string","description":"NFT contract address"},"tokenId":{"type":"string","description":"Token ID"},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameTokenBurnedEventType"}]},"owner":{"type":"string","description":"Owner."}},"required":["networkId","finalized","blockNumber","tokenAddress","tokenId","type","owner"]},"NameTokenBurnedEventType":{"type":"string","enum":["NAME_TOKEN_BURNED"],"description":"Blockchain event type."},"NameTokenTransferredEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"tokenAddress":{"type":"string","description":"NFT contract address"},"tokenId":{"type":"string","description":"Token ID"},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameTokenTransferredEventType"}]},"from":{"type":"string","description":"Sender."},"to":{"type":"string","description":"Recipient."}},"required":["networkId","finalized","blockNumber","tokenAddress","tokenId","type","from","to"]},"NameTokenTransferredEventType":{"type":"string","enum":["NAME_TOKEN_TRANSFERRED"],"description":"Blockchain event type."},"NameTokenApprovedForAllEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"tokenAddress":{"type":"string","description":"NFT contract address."},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameTokenApprovedForAllEventType"}]},"owner":{"type":"string","description":"Token owner."},"operator":{"type":"string","description":"Approved operator address."},"approved":{"type":"boolean","description":"Approval status."}},"required":["networkId","finalized","blockNumber","tokenAddress","type","owner","operator","approved"]},"NameTokenApprovedForAllEventType":{"type":"string","enum":["NAME_TOKEN_APPROVED_FOR_ALL"],"description":"Blockchain event type."},"NameTokenLockStatusChangeEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"tokenAddress":{"type":"string","description":"NFT contract address"},"tokenId":{"type":"string","description":"Token ID"},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameTokenLockStatusChangedEventType"}]},"isTransferLocked":{"type":"boolean","description":"Whether the transfer is locked for a given token."},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."}},"required":["networkId","finalized","blockNumber","tokenAddress","tokenId","type","isTransferLocked"]},"NameTokenLockStatusChangedEventType":{"type":"string","enum":["NAME_TOKEN_LOCK_STATUS_CHANGED"],"description":"Blockchain event type."},"PaymentFulfilledEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/PaymentFulfilledEventType"}]},"paymentContract":{"type":"string","description":"Payment contract address."},"buyer":{"type":"string","description":"Buyer."},"amount":{"type":"string","description":"Amount paid."},"tokenAddress":{"type":"string","description":"Address of the token used for payment."},"paymentId":{"type":"string","description":"Payment ID."},"immediateMint":{"type":"boolean","description":"True if it should be minted immediately."}},"required":["networkId","finalized","blockNumber","type","paymentContract","buyer","amount"]},"PaymentFulfilledEventType":{"type":"string","enum":["PAYMENT_FULFILLED"],"description":"Blockchain event type."},"NameTokenTransferApprovedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"tokenAddress":{"type":"string","description":"NFT contract address"},"tokenId":{"type":"string","description":"Token ID"},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameTokenTransferApprovedEventType"}]},"operator":{"type":"string","description":"Approved operator address."},"approvalId":{"type":"number","description":"Approval ID."}},"required":["networkId","finalized","blockNumber","tokenAddress","tokenId","type","operator","approvalId"]},"NameTokenTransferApprovedEventType":{"type":"string","enum":["NAME_TOKEN_TRANSFER_APPROVED"],"description":"Blockchain event type."},"NameTokenTransferApprovalRevokedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"tokenAddress":{"type":"string","description":"NFT contract address"},"tokenId":{"type":"string","description":"Token ID"},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameTokenTransferApprovalRevokedEventType"}]},"operator":{"type":"string","description":"Approved operator address."}},"required":["networkId","finalized","blockNumber","tokenAddress","tokenId","type","operator"]},"NameTokenTransferApprovalRevokedEventType":{"type":"string","enum":["NAME_TOKEN_TRANSFER_APPROVAL_REVOKED"],"description":"Blockchain event type."},"NameTokenRenewedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"tokenAddress":{"type":"string","description":"NFT contract address"},"tokenId":{"type":"string","description":"Token ID"},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameTokenRenewalEventType"}]},"expiresAt":{"type":"string","description":"Expiration date. ISO8601 format.","format":"date-time"},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."}},"required":["networkId","finalized","blockNumber","tokenAddress","tokenId","type","expiresAt"]},"NameTokenRenewalEventType":{"type":"string","enum":["NAME_TOKEN_RENEWED"],"description":"Blockchain event type."},"NameTokenizedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"domaRecordAddress":{"type":"string","description":"Address of a Doma Record Smart Contract."},"name":{"type":"string","description":"Associated name."},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameTokenizedEventType"}]},"expiresAt":{"type":"string","description":"Expiration date. ISO8601 format.","format":"date-time"},"nameservers":{"description":"List of nameserver.","type":"array","items":{"type":"string"}},"dsKeys":{"description":"List of DS Keys.","type":"array","items":{"$ref":"#/components/schemas/DsKeyResponseData"}},"claimedBy":{"type":"string","description":"Current wallet address that claimed the name. CAIP-10 format."},"ownershipTokenAddress":{"type":"string","description":"NFT contract address","nullable":true},"tokenId":{"type":"object","description":"Token ID","nullable":true}},"required":["networkId","finalized","blockNumber","domaRecordAddress","name","type","expiresAt","nameservers","dsKeys","claimedBy","ownershipTokenAddress","tokenId"]},"NameTokenizedEventType":{"type":"string","enum":["NAME_TOKENIZED"],"description":"Blockchain event type."},"DsKeyResponseData":{"type":"object","properties":{"keyTag":{"type":"number","description":"DS Key Tag."},"algorithm":{"type":"number","description":"DS Key Algorithm."},"digestType":{"type":"number","description":"DS Key Digest Type."},"digest":{"type":"string","description":"DS Key Digest."}},"required":["keyTag","algorithm","digestType","digest"]},"NameUpdatedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"domaRecordAddress":{"type":"string","description":"Address of a Doma Record Smart Contract."},"name":{"type":"string","description":"Associated name."},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameUpdatedEventType"}]},"nameservers":{"description":"List of nameservers.","type":"array","items":{"type":"string"}},"dsKeys":{"description":"List of DS Keys.","type":"array","items":{"$ref":"#/components/schemas/DsKeyResponseData"}}},"required":["networkId","finalized","blockNumber","domaRecordAddress","name","type","nameservers","dsKeys"]},"NameUpdatedEventType":{"type":"string","enum":["NAME_UPDATED"],"description":"Blockchain event type."},"NameRenewedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"domaRecordAddress":{"type":"string","description":"Address of a Doma Record Smart Contract."},"name":{"type":"string","description":"Associated name."},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameRenewedEventType"}]},"expiresAt":{"type":"string","description":"Expiration date. ISO8601 format.","format":"date-time"}},"required":["networkId","finalized","blockNumber","domaRecordAddress","name","type","expiresAt"]},"NameRenewedEventType":{"type":"string","enum":["NAME_RENEWED"],"description":"Blockchain event type."},"NameClaimedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"domaRecordAddress":{"type":"string","description":"Address of a Doma Record Smart Contract."},"name":{"type":"string","description":"Associated name."},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."},"claimedBy":{"type":"string","description":"Wallet address that was used for claim. CAIP-10 format."},"proofSource":{"description":"Indicates the source of contacts information. \"registrantHandle\" is used as a unique identifier within this source. Following values are possible: \n0 - NONE. No contacts provided. Only used for EOIs. \n1 - REGISTRAR. Registrar has approved claim, and holds the contact information. \"registrantHandle\" holds internal registrar-supplied identifier. \n2 - DOMA. Doma has approved claim, and holds the contact information. \"registrantHandle\" holds contacts identifier in Doma API, that should be used with /registrant/{handle} API to obtain encrypted contact information.","allOf":[{"$ref":"#/components/schemas/ProofSource"}]},"registrantHandle":{"type":"string","description":"Unique identifier that can be used to obtain contact information."},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameClaimedEventType"}]}},"required":["networkId","finalized","blockNumber","domaRecordAddress","name","claimedBy","proofSource","registrantHandle","type"]},"ProofSource":{"type":"number","enum":[0,1,2],"description":"Indicates the source of contacts information. \"registrantHandle\" is used as a unique identifier within this source. Following values are possible: \n0 - NONE. No contacts provided. Only used for EOIs. \n1 - REGISTRAR. Registrar has approved claim, and holds the contact information. \"registrantHandle\" holds internal registrar-supplied identifier. \n2 - DOMA. Doma has approved claim, and holds the contact information. \"registrantHandle\" holds contacts identifier in Doma API, that should be used with /registrant/{handle} API to obtain encrypted contact information."},"NameClaimedEventType":{"type":"string","enum":["NAME_CLAIMED"],"description":"Blockchain event type."},"NameDetokenizedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"domaRecordAddress":{"type":"string","description":"Address of a Doma Record Smart Contract."},"name":{"type":"string","description":"Associated name."},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameDetokenizedEventType"}]}},"required":["networkId","finalized","blockNumber","domaRecordAddress","name","type"]},"NameDetokenizedEventType":{"type":"string","enum":["NAME_DETOKENIZED"],"description":"Blockchain event type."},"NameTokenizationRequestedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"domaRecordAddress":{"type":"string","description":"Address of a Doma Record Smart Contract."},"name":{"type":"string","description":"Associated name."},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameTokenizationRequestedEventType"}]},"ownershipTokenChainId":{"type":"string","description":"CAIP-2 chain Id on which tokenization was requested"},"ownershipTokenOwnerAddress":{"type":"string","description":"Owner address on a target chain."}},"required":["networkId","finalized","blockNumber","domaRecordAddress","name","type","ownershipTokenChainId","ownershipTokenOwnerAddress"]},"NameTokenizationRequestedEventType":{"type":"string","enum":["NAME_TOKENIZATION_REQUESTED"],"description":"Blockchain event type."},"NameTokenizationRejectedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"domaRecordAddress":{"type":"string","description":"Address of a Doma Record Smart Contract."},"name":{"type":"string","description":"Associated name."},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameTokenizationRejectedEventType"}]}},"required":["networkId","finalized","blockNumber","domaRecordAddress","name","type"]},"NameTokenizationRejectedEventType":{"type":"string","enum":["NAME_TOKENIZATION_REJECTED"],"description":"Blockchain event type."},"CommandCreatedEventResponse":{"type":"object","properties":{"relayId":{"type":"string","description":"Associated relay id, that was used to crate this command."},"transactions":{"description":"List of transactions associated with the command event.","type":"array","items":{"$ref":"#/components/schemas/CommandTransaction"}},"failureData":{"description":"Failure data for a failed command. Only present for failed commands. Contains information about the error that caused the command to fail.","nullable":true,"allOf":[{"$ref":"#/components/schemas/CommandFailureData"}]},"type":{"description":"Command event type","allOf":[{"$ref":"#/components/schemas/CommandCreatedEventType"}]}},"required":["relayId","transactions","failureData","type"]},"CommandTransaction":{"type":"object","properties":{"type":{"description":"The type of the blockchain transaction command.","allOf":[{"$ref":"#/components/schemas/BlockchainTransactionCommandType"}]},"status":{"description":"The current status of the blockchain transaction.","allOf":[{"$ref":"#/components/schemas/BlockchainTransactionStatus"}]},"chainId":{"type":"string","description":"The identifier of the blockchain network where the transaction is executed."},"hash":{"type":"object","description":"The hash of the blockchain transaction."},"signer":{"type":"object","description":"The signer of the transaction, or null if no signer is present."},"gasPrice":{"type":"object","description":"The gas price of the transaction, or null if not available."},"gasCost":{"type":"object","description":"The gas cost of the transaction, or null if not available."}},"required":["type","status","chainId"]},"BlockchainTransactionCommandType":{"type":"string","enum":["RELAY","CLAIM","UPDATE_METADATA","SET_REVERSE_MAPPING","SUBMIT_CROSS_CHAIN_MESSAGE","LAUNCHPAD_UNLOCK_FAIL_LAUNCH","BULK_BURN_INACTIVE_SYNTHETIC"],"description":"The type of the blockchain transaction command."},"BlockchainTransactionStatus":{"type":"string","enum":["SIGNED","PENDING","SUCCEEDED","FAILED","FAILED_OUT_OF_GAS","FAILED_TO_SUBMIT","TIMED_OUT"],"description":"The current status of the blockchain transaction."},"CommandFailureData":{"type":"object","properties":{"chainId":{"type":"string","description":"Chain ID of the blockchain transaction (CAIP-2 format)."},"address":{"type":"object","description":"Address of the contract that failed.","nullable":true},"methodName":{"type":"object","description":"Name of the method that failed.","nullable":true},"methodArgs":{"type":"object","description":"Arguments of the method that failed.","nullable":true},"error":{"description":"Error that caused the command to fail.","nullable":true,"allOf":[{"$ref":"#/components/schemas/BlockchainTransactionError"}]},"rawError":{"type":"string","description":"Raw error that caused the command to fail."}},"required":["chainId","address","methodName","methodArgs","error","rawError"]},"BlockchainTransactionError":{"type":"object","properties":{"name":{"type":"string","description":"Error name."},"args":{"type":"string","description":"Error arguments."},"signature":{"type":"string","description":"Error signature."},"selector":{"type":"string","description":"Error selector."}},"required":["name","args","signature","selector"]},"CommandCreatedEventType":{"type":"string","enum":["COMMAND_CREATED"],"description":"Command event type"},"CommandSucceededEventResponse":{"type":"object","properties":{"relayId":{"type":"string","description":"Associated relay id, that was used to crate this command."},"transactions":{"description":"List of transactions associated with the command event.","type":"array","items":{"$ref":"#/components/schemas/CommandTransaction"}},"failureData":{"description":"Failure data for a failed command. Only present for failed commands. Contains information about the error that caused the command to fail.","nullable":true,"allOf":[{"$ref":"#/components/schemas/CommandFailureData"}]},"type":{"description":"Command event type","allOf":[{"$ref":"#/components/schemas/CommandSucceededEventType"}]}},"required":["relayId","transactions","failureData","type"]},"CommandSucceededEventType":{"type":"string","enum":["COMMAND_SUCCEEDED"],"description":"Command event type"},"CommandFailedEventResponse":{"type":"object","properties":{"relayId":{"type":"string","description":"Associated relay id, that was used to crate this command."},"transactions":{"description":"List of transactions associated with the command event.","type":"array","items":{"$ref":"#/components/schemas/CommandTransaction"}},"failureData":{"description":"Failure data for a failed command. Only present for failed commands. Contains information about the error that caused the command to fail.","nullable":true,"allOf":[{"$ref":"#/components/schemas/CommandFailureData"}]},"type":{"description":"Command event type","allOf":[{"$ref":"#/components/schemas/CommandFailedEventType"}]}},"required":["relayId","transactions","failureData","type"]},"CommandFailedEventType":{"type":"string","enum":["COMMAND_FAILED"],"description":"Command event type"},"CommandUpdatedEventResponse":{"type":"object","properties":{"relayId":{"type":"string","description":"Associated relay id, that was used to crate this command."},"transactions":{"description":"List of transactions associated with the command event.","type":"array","items":{"$ref":"#/components/schemas/CommandTransaction"}},"failureData":{"description":"Failure data for a failed command. Only present for failed commands. Contains information about the error that caused the command to fail.","nullable":true,"allOf":[{"$ref":"#/components/schemas/CommandFailureData"}]},"type":{"description":"Command event type","allOf":[{"$ref":"#/components/schemas/CommandUpdatedEventType"}]}},"required":["relayId","transactions","failureData","type"]},"CommandUpdatedEventType":{"type":"string","enum":["COMMAND_UPDATED"],"description":"Command event type"},"NameClaimRequestedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"domaRecordAddress":{"type":"string","description":"Address of a Doma Record Smart Contract."},"name":{"type":"string","description":"Associated name."},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."},"claimedBy":{"type":"string","description":"Wallet address that was used for claim. CAIP-10 format."},"proofSource":{"description":"Indicates the source of contacts information. \"registrantHandle\" is used as a unique identifier within this source. Following values are possible: \n0 - NONE. No contacts provided. Only used for EOIs. \n1 - REGISTRAR. Registrar has approved claim, and holds the contact information. \"registrantHandle\" holds internal registrar-supplied identifier. \n2 - DOMA. Doma has approved claim, and holds the contact information. \"registrantHandle\" holds contacts identifier in Doma API, that should be used with /registrant/{handle} API to obtain encrypted contact information.","allOf":[{"$ref":"#/components/schemas/ProofSource"}]},"registrantHandle":{"type":"string","description":"Unique identifier that can be used to obtain contact information."},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameClaimRequestedEventType"}]}},"required":["networkId","finalized","blockNumber","domaRecordAddress","name","claimedBy","proofSource","registrantHandle","type"]},"NameClaimRequestedEventType":{"type":"string","enum":["NAME_CLAIM_REQUESTED"],"description":"Blockchain event type."},"NameClaimApprovedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"domaRecordAddress":{"type":"string","description":"Address of a Doma Record Smart Contract."},"name":{"type":"string","description":"Associated name."},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."},"claimedBy":{"type":"string","description":"Wallet address that was used for claim. CAIP-10 format."},"proofSource":{"description":"Indicates the source of contacts information. \"registrantHandle\" is used as a unique identifier within this source. Following values are possible: \n0 - NONE. No contacts provided. Only used for EOIs. \n1 - REGISTRAR. Registrar has approved claim, and holds the contact information. \"registrantHandle\" holds internal registrar-supplied identifier. \n2 - DOMA. Doma has approved claim, and holds the contact information. \"registrantHandle\" holds contacts identifier in Doma API, that should be used with /registrant/{handle} API to obtain encrypted contact information.","allOf":[{"$ref":"#/components/schemas/ProofSource"}]},"registrantHandle":{"type":"string","description":"Unique identifier that can be used to obtain contact information."},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameClaimApprovedEventType"}]}},"required":["networkId","finalized","blockNumber","domaRecordAddress","name","claimedBy","proofSource","registrantHandle","type"]},"NameClaimApprovedEventType":{"type":"string","enum":["NAME_CLAIM_APPROVED"],"description":"Blockchain event type."},"NameClaimRejectedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"domaRecordAddress":{"type":"string","description":"Address of a Doma Record Smart Contract."},"name":{"type":"string","description":"Associated name."},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."},"claimedBy":{"type":"string","description":"Wallet address that was used for claim. CAIP-10 format."},"proofSource":{"description":"Indicates the source of contacts information. \"registrantHandle\" is used as a unique identifier within this source. Following values are possible: \n0 - NONE. No contacts provided. Only used for EOIs. \n1 - REGISTRAR. Registrar has approved claim, and holds the contact information. \"registrantHandle\" holds internal registrar-supplied identifier. \n2 - DOMA. Doma has approved claim, and holds the contact information. \"registrantHandle\" holds contacts identifier in Doma API, that should be used with /registrant/{handle} API to obtain encrypted contact information.","allOf":[{"$ref":"#/components/schemas/ProofSource"}]},"registrantHandle":{"type":"string","description":"Unique identifier that can be used to obtain contact information."},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameClaimRejectEventType"}]}},"required":["networkId","finalized","blockNumber","domaRecordAddress","name","claimedBy","proofSource","registrantHandle","type"]},"NameClaimRejectEventType":{"type":"string","enum":["NAME_CLAIM_REJECTED"],"description":"Blockchain event type."},"NameNameServersUpdateRequestedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameNameserversUpdateRequestedEventType"}]},"sld":{"type":"string","description":"Second-level domain (SLD) of the name."},"tld":{"type":"string","description":"Top-level domain (TLD) of the name."},"nameservers":{"description":"Nameservers to set for the name.","type":"array","items":{"type":"string"}},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."}},"required":["networkId","finalized","blockNumber","type","sld","tld","nameservers"]},"NameNameserversUpdateRequestedEventType":{"type":"string","enum":["NAME_NAMESERVERS_UPDATE_REQUESTED"],"description":"Blockchain event type."},"NameDsKeysUpdateRequestedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameDsKeysUpdateRequestedEventType"}]},"sld":{"type":"string","description":"Second-level domain (SLD) of the name."},"tld":{"type":"string","description":"Top-level domain (TLD) of the name."},"dsKeys":{"description":"List of DS Keys.","type":"array","items":{"$ref":"#/components/schemas/DsKeyResponseData"}},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."}},"required":["networkId","finalized","blockNumber","type","sld","tld","dsKeys"]},"NameDsKeysUpdateRequestedEventType":{"type":"string","enum":["NAME_DS_KEYS_UPDATE_REQUESTED"],"description":"Blockchain event type."},"NameDNSRRSetUpdateRequestedResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/NameDNSRRSetUpdateRequestedEventType"}]},"sld":{"type":"string","description":"Second-level domain (SLD) of the name."},"tld":{"type":"string","description":"Top-level domain (TLD) of the name."},"host":{"type":"string","description":"The host for the DNS record."},"recordType":{"type":"string","description":"The type of the DNS record."},"ttl":{"type":"number","description":"The time-to-live (TTL) value for the DNS record."},"records":{"description":"The list of DNS record values.","type":"array","items":{"type":"string"}},"useDomaNameservers":{"type":"boolean","description":"Indicates whether to use Doma nameservers."},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."}},"required":["networkId","finalized","blockNumber","type","sld","tld","host","recordType","ttl","records","useDomaNameservers"]},"NameDNSRRSetUpdateRequestedEventType":{"type":"string","enum":["NAME_DNS_RR_SET_UPDATE_REQUESTED"],"description":"Blockchain event type."},"SyntheticTokenMintedEventResponse":{"type":"object","properties":{"networkId":{"type":"string","description":"CAIP2 Chain Id."},"finalized":{"type":"boolean","description":"Whether this even is finalized on-chain."},"txHash":{"type":"string","description":"Transaction hash."},"blockNumber":{"type":"string","description":"Block height from which the event was indexed."},"logIndex":{"type":"number","description":"Field specific for EVM chains. Index of a an even log in a block. Together with blockNumber and networkId it uniquely identifies an event."},"tokenAddress":{"type":"string","description":"NFT contract address"},"tokenId":{"type":"string","description":"Token ID"},"type":{"description":"Blockchain event type.","allOf":[{"$ref":"#/components/schemas/SyntheticTokenMintedEventType"}]},"registrarIanaId":{"type":"number","description":"Registrar IANA ID."},"owner":{"type":"string","description":"Token owner address."},"ownership":{"type":"boolean","description":"Whether the token has ownership."},"revocable":{"type":"boolean","description":"Whether the token is revocable."},"name":{"type":"string","description":"Associated name."},"expiresAt":{"type":"string","description":"Expiration date. ISO8601 format.","format":"date-time"},"capabilities":{"type":"array","description":"Array of capabilities granted to this synthetic token.","items":{"$ref":"#/components/schemas/Capability"}},"host":{"type":"string","description":"Host subdomain."},"groupId":{"type":"string","description":"Group ID for related synthetic tokens."},"correlationId":{"type":"string","description":"Correlation ID. Used to associate events with each other. Events that are part of the same operation will have the same correlation ID."}},"required":["networkId","finalized","blockNumber","tokenAddress","tokenId","type","registrarIanaId","owner","ownership","revocable","name","expiresAt","capabilities","host","groupId"]},"SyntheticTokenMintedEventType":{"type":"string","enum":["SYNTHETIC_TOKEN_MINTED"],"description":"Blockchain event type."},"Capability":{"type":"string","enum":["REGISTRY_RECORDS_MANAGEMENT","DNS_RECORDS_MANAGEMENT"],"description":"Array of capabilities granted to this synthetic token."},"NameTokenListedEventResponse":{"type":"object","properties":{"type":{"description":"Marketplace event type","allOf":[{"$ref":"#/components/schemas/NameTokenListedEventType"}]},"tokenId":{"type":"string","description":"Token ID"},"tokenAddress":{"type":"string","description":"NFT contract address"},"orderbook":{"description":"Orderbook type","allOf":[{"$ref":"#/components/schemas/OrderbookType"}]},"orderId":{"type":"string","description":"Order ID"},"createdAt":{"type":"string","description":"Event creation timestamp","format":"date-time"},"startsAt":{"type":"string","description":"Start date of the listing. ISO8601 format.","format":"date-time"},"expiresAt":{"type":"string","description":"Expiration date of the listing. ISO8601 format.","format":"date-time"},"seller":{"type":"string","description":"Seller address"},"buyer":{"type":"string","description":"Buyer address if this is a private listing"},"payment":{"description":"Payment information","allOf":[{"$ref":"#/components/schemas/PaymentInfoResponse"}]}},"required":["type","orderbook","createdAt","startsAt","expiresAt","seller","payment"]},"NameTokenListedEventType":{"type":"string","enum":["NAME_TOKEN_LISTED"],"description":"Marketplace event type"},"OrderbookType":{"type":"string","enum":["DOMA","OPENSEA"],"description":"Orderbook type"},"PaymentInfoResponse":{"type":"object","properties":{"price":{"type":"string","description":"Price in the smallest unit of the currency"},"tokenAddress":{"type":"string","description":"Token address used for payment"},"currencySymbol":{"type":"string","description":"Currency symbol"}},"required":["price","tokenAddress","currencySymbol"]},"NameTokenOfferReceivedEventResponse":{"type":"object","properties":{"type":{"description":"Marketplace event type","allOf":[{"$ref":"#/components/schemas/NameTokenOfferReceivedEventType"}]},"tokenId":{"type":"string","description":"Token ID"},"tokenAddress":{"type":"string","description":"NFT contract address"},"orderbook":{"description":"Orderbook type","allOf":[{"$ref":"#/components/schemas/OrderbookType"}]},"orderId":{"type":"string","description":"Order ID"},"createdAt":{"type":"string","description":"Event creation timestamp","format":"date-time"},"expiresAt":{"type":"string","description":"Expiration date of the offer. ISO8601 format.","format":"date-time"},"buyer":{"type":"string","description":"Buyer address"},"seller":{"type":"string","description":"Seller address"},"payment":{"description":"Payment information","allOf":[{"$ref":"#/components/schemas/PaymentInfoResponse"}]}},"required":["type","orderbook","createdAt","expiresAt","buyer","seller","payment"]},"NameTokenOfferReceivedEventType":{"type":"string","enum":["NAME_TOKEN_OFFER_RECEIVED"],"description":"Marketplace event type"},"NameTokenListingCancelledEventResponse":{"type":"object","properties":{"type":{"description":"Marketplace event type","allOf":[{"$ref":"#/components/schemas/NameTokenListingCancelledEventType"}]},"tokenId":{"type":"string","description":"Token ID"},"tokenAddress":{"type":"string","description":"NFT contract address"},"orderbook":{"description":"Orderbook type","allOf":[{"$ref":"#/components/schemas/OrderbookType"}]},"orderId":{"type":"string","description":"Order ID"},"createdAt":{"type":"string","description":"Event creation timestamp","format":"date-time"},"seller":{"type":"string","description":"Address that cancelled the listing"}},"required":["type","orderbook","createdAt","seller"]},"NameTokenListingCancelledEventType":{"type":"string","enum":["NAME_TOKEN_LISTING_CANCELLED"],"description":"Marketplace event type"},"NameTokenOfferCancelledEventResponse":{"type":"object","properties":{"type":{"description":"Marketplace event type","allOf":[{"$ref":"#/components/schemas/NameTokenOfferCancelledEventType"}]},"tokenId":{"type":"string","description":"Token ID"},"tokenAddress":{"type":"string","description":"NFT contract address"},"orderbook":{"description":"Orderbook type","allOf":[{"$ref":"#/components/schemas/OrderbookType"}]},"orderId":{"type":"string","description":"Order ID"},"createdAt":{"type":"string","description":"Event creation timestamp","format":"date-time"}},"required":["type","orderbook","createdAt"]},"NameTokenOfferCancelledEventType":{"type":"string","enum":["NAME_TOKEN_OFFER_CANCELLED"],"description":"Marketplace event type"},"NameTokenPurchasedEventResponse":{"type":"object","properties":{"type":{"description":"Marketplace event type","allOf":[{"$ref":"#/components/schemas/NameTokenPurchasedEventType"}]},"tokenId":{"type":"string","description":"Token ID"},"tokenAddress":{"type":"string","description":"NFT contract address"},"orderbook":{"description":"Orderbook type","allOf":[{"$ref":"#/components/schemas/OrderbookType"}]},"orderId":{"type":"string","description":"Order ID"},"createdAt":{"type":"string","description":"Event creation timestamp","format":"date-time"},"orderType":{"description":"Order type (listing vs offer)","allOf":[{"$ref":"#/components/schemas/OrderType"}]},"purchasedAt":{"type":"string","description":"Purchase date. ISO8601 format.","format":"date-time"},"seller":{"type":"string","description":"Seller address"},"buyer":{"type":"string","description":"Buyer address"},"payment":{"description":"Payment information","allOf":[{"$ref":"#/components/schemas/PaymentInfoResponse"}]},"txHash":{"type":"object","description":"Transaction hash"}},"required":["type","orderbook","createdAt","orderType","purchasedAt","seller","buyer","payment"]},"NameTokenPurchasedEventType":{"type":"string","enum":["NAME_TOKEN_PURCHASED"],"description":"Marketplace event type"},"OrderType":{"type":"string","enum":["LISTING","OFFER"],"description":"Order type (listing vs offer)"}}},"paths":{"/v1/poll":{"get":{"description":"Returns blockchain events that have occurred since the last acknowledged event.","operationId":"PollApiController_events","parameters":[{"name":"cursor","required":false,"in":"query","description":"Cursor identifier to use for tracking the polling state. Can be used to manage multiple independent polling states.","schema":{"maxLength":255}},{"name":"eventTypes","required":false,"in":"query","description":"Filter events by type. Can be provided multiple times to filter by multiple types.","schema":{"type":"array","items":{"type":"string","enum":["NAME_TOKENIZATION_REQUESTED","NAME_TOKENIZATION_REJECTED","NAME_TOKENIZED","NAME_UPDATED","NAME_RENEWED","NAME_CLAIMED","NAME_CLAIM_REQUESTED","NAME_CLAIM_REJECTED","NAME_CLAIM_APPROVED","NAME_DETOKENIZED","NAME_TOKEN_MINTED","NAME_TOKEN_TRANSFERRED","NAME_TOKEN_RENEWED","NAME_TOKEN_BURNED","NAME_TOKEN_APPROVED_FOR_ALL","NAME_TOKEN_TRANSFER_APPROVED","NAME_TOKEN_TRANSFER_APPROVAL_REVOKED","NAME_TOKEN_LOCK_STATUS_CHANGED","NAME_TOKEN_BRIDGED","PAYMENT_FULFILLED","NAME_TOKEN_BOUGHT_OUT","NAME_NAMESERVERS_UPDATE_REQUESTED","NAME_DS_KEYS_UPDATE_REQUESTED","NAME_DNS_RR_SET_UPDATE_REQUESTED","SYNTHETIC_TOKEN_MINTED","NAME_TOKEN_LISTED","NAME_TOKEN_OFFER_RECEIVED","NAME_TOKEN_LISTING_CANCELLED","NAME_TOKEN_OFFER_CANCELLED","NAME_TOKEN_PURCHASED","NAME_TOKENS_BULK_LISTED","COMMAND_CREATED","COMMAND_SUCCEEDED","COMMAND_FAILED","COMMAND_UPDATED"]}}},{"name":"limit","required":false,"in":"query","description":"Maximum number of events to return in a single response page.","schema":{"minimum":1}},{"name":"includeSynthetics","required":false,"in":"query","description":"Whether to include synthetic token events.","schema":{"default":false,"type":"boolean"}},{"name":"finalizedOnly","required":false,"in":"query","description":"Whether to return only finalized events.","schema":{"default":true,"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventsPaginatedResponse"}}}},"400":{"description":"Bad Request. Invalid query parameters."},"401":{"description":"Unauthorized. API Key is missing or invalid."},"403":{"description":"Forbidden. API Key is missing 'EVENTS' permission."}},"summary":"Poll for new Doma Protocol events","tags":["Events Poll API"]}}}}
```

## Acknowledge received events

> Updates the last acknowledged event id for the client.

```json
{"openapi":"3.0.0","info":{"title":"Doma Registrar API","version":"1.0"},"servers":["https://api.doma.xyz","https://api-testnet.doma.xyz"],"security":[{"Api-Key":[]}],"components":{"securitySchemes":{"Api-Key":{"type":"apiKey","in":"header","name":"Api-Key"}}},"paths":{"/v1/poll/ack/{lastEventId}":{"post":{"description":"Updates the last acknowledged event id for the client.","operationId":"PollApiController_acknowledgeEvent","parameters":[{"name":"cursor","required":false,"in":"query","description":"Cursor identifier to use for tracking the polling state. Can be used to manage multiple independent polling states.","schema":{"maxLength":255}},{"name":"lastEventId","required":false,"in":"path","description":"Last event id that was processed by the client.","schema":{"type":"integer"}}],"responses":{"200":{"description":""},"400":{"description":"Bad Request. Invalid event id."},"401":{"description":"Unauthorized. API Key is missing or invalid."},"403":{"description":"Forbidden. API Key is missing 'EVENTS' permission."}},"summary":"Acknowledge received events","tags":["Events Poll API"]}}}}
```

## Reset last acknowledged event

> Updates the last acknowledged event id for the client. Can be used to reset the polling state to re-consume events.

```json
{"openapi":"3.0.0","info":{"title":"Doma Registrar API","version":"1.0"},"servers":["https://api.doma.xyz","https://api-testnet.doma.xyz"],"security":[{"Api-Key":[]}],"components":{"securitySchemes":{"Api-Key":{"type":"apiKey","in":"header","name":"Api-Key"}}},"paths":{"/v1/poll/reset/{eventId}":{"post":{"description":"Updates the last acknowledged event id for the client. Can be used to reset the polling state to re-consume events.","operationId":"PollApiController_resetLastAcknowledgedEventId","parameters":[{"name":"cursor","required":false,"in":"query","description":"Cursor identifier to use for tracking the polling state. Can be used to manage multiple independent polling states.","schema":{"maxLength":255}},{"name":"eventId","required":false,"in":"path","description":"Event id to reset the last acknowledged event id to.","schema":{"type":"integer"}}],"responses":{"200":{"description":""},"400":{"description":"Bad Request. Invalid event id."},"401":{"description":"Unauthorized. API Key is missing or invalid."},"403":{"description":"Forbidden. API Key is missing 'EVENTS' permission."}},"summary":"Reset last acknowledged event","tags":["Events Poll API"]}}}}
```


# Orderbook API

{% hint style="info" %}
API is subject to [Authentication and Rate Limit](/api-reference/authentication-and-rate-limits) rules.
{% endhint %}

## Create Listing

> Create a fixed priced listing on a supported orderbook (OpenSea, Doma).

```json
{"openapi":"3.0.0","info":{"title":"Doma Registrar API","version":"1.0"},"servers":["https://api.doma.xyz","https://api-testnet.doma.xyz"],"security":[{"Api-Key":[]}],"components":{"securitySchemes":{"Api-Key":{"type":"apiKey","in":"header","name":"Api-Key"}},"schemas":{"CreateOrderRequestBody":{"type":"object","properties":{"orderbook":{"type":"string","description":"Orderbook identifier."},"chainId":{"type":"string","description":"Chain ID in CAIP-2 format.","pattern":"^[a-z0-9]+:[a-zA-Z0-9]+$"},"parameters":{"description":"Order parameters.","allOf":[{"$ref":"#/components/schemas/OrderComponents"}]},"signature":{"type":"string","description":"Order signature."},"cancelExisting":{"type":"boolean","description":"Cancel existing order if it exists."},"cancelSignatures":{"type":"object","description":"Map of order IDs to cancellation signatures. Required for OpenSea orderbook when canceling existing orders.","additionalProperties":{"type":"string"}}},"required":["orderbook","chainId","parameters","signature","cancelExisting"]},"OrderComponents":{"type":"object","properties":{"offerer":{"type":"string","description":"Address of the offerer."},"zone":{"type":"string","description":"Zone address."},"orderType":{"type":"number","description":"Type of order.","enum":[0,1,2,3]},"startTime":{"type":"string","description":"Start time of the order (Unix timestamp as string)."},"endTime":{"type":"string","description":"End time of the order (Unix timestamp as string)."},"zoneHash":{"type":"string","description":"Zone hash."},"salt":{"type":"string","description":"Salt for the order."},"offer":{"description":"Array of offer items.","type":"array","items":{"$ref":"#/components/schemas/OfferItem"}},"consideration":{"description":"Array of consideration items. Considerations must include following fee items: Doma Marketplace fee, Name Token Royalties, OpenSea Fees (only for OpenSea Orderbook).","type":"array","items":{"$ref":"#/components/schemas/ConsiderationItem"}},"totalOriginalConsiderationItems":{"type":"number","description":"Total number of original consideration items."},"conduitKey":{"type":"string","description":"Conduit key."},"counter":{"type":"string","description":"Counter for the order (as string)."}},"required":["offerer","zone","orderType","startTime","endTime","zoneHash","salt","offer","consideration","totalOriginalConsiderationItems","conduitKey","counter"]},"OfferItem":{"type":"object","properties":{"itemType":{"type":"number","description":"The type of item being offered.","enum":[0,1,2,3,4,5]},"token":{"type":"string","description":"Token address of the item being offered."},"identifierOrCriteria":{"type":"string","description":"Identifier or criteria for the item."},"startAmount":{"type":"string","description":"Starting amount for the item."},"endAmount":{"type":"string","description":"Ending amount for the item."}},"required":["itemType","token","identifierOrCriteria","startAmount","endAmount"]},"ConsiderationItem":{"type":"object","properties":{"itemType":{"type":"number","description":"The type of item being offered.","enum":[0,1,2,3,4,5]},"token":{"type":"string","description":"Token address of the item being offered."},"identifierOrCriteria":{"type":"string","description":"Identifier or criteria for the item."},"startAmount":{"type":"string","description":"Starting amount for the item."},"endAmount":{"type":"string","description":"Ending amount for the item."},"recipient":{"type":"string","description":"Recipient address to receive the consideration item."}},"required":["itemType","token","identifierOrCriteria","startAmount","endAmount","recipient"]},"CreateOrderResponse":{"type":"object","properties":{"orderId":{"type":"string","description":"The unique identifier for the created order."},"orderData":{"description":"Order data.","allOf":[{"$ref":"#/components/schemas/OrderComponents"}]},"signature":{"type":"string","description":"Order signature."}},"required":["orderId","orderData","signature"]}}},"paths":{"/v1/orderbook/list":{"post":{"description":"Create a fixed priced listing on a supported orderbook (OpenSea, Doma).","operationId":"OrderbookApiController_createListing","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderRequestBody"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderResponse"}}}},"400":{"description":"Bad Request. Invalid query parameters."},"401":{"description":"Unauthorized. API Key is missing or invalid."},"403":{"description":"Forbidden. API Key is missing 'ORDERBOOK' permission."}},"summary":"Create Listing","tags":["Orderbook API"]}}}}
```

## Create Offer

> Create an offer on a supported orderbook (OpenSea, Doma).

```json
{"openapi":"3.0.0","info":{"title":"Doma Registrar API","version":"1.0"},"servers":["https://api.doma.xyz","https://api-testnet.doma.xyz"],"security":[{"Api-Key":[]}],"components":{"securitySchemes":{"Api-Key":{"type":"apiKey","in":"header","name":"Api-Key"}},"schemas":{"CreateOrderRequestBody":{"type":"object","properties":{"orderbook":{"type":"string","description":"Orderbook identifier."},"chainId":{"type":"string","description":"Chain ID in CAIP-2 format.","pattern":"^[a-z0-9]+:[a-zA-Z0-9]+$"},"parameters":{"description":"Order parameters.","allOf":[{"$ref":"#/components/schemas/OrderComponents"}]},"signature":{"type":"string","description":"Order signature."},"cancelExisting":{"type":"boolean","description":"Cancel existing order if it exists."},"cancelSignatures":{"type":"object","description":"Map of order IDs to cancellation signatures. Required for OpenSea orderbook when canceling existing orders.","additionalProperties":{"type":"string"}}},"required":["orderbook","chainId","parameters","signature","cancelExisting"]},"OrderComponents":{"type":"object","properties":{"offerer":{"type":"string","description":"Address of the offerer."},"zone":{"type":"string","description":"Zone address."},"orderType":{"type":"number","description":"Type of order.","enum":[0,1,2,3]},"startTime":{"type":"string","description":"Start time of the order (Unix timestamp as string)."},"endTime":{"type":"string","description":"End time of the order (Unix timestamp as string)."},"zoneHash":{"type":"string","description":"Zone hash."},"salt":{"type":"string","description":"Salt for the order."},"offer":{"description":"Array of offer items.","type":"array","items":{"$ref":"#/components/schemas/OfferItem"}},"consideration":{"description":"Array of consideration items. Considerations must include following fee items: Doma Marketplace fee, Name Token Royalties, OpenSea Fees (only for OpenSea Orderbook).","type":"array","items":{"$ref":"#/components/schemas/ConsiderationItem"}},"totalOriginalConsiderationItems":{"type":"number","description":"Total number of original consideration items."},"conduitKey":{"type":"string","description":"Conduit key."},"counter":{"type":"string","description":"Counter for the order (as string)."}},"required":["offerer","zone","orderType","startTime","endTime","zoneHash","salt","offer","consideration","totalOriginalConsiderationItems","conduitKey","counter"]},"OfferItem":{"type":"object","properties":{"itemType":{"type":"number","description":"The type of item being offered.","enum":[0,1,2,3,4,5]},"token":{"type":"string","description":"Token address of the item being offered."},"identifierOrCriteria":{"type":"string","description":"Identifier or criteria for the item."},"startAmount":{"type":"string","description":"Starting amount for the item."},"endAmount":{"type":"string","description":"Ending amount for the item."}},"required":["itemType","token","identifierOrCriteria","startAmount","endAmount"]},"ConsiderationItem":{"type":"object","properties":{"itemType":{"type":"number","description":"The type of item being offered.","enum":[0,1,2,3,4,5]},"token":{"type":"string","description":"Token address of the item being offered."},"identifierOrCriteria":{"type":"string","description":"Identifier or criteria for the item."},"startAmount":{"type":"string","description":"Starting amount for the item."},"endAmount":{"type":"string","description":"Ending amount for the item."},"recipient":{"type":"string","description":"Recipient address to receive the consideration item."}},"required":["itemType","token","identifierOrCriteria","startAmount","endAmount","recipient"]},"CreateOrderResponse":{"type":"object","properties":{"orderId":{"type":"string","description":"The unique identifier for the created order."},"orderData":{"description":"Order data.","allOf":[{"$ref":"#/components/schemas/OrderComponents"}]},"signature":{"type":"string","description":"Order signature."}},"required":["orderId","orderData","signature"]}}},"paths":{"/v1/orderbook/offer":{"post":{"description":"Create an offer on a supported orderbook (OpenSea, Doma).","operationId":"OrderbookApiController_createOffer","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderRequestBody"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderResponse"}}}},"400":{"description":"Bad Request. Invalid query parameters."},"401":{"description":"Unauthorized. API Key is missing or invalid."},"403":{"description":"Forbidden. API Key is missing 'ORDERBOOK' permission."}},"summary":"Create Offer","tags":["Orderbook API"]}}}}
```

## Get orderbook fees

> Get marketplace fees for a specific orderbook and chain.

```json
{"openapi":"3.0.0","info":{"title":"Doma Registrar API","version":"1.0"},"servers":["https://api.doma.xyz","https://api-testnet.doma.xyz"],"security":[{"Api-Key":[]}],"components":{"securitySchemes":{"Api-Key":{"type":"apiKey","in":"header","name":"Api-Key"}},"schemas":{"GetOrderbookFeeResponse":{"type":"object","properties":{"marketplaceFees":{"description":"Array of marketplace fees.","type":"array","items":{"$ref":"#/components/schemas/OrderbookFee"}}},"required":["marketplaceFees"]},"OrderbookFee":{"type":"object","properties":{"recipient":{"type":"string","description":"Fee recipient address."},"basisPoints":{"type":"number","description":"Fee amount in basis points (e.g., 250 = 2.5%)."},"feeType":{"type":"string","description":"Fee type.","enum":["DOMA","OPENSEA","ROYALTY"]}},"required":["recipient","basisPoints","feeType"]}}},"paths":{"/v1/orderbook/fee/{orderbook}/{chainId}/{contractAddress}":{"get":{"description":"Get marketplace fees for a specific orderbook and chain.","operationId":"OrderbookApiController_getOrderbookFee","parameters":[{"name":"orderbook","required":true,"in":"path","description":"The orderbook type","schema":{"type":"string"}},{"name":"chainId","required":true,"in":"path","description":"The chain ID in CAIP-2 format","schema":{"type":"string"}},{"name":"contractAddress","required":true,"in":"path","description":"The contract address of the token being listed or offered.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetOrderbookFeeResponse"}}}},"400":{"description":"Bad Request. Invalid path parameters."},"401":{"description":"Unauthorized. API Key is missing or invalid."},"403":{"description":"Forbidden. API Key is missing 'ORDERBOOK' permission."}},"summary":"Get orderbook fees","tags":["Orderbook API"]}}}}
```

## Get Listing fulfillment data

> Get listing fulfillment data by order id and buyer address.

```json
{"openapi":"3.0.0","info":{"title":"Doma Registrar API","version":"1.0"},"servers":["https://api.doma.xyz","https://api-testnet.doma.xyz"],"security":[{"Api-Key":[]}],"components":{"securitySchemes":{"Api-Key":{"type":"apiKey","in":"header","name":"Api-Key"}},"schemas":{"GetOrderByIdResponse":{"type":"object","properties":{"order":{"description":"Order containing parameters and signature.","allOf":[{"$ref":"#/components/schemas/OrderResponse"}]},"extraData":{"type":"object","description":"Extra data for seaport fulfilment."}},"required":["order"]},"OrderResponse":{"type":"object","properties":{"parameters":{"description":"Order parameters.","allOf":[{"$ref":"#/components/schemas/OrderComponents"}]},"signature":{"type":"string","description":"Order signature."}},"required":["parameters","signature"]},"OrderComponents":{"type":"object","properties":{"offerer":{"type":"string","description":"Address of the offerer."},"zone":{"type":"string","description":"Zone address."},"orderType":{"type":"number","description":"Type of order.","enum":[0,1,2,3]},"startTime":{"type":"string","description":"Start time of the order (Unix timestamp as string)."},"endTime":{"type":"string","description":"End time of the order (Unix timestamp as string)."},"zoneHash":{"type":"string","description":"Zone hash."},"salt":{"type":"string","description":"Salt for the order."},"offer":{"description":"Array of offer items.","type":"array","items":{"$ref":"#/components/schemas/OfferItem"}},"consideration":{"description":"Array of consideration items. Considerations must include following fee items: Doma Marketplace fee, Name Token Royalties, OpenSea Fees (only for OpenSea Orderbook).","type":"array","items":{"$ref":"#/components/schemas/ConsiderationItem"}},"totalOriginalConsiderationItems":{"type":"number","description":"Total number of original consideration items."},"conduitKey":{"type":"string","description":"Conduit key."},"counter":{"type":"string","description":"Counter for the order (as string)."}},"required":["offerer","zone","orderType","startTime","endTime","zoneHash","salt","offer","consideration","totalOriginalConsiderationItems","conduitKey","counter"]},"OfferItem":{"type":"object","properties":{"itemType":{"type":"number","description":"The type of item being offered.","enum":[0,1,2,3,4,5]},"token":{"type":"string","description":"Token address of the item being offered."},"identifierOrCriteria":{"type":"string","description":"Identifier or criteria for the item."},"startAmount":{"type":"string","description":"Starting amount for the item."},"endAmount":{"type":"string","description":"Ending amount for the item."}},"required":["itemType","token","identifierOrCriteria","startAmount","endAmount"]},"ConsiderationItem":{"type":"object","properties":{"itemType":{"type":"number","description":"The type of item being offered.","enum":[0,1,2,3,4,5]},"token":{"type":"string","description":"Token address of the item being offered."},"identifierOrCriteria":{"type":"string","description":"Identifier or criteria for the item."},"startAmount":{"type":"string","description":"Starting amount for the item."},"endAmount":{"type":"string","description":"Ending amount for the item."},"recipient":{"type":"string","description":"Recipient address to receive the consideration item."}},"required":["itemType","token","identifierOrCriteria","startAmount","endAmount","recipient"]}}},"paths":{"/v1/orderbook/listing/{orderId}/{buyer}":{"get":{"description":"Get listing fulfillment data by order id and buyer address.","operationId":"OrderbookApiController_getListingById","parameters":[{"name":"orderId","required":true,"in":"path","description":"The Order ID of the listing.","schema":{"type":"string"}},{"name":"buyer","required":true,"in":"path","description":"Buyer address, in native chain format.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetOrderByIdResponse"}}}},"400":{"description":"Bad Request. Invalid query parameters."},"401":{"description":"Unauthorized. API Key is missing or invalid."},"403":{"description":"Forbidden. API Key is missing 'ORDERBOOK' permission."}},"summary":"Get Listing fulfillment data","tags":["Orderbook API"]}}}}
```

## Get Offer fulfillment data

> Get offer fulfillment data by order id and fulfiller (token owner) address.

```json
{"openapi":"3.0.0","info":{"title":"Doma Registrar API","version":"1.0"},"servers":["https://api.doma.xyz","https://api-testnet.doma.xyz"],"security":[{"Api-Key":[]}],"components":{"securitySchemes":{"Api-Key":{"type":"apiKey","in":"header","name":"Api-Key"}},"schemas":{"GetOrderByIdResponse":{"type":"object","properties":{"order":{"description":"Order containing parameters and signature.","allOf":[{"$ref":"#/components/schemas/OrderResponse"}]},"extraData":{"type":"object","description":"Extra data for seaport fulfilment."}},"required":["order"]},"OrderResponse":{"type":"object","properties":{"parameters":{"description":"Order parameters.","allOf":[{"$ref":"#/components/schemas/OrderComponents"}]},"signature":{"type":"string","description":"Order signature."}},"required":["parameters","signature"]},"OrderComponents":{"type":"object","properties":{"offerer":{"type":"string","description":"Address of the offerer."},"zone":{"type":"string","description":"Zone address."},"orderType":{"type":"number","description":"Type of order.","enum":[0,1,2,3]},"startTime":{"type":"string","description":"Start time of the order (Unix timestamp as string)."},"endTime":{"type":"string","description":"End time of the order (Unix timestamp as string)."},"zoneHash":{"type":"string","description":"Zone hash."},"salt":{"type":"string","description":"Salt for the order."},"offer":{"description":"Array of offer items.","type":"array","items":{"$ref":"#/components/schemas/OfferItem"}},"consideration":{"description":"Array of consideration items. Considerations must include following fee items: Doma Marketplace fee, Name Token Royalties, OpenSea Fees (only for OpenSea Orderbook).","type":"array","items":{"$ref":"#/components/schemas/ConsiderationItem"}},"totalOriginalConsiderationItems":{"type":"number","description":"Total number of original consideration items."},"conduitKey":{"type":"string","description":"Conduit key."},"counter":{"type":"string","description":"Counter for the order (as string)."}},"required":["offerer","zone","orderType","startTime","endTime","zoneHash","salt","offer","consideration","totalOriginalConsiderationItems","conduitKey","counter"]},"OfferItem":{"type":"object","properties":{"itemType":{"type":"number","description":"The type of item being offered.","enum":[0,1,2,3,4,5]},"token":{"type":"string","description":"Token address of the item being offered."},"identifierOrCriteria":{"type":"string","description":"Identifier or criteria for the item."},"startAmount":{"type":"string","description":"Starting amount for the item."},"endAmount":{"type":"string","description":"Ending amount for the item."}},"required":["itemType","token","identifierOrCriteria","startAmount","endAmount"]},"ConsiderationItem":{"type":"object","properties":{"itemType":{"type":"number","description":"The type of item being offered.","enum":[0,1,2,3,4,5]},"token":{"type":"string","description":"Token address of the item being offered."},"identifierOrCriteria":{"type":"string","description":"Identifier or criteria for the item."},"startAmount":{"type":"string","description":"Starting amount for the item."},"endAmount":{"type":"string","description":"Ending amount for the item."},"recipient":{"type":"string","description":"Recipient address to receive the consideration item."}},"required":["itemType","token","identifierOrCriteria","startAmount","endAmount","recipient"]}}},"paths":{"/v1/orderbook/offer/{orderId}/{fulfiller}":{"get":{"description":"Get offer fulfillment data by order id and fulfiller (token owner) address.","operationId":"OrderbookApiController_getOfferById","parameters":[{"name":"orderId","required":true,"in":"path","description":"The Order ID of the offer.","schema":{"type":"string"}},{"name":"fulfiller","required":true,"in":"path","description":"Fulfiller address, in native chain format. This is the address that owns the name token.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetOrderByIdResponse"}}}},"400":{"description":"Bad Request. Invalid query parameters."},"401":{"description":"Unauthorized. API Key is missing or invalid."},"403":{"description":"Forbidden. API Key is missing 'ORDERBOOK' permission."}},"summary":"Get Offer fulfillment data","tags":["Orderbook API"]}}}}
```

## Cancel Listing

> Cancel a listing on a supported orderbook (OpenSea, Doma).

```json
{"openapi":"3.0.0","info":{"title":"Doma Registrar API","version":"1.0"},"servers":["https://api.doma.xyz","https://api-testnet.doma.xyz"],"security":[{"Api-Key":[]}],"components":{"securitySchemes":{"Api-Key":{"type":"apiKey","in":"header","name":"Api-Key"}},"schemas":{"CancelOrderRequestBody":{"type":"object","properties":{"orderId":{"type":"string","description":"The Order ID to cancel."},"signature":{"type":"string","description":"EIP-712 signature for cancel authorization."}},"required":["orderId","signature"]},"CancelOrderResponse":{"type":"object","properties":{"orderId":{"type":"string","description":"The cancelled order ID."}},"required":["orderId"]}}},"paths":{"/v1/orderbook/listing/cancel":{"post":{"description":"Cancel a listing on a supported orderbook (OpenSea, Doma).","operationId":"OrderbookApiController_cancelListing","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelOrderRequestBody"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelOrderResponse"}}}},"400":{"description":"Bad Request. Invalid signature or order not found."},"401":{"description":"Unauthorized. API Key is missing or invalid."},"403":{"description":"Forbidden. API Key is missing 'ORDERBOOK' permission."}},"summary":"Cancel Listing","tags":["Orderbook API"]}}}}
```

## Cancel Offer

> Cancel an offer on a supported orderbook (OpenSea, Doma).

```json
{"openapi":"3.0.0","info":{"title":"Doma Registrar API","version":"1.0"},"servers":["https://api.doma.xyz","https://api-testnet.doma.xyz"],"security":[{"Api-Key":[]}],"components":{"securitySchemes":{"Api-Key":{"type":"apiKey","in":"header","name":"Api-Key"}},"schemas":{"CancelOrderRequestBody":{"type":"object","properties":{"orderId":{"type":"string","description":"The Order ID to cancel."},"signature":{"type":"string","description":"EIP-712 signature for cancel authorization."}},"required":["orderId","signature"]},"CancelOrderResponse":{"type":"object","properties":{"orderId":{"type":"string","description":"The cancelled order ID."}},"required":["orderId"]}}},"paths":{"/v1/orderbook/offer/cancel":{"post":{"description":"Cancel an offer on a supported orderbook (OpenSea, Doma).","operationId":"OrderbookApiController_cancelOffer","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelOrderRequestBody"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelOrderResponse"}}}},"400":{"description":"Bad Request. Invalid signature or order not found."},"401":{"description":"Unauthorized. API Key is missing or invalid."},"403":{"description":"Forbidden. API Key is missing 'ORDERBOOK' permission."}},"summary":"Cancel Offer","tags":["Orderbook API"]}}}}
```

## Get supported currencies

> Get all supported currency tokens for orderbook operations on a specific chain.

```json
{"openapi":"3.0.0","info":{"title":"Doma Registrar API","version":"1.0"},"servers":["https://api.doma.xyz","https://api-testnet.doma.xyz"],"security":[{"Api-Key":[]}],"components":{"securitySchemes":{"Api-Key":{"type":"apiKey","in":"header","name":"Api-Key"}},"schemas":{"CurrenciesResponse":{"type":"object","properties":{"currencies":{"description":"List of supported currency tokens for orderbook operations.","type":"array","items":{"$ref":"#/components/schemas/CurrencyTokenResponse"}}},"required":["currencies"]},"CurrencyTokenResponse":{"type":"object","properties":{"contractAddress":{"type":"object","description":"Token contract address."},"name":{"type":"string","description":"Token name."},"symbol":{"type":"string","description":"Currency symbol."},"decimals":{"type":"number","description":"Number of decimal places for the token."},"type":{"type":"string","description":"Indicates what operations this currency supports.","enum":["LISTING_ONLY","OFFER_ONLY","ALL"]},"nativeWrapper":{"type":"boolean","description":"Indicates if this currency is a wrapper token (e.g., WETH)."},"usdExchangeRate":{"type":"number","description":"USD exchange rate for the currency."}},"required":["name","symbol","decimals","type","nativeWrapper","usdExchangeRate"]}}},"paths":{"/v1/orderbook/currencies/{chainId}/{contractAddress}/{orderbook}":{"get":{"description":"Get all supported currency tokens for orderbook operations on a specific chain.","operationId":"OrderbookApiController_getSupportedCurrencies","parameters":[{"name":"chainId","required":true,"in":"path","description":"The chain ID in CAIP-2 format","schema":{"type":"string"}},{"name":"contractAddress","required":true,"in":"path","description":"The contract address of the token.","schema":{"type":"string"}},{"name":"orderbook","required":true,"in":"path","description":"The name of the orderbook.","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrenciesResponse"}}}},"400":{"description":"Bad Request. Invalid chain ID format."},"401":{"description":"Unauthorized. API Key is missing or invalid."},"403":{"description":"Forbidden. API Key is missing 'ORDERBOOK' permission."}},"summary":"Get supported currencies","tags":["Orderbook API"]}}}}
```

## Create Bulk Listings

> Create multiple fixed priced listings on a supported orderbook (Doma and OpenSea).

```json
{"openapi":"3.0.0","info":{"title":"Doma Registrar API","version":"1.0"},"servers":["https://api.doma.xyz","https://api-testnet.doma.xyz"],"security":[{"Api-Key":[]}],"components":{"securitySchemes":{"Api-Key":{"type":"apiKey","in":"header","name":"Api-Key"}},"schemas":{"CreateBulkOrderRequestBody":{"type":"object","properties":{"orderbook":{"type":"string","description":"Orderbook identifier. Only DOMA is supported.","enum":["DOMA","OPENSEA"]},"chainId":{"type":"string","description":"Chain ID in CAIP-2 format.","pattern":"^[a-z0-9]+:[a-zA-Z0-9]+$"},"orders":{"description":"Array of orders to create. Maximum 50 orders allowed.","maxItems":50,"type":"array","items":{"$ref":"#/components/schemas/BulkListingOrder"}},"cancelExisting":{"type":"boolean","description":"Cancel existing orders if they exist."},"cancelSignatures":{"type":"object","description":"Map of order IDs to cancellation signatures. Required for OpenSea orderbook when canceling existing orders.","additionalProperties":{"type":"string"}}},"required":["orderbook","chainId","orders","cancelExisting"]},"BulkListingOrder":{"type":"object","properties":{"parameters":{"description":"Order parameters.","allOf":[{"$ref":"#/components/schemas/OrderComponents"}]},"signature":{"type":"string","description":"Order signature."}},"required":["parameters","signature"]},"OrderComponents":{"type":"object","properties":{"offerer":{"type":"string","description":"Address of the offerer."},"zone":{"type":"string","description":"Zone address."},"orderType":{"type":"number","description":"Type of order.","enum":[0,1,2,3]},"startTime":{"type":"string","description":"Start time of the order (Unix timestamp as string)."},"endTime":{"type":"string","description":"End time of the order (Unix timestamp as string)."},"zoneHash":{"type":"string","description":"Zone hash."},"salt":{"type":"string","description":"Salt for the order."},"offer":{"description":"Array of offer items.","type":"array","items":{"$ref":"#/components/schemas/OfferItem"}},"consideration":{"description":"Array of consideration items. Considerations must include following fee items: Doma Marketplace fee, Name Token Royalties, OpenSea Fees (only for OpenSea Orderbook).","type":"array","items":{"$ref":"#/components/schemas/ConsiderationItem"}},"totalOriginalConsiderationItems":{"type":"number","description":"Total number of original consideration items."},"conduitKey":{"type":"string","description":"Conduit key."},"counter":{"type":"string","description":"Counter for the order (as string)."}},"required":["offerer","zone","orderType","startTime","endTime","zoneHash","salt","offer","consideration","totalOriginalConsiderationItems","conduitKey","counter"]},"OfferItem":{"type":"object","properties":{"itemType":{"type":"number","description":"The type of item being offered.","enum":[0,1,2,3,4,5]},"token":{"type":"string","description":"Token address of the item being offered."},"identifierOrCriteria":{"type":"string","description":"Identifier or criteria for the item."},"startAmount":{"type":"string","description":"Starting amount for the item."},"endAmount":{"type":"string","description":"Ending amount for the item."}},"required":["itemType","token","identifierOrCriteria","startAmount","endAmount"]},"ConsiderationItem":{"type":"object","properties":{"itemType":{"type":"number","description":"The type of item being offered.","enum":[0,1,2,3,4,5]},"token":{"type":"string","description":"Token address of the item being offered."},"identifierOrCriteria":{"type":"string","description":"Identifier or criteria for the item."},"startAmount":{"type":"string","description":"Starting amount for the item."},"endAmount":{"type":"string","description":"Ending amount for the item."},"recipient":{"type":"string","description":"Recipient address to receive the consideration item."}},"required":["itemType","token","identifierOrCriteria","startAmount","endAmount","recipient"]},"CreateBulkOrderResponse":{"type":"object","properties":{"orders":{"description":"Array of created listing responses.","type":"array","items":{"$ref":"#/components/schemas/CreateOrderResponse"}},"errors":{"description":"Array of error messages.","type":"array","items":{"$ref":"#/components/schemas/BulkOrderError"}}},"required":["orders","errors"]},"CreateOrderResponse":{"type":"object","properties":{"orderId":{"type":"string","description":"The unique identifier for the created order."},"orderData":{"description":"Order data.","allOf":[{"$ref":"#/components/schemas/OrderComponents"}]},"signature":{"type":"string","description":"Order signature."}},"required":["orderId","orderData","signature"]},"BulkOrderError":{"type":"object","properties":{"tokenId":{"type":"string","description":"Token ID of name that failed"},"contract":{"type":"string","description":"Contract address of name that failed"},"error":{"type":"string","description":"Error message"}},"required":["tokenId","contract","error"]}}},"paths":{"/v1/orderbook/list/bulk":{"post":{"description":"Create multiple fixed priced listings on a supported orderbook (Doma and OpenSea).","operationId":"OrderbookApiController_createBulkListing","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBulkOrderRequestBody"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBulkOrderResponse"}}}},"400":{"description":"Bad Request. Invalid query parameters."},"401":{"description":"Unauthorized. API Key is missing or invalid."},"403":{"description":"Forbidden. API Key is missing 'ORDERBOOK' permission."}},"summary":"Create Bulk Listings","tags":["Orderbook API"]}}}}
```

## Create Bulk Offers

> Create multiple fixed priced offers on a supported orderbook (Doma and OpenSea).

```json
{"openapi":"3.0.0","info":{"title":"Doma Registrar API","version":"1.0"},"servers":["https://api.doma.xyz","https://api-testnet.doma.xyz"],"security":[{"Api-Key":[]}],"components":{"securitySchemes":{"Api-Key":{"type":"apiKey","in":"header","name":"Api-Key"}},"schemas":{"CreateBulkOrderRequestBody":{"type":"object","properties":{"orderbook":{"type":"string","description":"Orderbook identifier. Only DOMA is supported.","enum":["DOMA","OPENSEA"]},"chainId":{"type":"string","description":"Chain ID in CAIP-2 format.","pattern":"^[a-z0-9]+:[a-zA-Z0-9]+$"},"orders":{"description":"Array of orders to create. Maximum 50 orders allowed.","maxItems":50,"type":"array","items":{"$ref":"#/components/schemas/BulkListingOrder"}},"cancelExisting":{"type":"boolean","description":"Cancel existing orders if they exist."},"cancelSignatures":{"type":"object","description":"Map of order IDs to cancellation signatures. Required for OpenSea orderbook when canceling existing orders.","additionalProperties":{"type":"string"}}},"required":["orderbook","chainId","orders","cancelExisting"]},"BulkListingOrder":{"type":"object","properties":{"parameters":{"description":"Order parameters.","allOf":[{"$ref":"#/components/schemas/OrderComponents"}]},"signature":{"type":"string","description":"Order signature."}},"required":["parameters","signature"]},"OrderComponents":{"type":"object","properties":{"offerer":{"type":"string","description":"Address of the offerer."},"zone":{"type":"string","description":"Zone address."},"orderType":{"type":"number","description":"Type of order.","enum":[0,1,2,3]},"startTime":{"type":"string","description":"Start time of the order (Unix timestamp as string)."},"endTime":{"type":"string","description":"End time of the order (Unix timestamp as string)."},"zoneHash":{"type":"string","description":"Zone hash."},"salt":{"type":"string","description":"Salt for the order."},"offer":{"description":"Array of offer items.","type":"array","items":{"$ref":"#/components/schemas/OfferItem"}},"consideration":{"description":"Array of consideration items. Considerations must include following fee items: Doma Marketplace fee, Name Token Royalties, OpenSea Fees (only for OpenSea Orderbook).","type":"array","items":{"$ref":"#/components/schemas/ConsiderationItem"}},"totalOriginalConsiderationItems":{"type":"number","description":"Total number of original consideration items."},"conduitKey":{"type":"string","description":"Conduit key."},"counter":{"type":"string","description":"Counter for the order (as string)."}},"required":["offerer","zone","orderType","startTime","endTime","zoneHash","salt","offer","consideration","totalOriginalConsiderationItems","conduitKey","counter"]},"OfferItem":{"type":"object","properties":{"itemType":{"type":"number","description":"The type of item being offered.","enum":[0,1,2,3,4,5]},"token":{"type":"string","description":"Token address of the item being offered."},"identifierOrCriteria":{"type":"string","description":"Identifier or criteria for the item."},"startAmount":{"type":"string","description":"Starting amount for the item."},"endAmount":{"type":"string","description":"Ending amount for the item."}},"required":["itemType","token","identifierOrCriteria","startAmount","endAmount"]},"ConsiderationItem":{"type":"object","properties":{"itemType":{"type":"number","description":"The type of item being offered.","enum":[0,1,2,3,4,5]},"token":{"type":"string","description":"Token address of the item being offered."},"identifierOrCriteria":{"type":"string","description":"Identifier or criteria for the item."},"startAmount":{"type":"string","description":"Starting amount for the item."},"endAmount":{"type":"string","description":"Ending amount for the item."},"recipient":{"type":"string","description":"Recipient address to receive the consideration item."}},"required":["itemType","token","identifierOrCriteria","startAmount","endAmount","recipient"]},"CreateBulkOrderResponse":{"type":"object","properties":{"orders":{"description":"Array of created listing responses.","type":"array","items":{"$ref":"#/components/schemas/CreateOrderResponse"}},"errors":{"description":"Array of error messages.","type":"array","items":{"$ref":"#/components/schemas/BulkOrderError"}}},"required":["orders","errors"]},"CreateOrderResponse":{"type":"object","properties":{"orderId":{"type":"string","description":"The unique identifier for the created order."},"orderData":{"description":"Order data.","allOf":[{"$ref":"#/components/schemas/OrderComponents"}]},"signature":{"type":"string","description":"Order signature."}},"required":["orderId","orderData","signature"]},"BulkOrderError":{"type":"object","properties":{"tokenId":{"type":"string","description":"Token ID of name that failed"},"contract":{"type":"string","description":"Contract address of name that failed"},"error":{"type":"string","description":"Error message"}},"required":["tokenId","contract","error"]}}},"paths":{"/v1/orderbook/offer/bulk":{"post":{"description":"Create multiple fixed priced offers on a supported orderbook (Doma and OpenSea).","operationId":"OrderbookApiController_createBulkOffer","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBulkOrderRequestBody"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBulkOrderResponse"}}}},"400":{"description":"Bad Request. Invalid query parameters."},"401":{"description":"Unauthorized. API Key is missing or invalid."},"403":{"description":"Forbidden. API Key is missing 'ORDERBOOK' permission."}},"summary":"Create Bulk Offers","tags":["Orderbook API"]}}}}
```

## Get bulk listing items

> Get paginated items for a bulk listing by bulk listing ID.

```json
{"openapi":"3.0.0","info":{"title":"Doma Registrar API","version":"1.0"},"servers":["https://api.doma.xyz","https://api-testnet.doma.xyz"],"security":[{"Api-Key":[]}],"components":{"securitySchemes":{"Api-Key":{"type":"apiKey","in":"header","name":"Api-Key"}},"schemas":{"PaginatedBulkListingResponse":{"type":"object","properties":{"items":{"description":"Response items","type":"array","items":{"type":"string"}},"totalCount":{"type":"number","description":"Total number of items"},"pageSize":{"type":"number","description":"Number of items per page"},"currentPage":{"type":"number","description":"Current page"},"totalPages":{"type":"number","description":"Total of pages"},"hasPreviousPage":{"type":"boolean","description":"Has previous page"},"hasNextPage":{"type":"boolean","description":"Has next page"}},"required":["items","totalCount","pageSize","currentPage","totalPages","hasPreviousPage","hasNextPage"]}}},"paths":{"/v1/orderbook/list/bulk/{id}/items":{"get":{"description":"Get paginated items for a bulk listing by bulk listing ID.","operationId":"OrderbookApiController_getBulkListingItems","parameters":[{"name":"id","required":true,"in":"path","description":"Bulk listing ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Bulk listing items retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedBulkListingResponse"}}}},"400":{"description":"Bad Request. Bulk listing not found or invalid parameters."},"403":{"description":"Forbidden. API Key is missing 'ORDERBOOK' permission."}},"summary":"Get bulk listing items","tags":["Orderbook API"]}}}}
```


# Doma Network Information

## Mainnet

**Chain ID:** 97477

**Currency:** ETH

**Bridge:** [https://bridge.doma.xyz](https://bridge.doma.xyz/)

**RPC Server Address:** [https://rpc.doma.xyz](https://rpc.doma.xyz/)

**Blockchain Explorer:** [https://explorer.doma.xyz](https://explorer.doma.xyz/)

**Doma API endpoint:** [https://api.doma.xyz](https://api.doma.xyz/)

**Subgraph API endpoint:** <https://api.doma.xyz/graphql>

**Chainlist:** <https://chainlist.org/chain/97477>

## Testnet

**Chain ID:** 97476

**Currency:** ETH

**Bridge:** <https://bridge-testnet.doma.xyz>

**RPC Server Address:** [https://rpc-testnet.doma.xyz](https://rpc-testnet.doma.xyz/)

**Blockchain Explorer:** <https://explorer-testnet.doma.xyz>

**Doma API endpoint:** [https://api-testnet.doma.xyz](https://api-testnet.doma.xyz/)

**Subgraph API endpoint:** <https://api-testnet.doma.xyz/graphql>

**Chainlist:** <https://chainlist.org/chain/97476>

## Ecosystem Partners

[dRPC NodeCloud](https://drpc.org/chainlist/doma): With NodeCloud’s AI-powered load balancer and globally distributed node network, your RPC stays fast, reliable, and resilient to outages. Designed for devs who want to build, not babysit infrastructure. Supports Doma, and 100+ other chains.


# Deployed Smart Contracts

## Doma Mainnet Contracts

### Doma

* Doma Record: [0xd000000000003eC7096c7B280b274F20b305b82a](https://explorer.doma.xyz/address/0xd000000000003eC7096c7B280b274F20b305b82a)
* Cross Chain Gateway: [0xD000000000007f18154b96c65eBdF26963d4FbB4](https://explorer.doma.xyz/address/0xD000000000007f18154b96c65eBdF26963d4FbB4)
* Forwarder: [0xd000000000bc34dBa2A100ab94cfdDc1e49266B9](https://explorer.doma.xyz/address/0xd000000000bc34dBa2A100ab94cfdDc1e49266B9)
* Ownership Token: [0xd000000000009E6bEa0bA0c5D964AE98d59ED318](https://explorer.doma.xyz/address/0xd000000000009E6bEa0bA0c5D964AE98d59ED318)
* Proxy Doma Record: [0xd0000000000067CB44aE7b6aC3AB5764dE20A3E2](https://explorer.doma.xyz/address/0xd0000000000067CB44aE7b6aC3AB5764dE20A3E2)

### Base

* Ownership Token: [0xd000000000009E6bEa0bA0c5D964AE98d59ED318](https://explorer.base.org/address/0xd000000000009E6bEa0bA0c5D964AE98d59ED318)
* Proxy Doma Record: [0xd0000000000067CB44aE7b6aC3AB5764dE20A3E2](https://explorer.base.org/address/0xd0000000000067CB44aE7b6aC3AB5764dE20A3E2)
* Cross Chain Gateway: [0xD000000000007f18154b96c65eBdF26963d4FbB4](https://explorer.base.org/address/0xD000000000007f18154b96c65eBdF26963d4FbB4)

### Avalanche C-Chain

* Ownership Token: [0xd000000000009E6bEa0bA0c5D964AE98d59ED318](https://snowtrace.io/address/0xd000000000009E6bEa0bA0c5D964AE98d59ED318)
* Proxy Doma Record: [0xd0000000000067CB44aE7b6aC3AB5764dE20A3E2](https://snowtrace.io/address/0xd0000000000067CB44aE7b6aC3AB5764dE20A3E2)
* Cross Chain Gateway: [0xD000000000007f18154b96c65eBdF26963d4FbB4](https://snowtrace.io/address/0xD000000000007f18154b96c65eBdF26963d4FbB4)

### Shibarium

* Ownership Token: [0xDe74799371Ceac11A0F52BA2694392A391D0dA18](https://shibariumscan.io/address/0xDe74799371Ceac11A0F52BA2694392A391D0dA18)
* Proxy Doma Record: [0xd0000000000067CB44aE7b6aC3AB5764dE20A3E2](https://shibariumscan.io/address/0xd0000000000067CB44aE7b6aC3AB5764dE20A3E2)
* Cross Chain Gateway: [0xD000000000007f18154b96c65eBdF26963d4FbB4](https://shibariumscan.io/address/0xD000000000007f18154b96c65eBdF26963d4FbB4)

### Core:

* Ownership Token: [0x2fa82373Ff812613FCcf2bBBe6DEC8267EcBa2dc](https://scan.coredao.org/address/0x2fa82373Ff812613FCcf2bBBe6DEC8267EcBa2dc)
* Proxy Doma Record: [0xd0000000000067CB44aE7b6aC3AB5764dE20A3E2](https://scan.coredao.org/address/0xd0000000000067CB44aE7b6aC3AB5764dE20A3E2)
* Cross Chain Gateway: [0xD000000000007f18154b96c65eBdF26963d4FbB4](https://scan.coredao.org/address/0xD000000000007f18154b96c65eBdF26963d4FbB4)

### ApeChain

* Ownership Token: [0x0D435A6c16045Abeaf6A442Bf162fd52597B4Ed3](https://apescan.io/address/0x0D435A6c16045Abeaf6A442Bf162fd52597B4Ed3)
* Proxy Doma Record: [0xd0000000000067CB44aE7b6aC3AB5764dE20A3E2](https://apescan.io/address/0xd0000000000067CB44aE7b6aC3AB5764dE20A3E2)
* Cross Chain Gateway: [0xD000000000007f18154b96c65eBdF26963d4FbB4](https://apescan.io/address/0xD000000000007f18154b96c65eBdF26963d4FbB4)

### Viction

* Ownership Token: [0x619F26d2c0E9C0102aD7924A63c5834776167292](https://www.vicscan.xyz/address/0x619F26d2c0E9C0102aD7924A63c5834776167292)
* Proxy Doma Record: [0xd0000000000067CB44aE7b6aC3AB5764dE20A3E2](https://www.vicscan.xyz/address/0xd0000000000067CB44aE7b6aC3AB5764dE20A3E2)
* Cross Chain Gateway: [0xD000000000007f18154b96c65eBdF26963d4FbB4](https://www.vicscan.xyz/address/0xD000000000007f18154b96c65eBdF26963d4FbB4)

## Testnet Contracts

### Doma Testnet

* Doma Record: [0x6754B3F973A83d9B7A9Bfd3B5fC139ED7B9b1aAf](https://explorer-testnet.doma.xyz/address/0x6754B3F973A83d9B7A9Bfd3B5fC139ED7B9b1aAf)
* Cross Chain Gateway: [0xB27fFA8259594C8b16BF9BA6531F7Bf9d27c4D2F](https://explorer-testnet.doma.xyz/address/0xB27fFA8259594C8b16BF9BA6531F7Bf9d27c4D2F)
* Forwarder: [0x85D2D8cFe5a4bC65927616c3167D89F7A4b5a143](https://explorer-testnet.doma.xyz/address/0x85D2D8cFe5a4bC65927616c3167D89F7A4b5a143)
* Ownership Token: [0x9a88adE7B61998dCF2A64A1D43B3377Ad1a62318](https://explorer-testnet.doma.xyz/address/0x9a88adE7B61998dCF2A64A1D43B3377Ad1a62318)
* Proxy Doma Record: [0xC21C932BE327A6Ae32071f40e097612fd8D5b445](https://explorer-testnet.doma.xyz/address/0xC21C932BE327A6Ae32071f40e097612fd8D5b445)

### Sepolia Testnet

* Ownership Token: [0x9A374915648f1352827fFbf0A7bB5752b6995eB7](https://sepolia.etherscan.io/address/0x9A374915648f1352827fFbf0A7bB5752b6995eB7)
* Proxy Doma Record: [0xD9A0E86AACf2B01013728fcCa9F00093B9b4F3Ff](https://sepolia.etherscan.io/address/0xD9A0E86AACf2B01013728fcCa9F00093B9b4F3Ff)
* Cross Chain Gateway: [0xEC67EfB227218CCc3c7032a6507339E7B4D623Ad](https://sepolia.etherscan.io/address/0xEC67EfB227218CCc3c7032a6507339E7B4D623Ad)

### Base Sepolia Testnet

* Ownership Token: [0x2f45DfC5f4c9473fa72aBdFbd223d0979B265046](https://sepolia.basescan.org/address/0x2f45DfC5f4c9473fa72aBdFbd223d0979B265046)
* Proxy Doma Record: [0xa40aA710F0C77DF3De6CEe7493d1FfF3715D59Da](https://sepolia.basescan.org/address/0xa40aA710F0C77DF3De6CEe7493d1FfF3715D59Da)
* Cross Chain Gateway: [0xC721925DF8268B1d4a1673D481eB446B3EDaAAdE](https://sepolia.basescan.org/address/0xC721925DF8268B1d4a1673D481eB446B3EDaAAdE)

### Avalanche Fuji Testnet

* Ownership Token: [0x4a6702E57081F6677D4b75D902223ffBF026efea](https://testnet.snowtrace.io/address/0x4a6702E57081F6677D4b75D902223ffBF026efea)
* Proxy Doma Record: [0x005815F7de38F192260b26005444cD62126D3D8A](https://testnet.snowtrace.io/address/0x005815F7de38F192260b26005444cD62126D3D8A)
* Gateway: [0x1443bC2bBAB07437BCF9C577b647523A736bB33E](https://testnet.snowtrace.io/address/0x1443bC2bBAB07437BCF9C577b647523A736bB33E)

### Shibarium (Puppynet) Testnet

* Ownership Token: [0x55460792B2e3eDEbdF28f6C8766B7778Db7092A9](https://puppyscan.shib.io/address/0x55460792B2e3eDEbdF28f6C8766B7778Db7092A9)
* Proxy Doma Record: [0x8420729Dc9eBb5a30dBa8CEe1392F56bfc03b1F5](https://puppyscan.shib.io/address/0x8420729Dc9eBb5a30dBa8CEe1392F56bfc03b1F5)
* Cross Chain Gateway: [0x79e70acd155bFA071E57cA6a2f507d87d0e7B7f9](https://puppyscan.shib.io/address/0x79e70acd155bFA071E57cA6a2f507d87d0e7B7f9)

### Apechain Testnet

* Ownership Token: [0x63b7749B3b79B974904E0c684Ee589191fd807b4](https://curtis.explorer.caldera.xyz/address/0x63b7749B3b79B974904E0c684Ee589191fd807b4)
* Proxy Doma Record: [0x797293E811f9C5eFa1973004B581E46d1787F929](https://curtis.explorer.caldera.xyz/address/0x797293E811f9C5eFa1973004B581E46d1787F929)
* Cross Chain Gateway: [0xa483D7d32D7f5f2bd430CA9e61db275Eda72Fd23](https://curtis.explorer.caldera.xyz/address/0xa483D7d32D7f5f2bd430CA9e61db275Eda72Fd23)

### Solana Devnet

*Coming Soon*

## Uniswap Mainnet Contracts

* UniswapV3Factory: [0x2e50b586d5bcD04cb6125E028A6a669f7f3cF1C2](https://explorer.doma.xyz/address/0x2e50b586d5bcD04cb6125E028A6a669f7f3cF1C2)
* Multicall: [0x722Cc8B61DBC684379a6D449D91E9d4047C63432](https://explorer.doma.xyz/address/0x722Cc8B61DBC684379a6D449D91E9d4047C63432)
* ProxyAdmin: [0x0F76f61C0806e19FaA420bEF33dEa19f68536BbE](https://explorer.doma.xyz/address/0x0F76f61C0806e19FaA420bEF33dEa19f68536BbE)
* TickLens: [0x0fDad9964465900C0e835D930B44609F6cE716DA](https://explorer.doma.xyz/address/0x0fDad9964465900C0e835D930B44609F6cE716DA)
* NFTDescriptor: [0x4cd3D91cd0c78126ffdAbd996741Bd43bA4e840F](https://explorer.doma.xyz/address/0x4cd3D91cd0c78126ffdAbd996741Bd43bA4e840F)
* TransparentUpgradeableProxy: [0x2805B68fB626546B91e44dF734AfC1E33D4b85a9](https://explorer.doma.xyz/address/0x2805B68fB626546B91e44dF734AfC1E33D4b85a9)
* NonfungiblePositionManager: [0xce126ca6aceBBDCe95D7b8A3Ce637951640811E0](https://explorer.doma.xyz/address/0xce126ca6aceBBDCe95D7b8A3Ce637951640811E0)
* V3Migrator: [0xE73103867BBE5ed589eE9d1056a363265974f225](https://explorer.doma.xyz/address/0xE73103867BBE5ed589eE9d1056a363265974f225)
* UniswapV3Staker: [0x6Ebb1F93f6Ef4589946E69EF023d4944BBb98FA9](https://explorer.doma.xyz/address/0x6Ebb1F93f6Ef4589946E69EF023d4944BBb98FA9)
* QuoterV2: [0x2023ADF9fF50219C34989baABD76A0f5cfe432f4](https://explorer.doma.xyz/address/0x2023ADF9fF50219C34989baABD76A0f5cfe432f4)
* SwapRouter02: [0x50cDfe221F0B478b7F58319d7b42cf61e5d904A9](https://explorer.doma.xyz/address/0x50cDfe221F0B478b7F58319d7b42cf61e5d904A9)
* UniversalRouter: [0x5089863E97196773038f98459262D866f2281f58](https://explorer.doma.xyz/address/0x5089863E97196773038f98459262D866f2281f58)
* Permit2: [0x000000000022D473030F116dDEE9F6B43aC78BA3](https://explorer.doma.xyz/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)

## Uniswap Testnet Contracts

* UniswapV3Factory: [0xF1398cA2C4F1113C5B618D71E4751D2E744f8369](https://explorer-testnet.doma.xyz/address/0xF1398cA2C4F1113C5B618D71E4751D2E744f8369)
* Multicall: [0x6ecF6BD5D49Dc9574968BC9f65768420d3220699](https://explorer-testnet.doma.xyz/address/0x6ecF6BD5D49Dc9574968BC9f65768420d3220699)
* ProxyAdmin: [0xFa534D966F2b6EAF0d9BAdb3A8d99f974614b166](https://explorer-testnet.doma.xyz/address/0xFa534D966F2b6EAF0d9BAdb3A8d99f974614b166)
* TickLens: [0x436f08a0c239e9bC5db55c90CF73efC6D2E8C325](https://explorer-testnet.doma.xyz/address/0x436f08a0c239e9bC5db55c90CF73efC6D2E8C325)
* NFTDescriptor: [0xd88A2a8E079B01e2b1B9D77dF03877BAC90873Bf](https://explorer-testnet.doma.xyz/address/0xd88A2a8E079B01e2b1B9D77dF03877BAC90873Bf)
* NonfungiblePositionManager: [0xDE54bc2C9A9726a67c4eEec6b96DC28cF6F689e1](https://explorer-testnet.doma.xyz/address/0xDE54bc2C9A9726a67c4eEec6b96DC28cF6F689e1)
* TransparentUpgradeableProxy: [0x09DE7861C18dfaB91B5c261F3EE4CdE0159e65D7](https://explorer-testnet.doma.xyz/address/0x09DE7861C18dfaB91B5c261F3EE4CdE0159e65D7)
* NonfungiblePositionManager: [0x3D34Ae8e53dc993C0F7Ee1AAa8020d3F0279147b](https://explorer-testnet.doma.xyz/address/0x3D34Ae8e53dc993C0F7Ee1AAa8020d3F0279147b)
* V3Migrator: [0x4407B6e7eB9c877cB1192a681a91feF340F8817d](https://explorer-testnet.doma.xyz/address/0x4407B6e7eB9c877cB1192a681a91feF340F8817d)
* UniswapV3Staker: [0x465351f1d1db6bdF74aCE1a40ED9bd983ea79C67](https://explorer-testnet.doma.xyz/address/0x465351f1d1db6bdF74aCE1a40ED9bd983ea79C67)
* QuoterV2: [0x823022BB81e50aBcD1f6cf9F8eE6e2557A345a9d](https://explorer-testnet.doma.xyz/address/0x823022BB81e50aBcD1f6cf9F8eE6e2557A345a9d)
* SwapRouter02: [0x7cE273df74dc43c79E5880Dd2c3eB44309236743](https://explorer-testnet.doma.xyz/address/0x7cE273df74dc43c79E5880Dd2c3eB44309236743)
* UniversalRouter: [0x7BD025f880C4D00AD009C70792Cbd0418556D667](https://explorer-testnet.doma.xyz/address/0x7BD025f880C4D00AD009C70792Cbd0418556D667)
* Permit2: [0x000000000022D473030F116dDEE9F6B43aC78BA3](https://explorer-testnet.doma.xyz/address/0x000000000022D473030F116dDEE9F6B43aC78BA3)


# Supported TLDs

## Supported gTLDs

.academy\
.accountant\
.accountants\
.actor\
.adult\
.africa\
.agency\
.airforce\
.apartments\
.app\
.army\
.art\
.associates\
.attorney\
.auction\
.audio\
.author\
.auto\
.autos\
.baby\
.band\
.bar\
.bargains\
.beauty\
.beer\
.best\
.bet\
.bible\
.bid\
.bike\
.bingo\
.bio\
.biz\
.black\
.blackfriday\
.blog\
.blue\
.bond\
.boo\
.book\
.boston\
.bot\
.boutique\
.box\
.broker\
.build\
.builders\
.business\
.buy\
.buzz\
.cab\
.cafe\
.call\
.cam\
.camera\
.camp\
.cancerresearch\
.capital\
.car\
.cards\
.care\
.career\
.careers\
.cars\
.casa\
.cash\
.casino\
.catering\
.catholic\
.center\
.ceo\
.cfd\
.channel\
.chat\
.cheap\
.christmas\
.church\
.circle\
.city\
.claims\
.cleaning\
.click\
.clinic\
.clothing\
.cloud\
.club\
.coach\
.codes\
.coffee\
.college\
.com\
.community\
.company\
.computer\
.condos\
.construction\
.consulting\
.contact\
.contractors\
.cooking\
.cool\
.country\
.coupon\
.coupons\
.courses\
.credit\
.creditcard\
.cricket\
.cruise\
.cruises\
.cyou\
.dad\
.dance\
.data\
.date\
.dating\
.day\
.deal\
.deals\
.degree\
.delivery\
.democrat\
.dental\
.dentist\
.design\
.dev\
.diamonds\
.diet\
.digital\
.direct\
.directory\
.discount\
.diy\
.docs\
.doctor\
.dog\
.domains\
.dot\
.download\
.earth\
.eat\
.education\
.email\
.energy\
.engineer\
.engineering\
.edeka\
.enterprises\
.equipment\
.estate\
.events\
.exchange\
.expert\
.exposed\
.express\
.fail\
.faith\
.family\
.fan\
.fans\
.farm\
.fashion\
.feedback\
.film\
.final\
.finance\
.financial\
.fish\
.fishing\
.fit\
.fitness\
.flights\
.florist\
.flowers\
.food\
.football\
.forsale\
.forum\
.foundation\
.free\
.fun\
.fund\
.furniture\
.fyi\
.gallery\
.game\
.games\
.garden\
.gay\
.gdn\
.gift\
.gifts\
.gives\
.glass\
.global\
.gmbh\
.gold\
.golf\
.gop\
.graphics\
.gripe\
.group\
.guide\
.guitars\
.guru\
.hair\
.haus\
.health\
.healthcare\
.help\
.here\
.hiphop\
.hiv\
.hockey\
.holdings\
.holiday\
.homes\
.horse\
.hospital\
.host\
.hosting\
.hot\
.house\
.how\
.icu\
.industries\
.info\
.ing\
.ink\
.institute\
.insurance\
.insure\
.international\
.investments\
.jewelry\
.joy\
.kim\
.kitchen\
.land\
.lat\
.lawyer\
.lease\
.legal\
.lgbt\
.life\
.lifeinsurance\
.lighting\
.like\
.limited\
.limo\
.link\
.live\
.living\
.llc\
.loan\
.loans\
.lol\
.lotto\
.love\
.ltd\
.makeup\
.management\
.map\
.market\
.marketing\
.markets\
.mba\
.med\
.media\
.meet\
.meme\
.memorial\
.men\
.menu\
.mobile\
.moda\
.moe\
.mom\
.money\
.monster\
.mortgage\
.motorcycles\
.mov\
.movie\
.navy\
.net\
.network\
.news\
.nexus\
.ninja\
.now\
.observer\
.one\
.onl\
.online\
.ooo\
.open\
.org\
.page\
.partners\
.parts\
.party\
.pay\
.pet\
.phone\
.photo\
.photography\
.photos\
.pics\
.pictures\
.pid\
.pin\
.pink\
.pizza\
.place\
.plumbing\
.plus\
.poker\
.press\
.pro\
.productions\
.prof\
.promo\
.properties\
.property\
.protection\
.pub\
.qpon\
.quebec\
.racing\
.read\
.realestate\
.realty\
.recipes\
.red\
.rehab\
.rent\
.rentals\
.repair\
.report\
.republican\
.rest\
.restaurant\
.review\
.reviews\
.rich\
.rip\
.rocks\
.rodeo\
.room\
.rugby\
.run\
.safe\
.sale\
.save\
.sbi\
.sbs\
.scholarships\
.school\
.science\
.search\
.secure\
.security\
.select\
.services\
.sexy\
.shoes\
.shop\
.shopping\
.show\
.singles\
.site\
.ski\
.skin\
.sky\
.soccer\
.social\
.software\
.solar\
.solutions\
.song\
.space\
.spreadbetting\
.spot\
.srl\
.store\
.studio\
.study\
.style\
.sucks\
.supplies\
.supply\
.support\
.surf\
.surgery\
.systems\
.talk\
.tattoo\
.tax\
.taxi\
.team\
.tech\
.technology\
.tennis\
.theater\
.theatre\
.tickets\
.tips\
.tires\
.today\
.tools\
.top\
.tours\
.town\
.toys\
.trade\
.trading\
.training\
.trust\
.tube\
.tunes\
.uconnect\
.university\
.uno\
.vacations\
.ventures\
.vet\
.video\
.villas\
.vip\
.vision\
.vodka\
.voting\
.voyage\
.wang\
.watch\
.watches\
.webcam\
.website\
.wedding\
.win\
.wine\
.work\
.works\
.world\
.wow\
.wtf\
.xyz\
.yachts\
.yoga\
.you

## Supported ccTLDs

.ac\
.ad\
.ag\
.ai\
.al\
.am\
.ar\
.as\
.az\
.bz\
.ca\
.cc\
.cd\
.co\
.cu\
.cv\
.de\
.dj\
.fm\
.ga\
.gg\
.io\
.il\
.in\
.is\
.it\
.kg\
.ky\
.la\
.ly\
.ma\
.md\
.me\
.mn\
.ms\
.mt\
.ne\
.nu\
.pa\
.pe\
.pn\
.pr\
.pw\
.re\
.rs\
.sc\
.sd\
.sh\
.sx\
.tf\
.tk\
.tm\
.tn\
.to\
.tv\
.ws\
.yt


# Doma Fractionalization

Doma fractionalization enables the conversion of domain NFTs into fungible tokens. This functionality allows domain investors to access liquidity while retaining ownership of their domains. It also enables crypto investors to gain partial ownership of valuable domains by holding the fractionalized fungible tokens.

## Overview

Below is an overview of the components that communicate with the Doma Fractionalization smart contract.

<img src="/files/s1wpWk8GS3mRGvsiP6fi" alt="" class="gitbook-drawing">

* **Doma Fractionalization:** The core contract for domain fractionalization deployed on Doma Chain. By interacting with the smart contract, users can fractionalize and buy out domain ownership tokens, mint fractional tokens which are fungible, and exchange fractional tokens after a buyout. Additionally, the contract interacts with decentralized exchanges (DEXs) to fetch the prices of fractional tokens.
* **Fractional Token (**[**ERC-20**](https://eips.ethereum.org/EIPS/eip-20)**):** A fungible token contract for each fractionalized domain ownership token (NFT). These tokens can only be minted and burned by the Doma Fractionalization contract to prevent dilution. They can be bridged to other chains like Base and Solana. When minting tokens during the initial fractionalization, or when redeeming fractional tokens after a buyout, the user must bridge the domain ownership token or fractional token back to Doma Chain.
* **Doma Launchpad:** The launchpad contract provides a bonding curve for initial launch. A linear curve model with fixed start and end price is used. The launch is considered successful when all tokens are bought out. Once launched, tokens are migrated into a Uniswap V3 liquidity pool.
* **Doma Vesting:** Tokens allocated for a domain owner are put into vesting, to ensure full tokens supply is unlocked on a predictable schedule.
* **USDC.e:** The stablecoin used to buy out the original ownership token or to redeem income by exchanging fractional tokens after a buyout. USDC bridged using Layer Zero is used.
* **DEX(s):** The DEX(s) provide a liquidity platform for users to swap to and from fractional tokens. They also serve as the price source for all fractional tokens used by the Doma Fractionalization contract. Uniswap V3 deployed on Doma Chain is used.

For more information on the Doma Record and Domain Ownership Token, please refer to [this page](https://docs.doma.xyz/api-reference/doma-smart-contracts-api).

## Main Use Cases

### Fractionalize Domain NFTs

To obtain fractional tokens from a Domain Ownership Token (NFT), the user must fractionalize the NFT by interacting with the Doma Fractionalization smart contract. Upon fractionalization, the smart contract mints the corresponding fractional tokens and transfers them to a Doma-approved launchpad in a single transaction. The launchpad is responsible for raising funds and launching the fractional token on supported DEXs.

When minting the fractional tokens, the user specifies the total supply and other token metadata.

After a domain NFT is fractionalized, anyone can buy out the NFT by paying the required buy out price. Once the NFT is reassigned to a new owner, it follows the standard Doma Protocol domain ownership rules.

To protect against price volatility, the original domain NFT owner sets a minimum buy out price in USDC. A buyer must pay a price that is greater than or equal to the minimum buy out price. The exact formula for determining the buy out price is covered in the next section.

### Buy Out Domain NFTs

Doma Fractionalization allows domain NFTs to remain tradable even after they have been fractionalized. This means that any user can buy out a fractionalized domain NFT by interacting with the Doma Fractionalization smart contract. Since a fractionalized domain NFT may have increased market value due to demand for its fractional tokens, the buyout price is defined as follows:

$$
Price\_{buyout} = Max(MBP, FDV, FDV{twap})
$$

where `MBP` is short for the minimum buyout price set by the user when fractionalizing the domain NFT. `FDV` is short for the fully diluted value, which is calculated by:

$$
FDV = TotalSupply \* Price
$$

`FDVtwap` is short for the Time-Weighted Average Price (TWAP) FDV, which is similar to FDV, but uses TWAP price, instead of the current pool price. This is used to protect against short-term price volatility.

$$
FDV = TotalSupply \* Price\_{twap}
$$

Please note that the user performing the buy out may be different from the original NFT owner. In such cases, the Doma Protocol will initiate a process to update domain ownership records accordingly.

Once the domain NFT is bought out, the associated fractional tokens no longer represent ownership of the domain. However, holders of these tokens can still trade these tokens on exchanges, or redeem them for a portion of the buy out proceeds by interacting with the Doma Fractionalization smart contract (as explained in the next section).

### Exchange Fractional Tokens After Buying Out

When a domain NFT is bought out, the original fractional tokens no longer represent ownership of the domain. To protect the value of these tokens, the Doma Protocol allows holders to redeem them for USDC.e based on the buy out price of the domain:

$$
Price\_{token} = \frac {Price\_{buyout}}  {TotalSupply\_{token}}
$$

After the buy out, the new domain owner may choose to re-fractionalize the domain by issuing a new set of fractional tokens through a separate ERC-20 contract. These new tokens are distinct and do not affect the value or redeemability of the original tokens.


# For Registrars

For access to the complete Registrar documentation, please visit [docs-registrar.doma.xyz](https://docs-registrar.doma.xyz) (requires Registrar credentials).


# Agentic Commerce

Doma exposes its commerce primitives (buy, sell, register, configure) as **AI-native records** that any LLM agent can discover and execute.

There are two ways in:

* [**MCP Server**](/agentic-commerce/mcp-server) **— start here.** A hosted MCP endpoint you add to any MCP client with one URL. The agent discovers Doma's tools itself; no API key, no install, nothing to keep up to date.
* **AgentRoot + Doma CLI.** The discovery layer is [AgentRoot](https://agentroot.io), an open DNS-based protocol; the execution layer is the [Doma CLI](/agentic-commerce/doma-cli). Use this when you want Doma's own skill instructions in your agent, or need operations the MCP server doesn't expose yet (selling, bridging, subdomains). The rest of this page covers how that stack fits together.

## The three-step model

<figure><img src="/files/A6LjN9lQXnpGegoaxrnP" alt="Three-step model: install Doma skills via AgentRoot once, prompt the agent in English for every operation, the skill drives the Doma CLI and wallet automatically."><figcaption></figcaption></figure>

[AgentRoot](/agentic-commerce/agentroot) is a general-purpose discovery protocol; any domain can publish capabilities through it. This section walks through the **Doma case**: the user installs Doma's skills via AgentRoot once, then talks to the agent in English to run operations on Doma. The same three steps apply to any AgentRoot-published capability at any domain.

1. **Install Doma's skills via AgentRoot (one time).** The user asks the agent to install the skills published at `doma.xyz`: e.g. *"Use AgentRoot to install the trade-tokens skill from doma.xyz"*, or *"install all doma.xyz skills"*. The agent's AgentRoot MCP resolves `doma.xyz`, fetches the matching `SKILL.md` files, and drops them into the agent's skills folder. From this point on, the skills are part of the agent's runtime context (portable across Claude Code, Cursor, Codex CLI, Gemini, OpenCode).
2. **Prompt the agent in plain English (every operation).** With the skills installed, prompts like *"buy 10 USDC of `<token>` on Doma testnet"* or *"register `mydomain.io` via MPP"* match against the installed skills' trigger phrases. The user does not pick which skill runs; the agent matches the prompt.
3. **The skill drives the Doma CLI and the wallet.** The matched skill orchestrates everything below the prompt: it calls [Doma CLI](/agentic-commerce/doma-cli) commands, triggers the [agentic wallet](/agentic-commerce/agentic-wallet) browser sign-in the first time a signature is needed, surfaces confirmations, and reports results. **The user doesn't type any `doma <command>` lines.**

## Protocol stack

<figure><img src="/files/H1cO1qpQ5aJMRGp1qvqC" alt="Protocol stack with two phases. ONE-TIME SETUP via AgentRoot: user prompt resolves _agentroot.doma.xyz through DNS, fetches the zone file, installs SKILL.md files into the agent&#x27;s skills folder. EVERY OPERATION via the installed skill: user prompt is matched to an installed skill, the skill orchestrates Doma CLI commands, the wallet signs (agentic wallet or local private key), and on-chain settlement happens on Doma, Ethereum, or Base."><figcaption></figcaption></figure>

## Prerequisites

You configure the toolchain once. After that, you talk to your agent in plain English; the skill drives the CLI, the wallet, and the on-chain calls. You don't have to remember any `doma …` commands.

* **Node.js ≥ 20.** Required for both `agent-root` (the AgentRoot CLI) and `@doma-protocol/cli`.
* **An AI agent that can load `SKILL.md` or MCP tools.** Claude Code, Claude Desktop, Cursor, Codex CLI, Gemini CLI, or OpenCode.
* **Install the Doma CLI:** `npm install -g @doma-protocol/cli` (one time).
* **Pick a wallet path:**
  * **Agentic wallet (recommended).** Nothing to configure up front. The first time a skill needs a signature, it triggers `doma auth login` for you. You click **Authorize** in the resulting browser tab once, and Privy is your signer from then on. No key on your machine. Spend cap enforced server-side. Revocable any time.
  * **Local private key.** `export DOMA_PRIVATE_KEY=0x…` once. The skill uses that key for every signature. Faster setup, no browser dance, but the key sits on disk and the wallet has unbounded spend authority. Use a dedicated test wallet.

{% hint style="info" %}
The `doma <command>` lines that appear in the [Doma CLI](/agentic-commerce/doma-cli) reference are what the **skill** runs on your behalf. As a user you don't need to memorize or type them. They're documented so you understand what's happening under the hood and so you can use the CLI directly if you want.
{% endhint %}

## Where to go next

* **Just want Doma in your agent, fast?** Connect the [MCP Server](/agentic-commerce/mcp-server) — one URL, no install.
* **New to AgentRoot?** Start with [AgentRoot → Protocol Overview](/agentic-commerce/agentroot). The protocol's home is [agentroot.io](https://agentroot.io).
* **Want to install a Doma skill in your agent?** Jump to [Discover & Use Skills](/agentic-commerce/agentroot/discover-and-use).
* **Ready to run transactions?** Install the [Doma CLI](/agentic-commerce/doma-cli) and pick a [wallet mode](/agentic-commerce/doma-cli/wallet-modes).
* **Building your own AgentRoot zone?** See [Publish Your Own](/agentic-commerce/agentroot/publish-your-own).
* **Want a worked example?** Read the [Skills](/agentic-commerce/skills) reference pages for `secondary-sales`, `trade-tokens`, and `MPP`.


# MCP Server

The **Doma MCP server** is a hosted [Model Context Protocol](https://modelcontextprotocol.io) server that gives any MCP-capable AI agent direct access to Doma: domain lookup and search, availability and pricing, DNS, marketplace listings and quotes, token prices and wallet balances — and, once authorized, buying listed domains and swapping tokens from the user's own Doma wallet.

One URL in your MCP client and the agent discovers the tools itself — **no API key, no contract ABIs, no Seaport or orderbook code**.

## Endpoints

| Network      | URL                                | Chain ID |
| ------------ | ---------------------------------- | -------- |
| Doma Mainnet | `https://mcp.doma.xyz/mcp`         | `97477`  |
| Doma Testnet | `https://mcp-testnet.doma.xyz/mcp` | `97476`  |

Both speak MCP over **Streamable HTTP**. Each server executes on exactly one network — `server.ping.v1` reports which. To use both, connect both under distinct names (`doma`, `doma-testnet`).

{% hint style="info" %}
Pasting the bare origin (`https://mcp.doma.xyz`) also works — the server routes protocol traffic from `/` to `/mcp`. The `/mcp` path is the canonical one to configure.
{% endhint %}

## Two tiers

* **Read tools — no authentication.** Lookups, search, pricing, DNS, listings, offers, quotes, token data, balances. Connect and start asking; nothing is signed, nothing is spent.
* **Execute tools — OAuth authorization required.** Buying a listing and swapping tokens sign and broadcast real transactions from the user's Doma wallet, gated by an OAuth 2.1 flow, a **USD spend allowance the user sets**, a per-transaction ceiling, and a rate limit. See [Authorization & Spend Controls](/agentic-commerce/mcp-server/authorization).

Execute tools are still *listed* to unauthorized clients, so an agent can see what's possible; calling one without a token returns a standard `401` challenge, which is the cue for your client to run the OAuth flow.

Execution on Doma chains is **gasless** — the user needs no ETH on the Doma chain to trade.

## Which surface should I use?

Doma exposes agent-facing capability three ways. They overlap deliberately; pick by how much control you want.

| Surface                                             | Best for                                                                                                                             | Setup                  |
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- |
| **MCP server** (this section)                       | Any MCP client — Claude, Cursor, Codex, your own agent. Tools are discovered automatically and stay current with no action from you. | One URL                |
| [**AgentRoot skills**](/agentic-commerce/agentroot) | Agents that load `SKILL.md` files and want Doma's own instructions for multi-step workflows, discovered over DNS.                    | One-time skill install |
| [**Doma CLI**](/agentic-commerce/doma-cli)          | Humans in a terminal, scripts, and CI — plus operations the MCP server doesn't expose yet (bridging, subdomains, selling).           | `npm install -g`       |

If you are wiring up an agent and don't have a reason to prefer another path, start here.

## Where to go next

* [**Connect Your Client**](/agentic-commerce/mcp-server/connect) — copy-paste config for Claude Code, Claude, Cursor, VS Code, Codex, Windsurf, and anything else.
* [**Authorization & Spend Controls**](/agentic-commerce/mcp-server/authorization) — how the OAuth flow and the spend allowance work, and how to revoke.
* [**Tool Reference**](/agentic-commerce/mcp-server/tools) — the full tool surface at a glance, and what isn't exposed yet.
* [**Examples**](/agentic-commerce/mcp-server/examples) — real prompts and what the agent does with them.


# Connect Your Client

Add one URL to your MCP client and you're done. Pick the network you want:

```
https://mcp.doma.xyz/mcp           # Doma Mainnet
https://mcp-testnet.doma.xyz/mcp   # Doma Testnet
```

{% hint style="success" %}
**Start on testnet.** The read tools behave identically on both networks, and testnet lets you exercise the buy and swap flows without spending real money. New tools also reach testnet first.
{% endhint %}

No API key, token, or account is needed to connect. Authorization only comes up the first time the agent calls a tool that spends money — see [Authorization & Spend Controls](/agentic-commerce/mcp-server/authorization).

## Claude Code

```bash
claude mcp add --transport http doma https://mcp.doma.xyz/mcp
```

Then run `/mcp` inside Claude Code to check the connection and, when you want the execute tools, to start the authorization flow.

## Claude (web and desktop)

Open **Settings → Connectors → Add custom connector**, then paste the URL. Claude walks you through authorization in the browser when it's needed.

## Cursor

Add the server to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (per project):

```json
{
  "mcpServers": {
    "doma": {
      "url": "https://mcp.doma.xyz/mcp"
    }
  }
}
```

## VS Code (GitHub Copilot)

Add it to `.vscode/mcp.json` in your workspace:

```json
{
  "servers": {
    "doma": {
      "type": "http",
      "url": "https://mcp.doma.xyz/mcp"
    }
  }
}
```

## Codex CLI

Add it to `~/.codex/config.toml`:

```toml
[mcp_servers.doma]
url = "https://mcp.doma.xyz/mcp"
```

## Windsurf

Add it to `~/.codeium/windsurf/mcp_config.json`:

```json
{
  "mcpServers": {
    "doma": {
      "serverUrl": "https://mcp.doma.xyz/mcp"
    }
  }
}
```

## Any other MCP client

The server is a standard remote MCP server: **Streamable HTTP** transport, OAuth 2.1 with dynamic client registration (RFC 7591) and mandatory S256 PKCE, and RFC 9728 protected-resource metadata for discovery. Any spec-compliant client works with just the URL — most use a config block shaped like the Cursor example above.

If your client only supports local **stdio** servers, bridge to the remote one:

```json
{
  "mcpServers": {
    "doma": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.doma.xyz/mcp"]
    }
  }
}
```

{% hint style="info" %}
Client config formats change faster than these docs — if a block above doesn't match what your client expects, check its own MCP documentation. The only Doma-specific value is the URL.
{% endhint %}

## Verify the connection

Ask the agent:

> Ping the Doma MCP server and tell me which network it's on.

Then try a read:

> Look up example.com on Doma and tell me if it's tokenized.

## Troubleshooting

| Symptom                                                                            | Cause and fix                                                                                                                                                                                             |
| ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Client connects but lists no tools                                                 | Almost always a transport mismatch — the client is trying stdio or SSE against an HTTP server. Set the transport explicitly (`--transport http`, `"type": "http"`), or use the `mcp-remote` bridge above. |
| Tools list fine, but a buy or swap returns a `401`                                 | Expected. That's the authorization challenge; complete the OAuth flow in your client (in Claude Code, `/mcp`). See [Authorization](/agentic-commerce/mcp-server/authorization).                           |
| Authorization opens a browser and stalls on "Handing you back to your MCP client…" | Click the **Return to your MCP client** link on the page. Some browsers block the automatic hand-off; the link completes it.                                                                              |
| A buy or swap is refused for insufficient allowance                                | Your spend cap is used up. Re-authorize to refill it — see [Managing the allowance](/agentic-commerce/mcp-server/authorization#managing-the-allowance).                                                   |
| Everything works interactively but fails in CI                                     | Headless environments can't complete a browser consent step. The read tier works unauthenticated in CI; for automated execution use the [Doma CLI](/agentic-commerce/doma-cli) with a dedicated wallet.   |


# Authorization & Spend Controls

The read tools need nothing. The execute tools — buying a listed domain, swapping tokens — sign and broadcast real transactions from your Doma wallet, so they sit behind an authorization step and a spend budget you control.

This page is the user-facing model. For the architecture underneath it, see [Agentic Wallet](/agentic-commerce/agentic-wallet).

## Authorize once

The first time an agent calls an execute tool without a token, the server answers with a standard OAuth challenge instead of an error. Your MCP client turns that into a browser flow:

{% stepper %}
{% step %}

### The client starts the flow

Your MCP client registers itself and opens the Doma consent page in your browser. In Claude Code this is what `/mcp` triggers; hosted clients like Claude do it inline when a tool needs it.
{% endstep %}

{% step %}

### You sign in and set a budget

You sign in to Doma with your existing account, pick the wallet the agent will act from, enter a USD spending budget (default `$200`), and review exactly what you're approving:

* Trade on Doma chains only
* Cannot transfer your funds to outside addresses
* Spend up to the budget you entered
* When the budget is used, you'll be asked to refill
* Until you revoke
  {% endstep %}

{% step %}

### The agent gets a scoped session

Approving mints a session bound to that one wallet, that one network, and that one spend allowance. From then on the agent can execute within the budget without asking again.
{% endstep %}
{% endstepper %}

Authorization is **per network**. A session on testnet grants nothing on mainnet, and vice versa.

## What the agent can and cannot do

The agent **never holds your private key**. The key stays inside Privy's HSM (see [Agentic Wallet](/agentic-commerce/agentic-wallet)); the agent holds a delegated authorization that Privy enforces a policy against, and Doma's own spend ledger gates every call on top of that.

**Can:** buy listed domains, swap tokens, and read anything — on the Doma network you authorized, from the one wallet you approved, within the budget you set.

**Cannot:**

* Transfer your funds to an outside address.
* Act on any other chain.
* Grant an ERC-20 approval — a standing approval hands out authority the budget can't bound, so it's refused outright (*revoking* one is allowed). Tools that need an approval (swap, buy) batch it with the trade into a single atomic operation instead.
* Exceed the budget, the per-transaction ceiling, or the rate limit.
* Sell anything. Listing a domain and accepting an offer are not exposed as tools.

## The spend allowance

Every execution is priced in USD by simulating it, then charged against your allowance before it's broadcast. Reads are free and never touch it.

| Control                   | What it does                                                                           | Default                        |
| ------------------------- | -------------------------------------------------------------------------------------- | ------------------------------ |
| **Spend cap**             | Cumulative USD the agent may spend on this grant before it must be refilled            | `$200` — you set it at consent |
| **Per-transaction limit** | A single transaction above this is refused, even with budget remaining                 | `$250`                         |
| **Minimum charge**        | Floor charged per execution, so gasless writes that move no funds still consume budget | `$0.01`                        |
| **Rate limit**            | Maximum executions per allowance per window                                            | `30` per 60s                   |

{% hint style="warning" %}
The cap is a **budget, not an escrow**. It bounds what the agent can spend; it doesn't lock funds or guarantee a trade is favourable. Quote first (`marketplace.quote.v1`, `tokens.quote.v1`) and set a cap you'd be comfortable losing to a bad decision.
{% endhint %}

## Managing the allowance

Three tools let the agent — and therefore you, in plain language — manage the session:

| Tool                 | What it does                                                                           |
| -------------------- | -------------------------------------------------------------------------------------- |
| `agent.allowance.v1` | Report the wallet, network, cap, spent, remaining, and the limits in force. Read-only. |
| `agent.setCap.v1`    | **Lower** the cap. Lowering needs only the session; raising requires re-consent.       |
| `agent.revoke.v1`    | Kill switch — revoke the allowance so no further execution is authorized.              |

Just ask:

> How much budget does my Doma agent have left?

> Lower my Doma spend cap to $20.

> Revoke my Doma agent session.

**Refilling.** When the budget runs out, re-run the consent flow. Re-consenting while the grant is active tops up the same grant; re-consenting *after* a revoke opens a new grant with a fresh budget.

**Revoking completely.** `agent.revoke.v1` stops all further execution immediately. To also detach the agent signer from the wallet, do it from the Doma launchpad.

## Gasless execution

Executions on Doma chains are broadcast as **sponsored transactions** — no ETH needed on the Doma chain, and gas is never charged to your allowance; only the value the transaction moves is.

One consequence worth knowing: a successful call means the operation was **broadcast on-chain**. For writes a registrar or DNS layer then applies, that's not the same as the change being live — read the corresponding read tool back to confirm.

## When an execution is refused

Refusals come back as a structured result with a reason and your current budget view — not as a crash — so the agent can explain and adapt:

| Reason                                       | What happened                                                                            |
| -------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `allowance_exhausted` / `allowance_exceeded` | The budget is spent, or this transaction would exceed it. Refill by re-consenting.       |
| `allowance_revoked`                          | The session was revoked. Re-authorize to get a new one.                                  |
| `per_tx_limit_exceeded`                      | This one transaction is above the per-transaction ceiling.                               |
| `rate_limited`                               | Too many executions too fast. Wait for the window to roll over.                          |
| `approval_not_permitted`                     | The transaction would grant an ERC-20 approval. Use the swap or buy tool instead.        |
| `chain_not_supported`                        | The transaction targets a chain this server doesn't broadcast on.                        |
| `simulation_failed`                          | The transaction wouldn't succeed on-chain, so it was never sent and nothing was charged. |

Every execute-tier outcome — broadcast or refused — is written to an append-only audit trail on Doma's side.


# Tool Reference

You don't need this page to use the server — your MCP client discovers the tools, parameters, and schemas on connect, and the descriptions the agent reads are more detailed than the summaries here. This page is for deciding whether to connect.

{% hint style="info" %}
**Your client's tool list is authoritative.** Tools ship to testnet before mainnet, so the two endpoints can differ for a while.
{% endhint %}

## Read tools

No authentication. Nothing is signed, nothing is spent.

### Domains

| Tool                     | What it does                                                                                                                       |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `domains.lookup.v1`      | Look up a tokenized domain by name: expiry, registrar, nameservers, ownership token, fractionalization status, active offer count. |
| `domains.search.v1`      | Search tokenized domains, filtered by name fragment and TLD. Paginated.                                                            |
| `domains.check.v1`       | Check availability for registration across supported TLDs (RDAP).                                                                  |
| `domains.pricing.v1`     | Per-registrar USD pricing for one operation (registration, renewal, or transfer), plus availability and coupon validation.         |
| `domains.nameservers.v1` | The nameservers currently configured for a tokenized domain.                                                                       |

### DNS

| Tool             | What it does                                                                        |
| ---------------- | ----------------------------------------------------------------------------------- |
| `dns.records.v1` | The DNS record sets for a tokenized domain, optionally filtered by host. Paginated. |

### Marketplace

| Tool                      | What it does                                                                                                                         |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `marketplace.listings.v1` | Active listings for tokenized domains, filtered by name, TLD, or network. Paginated.                                                 |
| `marketplace.offers.v1`   | Active offers on a tokenized domain, optionally filtered by offerer. Paginated.                                                      |
| `marketplace.quote.v1`    | What an order actually costs: total price, fee breakdown (treasury fee, royalties), and seller proceeds — for a listing or an offer. |

### Tokens

| Tool                   | What it does                                                                                                                        |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `tokens.info.v1`       | On-chain details for a Doma token by token ID: owner, network, contract, validity.                                                  |
| `tokens.price.v1`      | USD price, 24h change, TVL, and volume for one or more Doma token contracts.                                                        |
| `tokens.fractional.v1` | Fractional (ERC-20) domain tokens with their contracts and trading venue, filtered by status, launch timing, name, TLD, or network. |
| `tokens.quote.v1`      | Exact-in swap quote, routed automatically between the bonding curve and Uniswap pools depending on whether the token has graduated. |

### Wallet & server

| Tool                 | What it does                                                                                                 |
| -------------------- | ------------------------------------------------------------------------------------------------------------ |
| `wallet.balances.v1` | A wallet's Doma balances: fractional domain tokens, currency tokens, native balances, and specified ERC-20s. |
| `server.ping.v1`     | Liveness probe. Reports the server timestamp and which Doma network this server executes on.                 |

## Execute tools

These sign and broadcast from your wallet and require [authorization](/agentic-commerce/mcp-server/authorization). Each one is gated by your spend allowance, the per-transaction limit, and the rate limit.

| Tool                 | What it does                                                                                                                                                                                                                                                  |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `marketplace.buy.v1` | Buy a listed tokenized domain: resolves the listing (cheapest active one unless you name an order), pays the total price, receives the ownership token — one atomic gasless operation with the payment approval and the Seaport fulfillment batched together. |
| `tokens.swap.v1`     | Swap tokens on Doma, exact-in, routed like `tokens.quote.v1` — one atomic gasless operation with the approval and the trade batched together.                                                                                                                 |
| `agent.execute.v1`   | Sign and broadcast a prepared transaction. The escape hatch for operations without a dedicated tool; ERC-20 approvals are refused.                                                                                                                            |
| `agent.allowance.v1` | Report the session's wallet, network, budget, and limits. Read-only, but session-scoped.                                                                                                                                                                      |
| `agent.setCap.v1`    | Lower the spend cap. Raising requires re-consent.                                                                                                                                                                                                             |
| `agent.revoke.v1`    | Revoke the spend allowance. Kill switch.                                                                                                                                                                                                                      |

Execute tools are deliberately **listed to unauthorized clients** so an agent can see what's possible and prompt you to authorize, rather than concluding the server is read-only. Calling one without a token returns a `401` challenge.

## Not yet available

* **Selling.** Listing a domain for sale and accepting an offer are not exposed as tools. Reading listings and offers, quoting them, and buying are.
* **DNS and nameserver writes.** `dns.records.v1` and `domains.nameservers.v1` read; the matching write tools are built but not yet released. Use the [Doma CLI](/agentic-commerce/doma-cli) meanwhile.
* **Registration.** Checking availability and pricing is here; registering a domain is not. See the [MPP skill](/agentic-commerce/skills/mpp).
* **Bridging and subdomains.** [Doma CLI](/agentic-commerce/doma-cli) only.
* **Solana live balance reads.** `wallet.balances.v1` reads EVM chains live and lists anything it skipped.

## Versioning

Every tool name carries an explicit version suffix (`domains.lookup.v1`). Breaking changes ship as a new version alongside the old one, so an agent pinned to `.v1` keeps working; backwards-compatible changes land in place.


# Examples

What the server feels like in use. You don't name tools or pass parameters — you ask, and the agent picks the tools and chains them.

## Research a name

> Is `spark.ai` tokenized on Doma? If it is, tell me who owns it, when it expires, and whether anyone has an open offer on it.

One lookup answers all of it — ownership, expiry, registrar, nameservers, and open offer count come back together.

## Find something to register

> I want a short two-word `.ai` domain for an AI recruiting tool. Check availability for ten candidates and tell me the cheapest three across registrars.

The agent generates the candidates itself, checks live availability, and prices the survivors per registrar — the "generate candidates" half is the part an API can't do for you.

## Shop the marketplace with a budget

> Show me every active Doma listing under $500, and for the three cheapest tell me the total I'd actually pay including fees and royalties.

Quoting surfaces the real number — fees and royalties — not the sticker price.

## Buy one

> Buy the cheapest active listing for `example.com`, but only if the all-in total is under $200. Quote it first and tell me the number before you buy.

Quote, check against the ceiling, then one atomic gasless purchase — inside the budget you set at [authorization](/agentic-commerce/mcp-server/authorization).

## Trade a fractional token

> Which Doma launchpad tokens are still on their bonding curve and opened for trading this week? Give me 24h price change for each, then swap $25 of USDC into whichever is down the most.

The agent doesn't need to know bonding curve vs. Uniswap pool — routing is the tool's job.

## Audit a portfolio

> Look at the balances in my wallet, and for every fractional domain token I hold tell me what it's worth now and whether it's up or down over 24 hours.

## Check the DNS on a domain you own

> What DNS records are set on `mysite.com` right now? Show me the A and MX records and tell me which nameservers it's delegated to.

## Stay in control

> How much spend budget does my Doma agent have left?

> Lower my cap to $20.

> Revoke my Doma agent session.

Supervising the agent is the same conversation as directing it — see [Authorization & Spend Controls](/agentic-commerce/mcp-server/authorization).


# AgentRoot

**The open protocol for the agentic web.** Live spec and registry: [agentroot.io](https://agentroot.io).

AgentRoot lets any domain declare its AI capabilities (agents, MCP servers, skills, A2A endpoints, payment endpoints, anything else) using a DNS TXT record and a JSON zone file. Like DNS resolves domain names to IP addresses, AgentRoot resolves domain names to agent capabilities.

> No gatekeepers. No API keys. Just DNS and JSON.

> *Domains are the identity layer for the agentic web. AgentRoot is the DNS of that layer.*

## How discovery works

<figure><img src="/files/1itTVERD8KM8NpgAbVdK" alt="AgentRoot discovery sequence. The AI agent queries DNS for the TXT record at _agentroot.[domain]; DNS returns v=ar1 zone=https://.../agentroot.json; the agent fetches the zone file at /.well-known/agentroot.json; the web server returns a records array containing agent, mcp, skill, a2a, and payment records. The agent then picks the record matching user intent and installs it locally."><figcaption></figcaption></figure>

Discovery is open by design: any domain can publish a zone, any agent can resolve it, no central registry sits in the middle. AgentRoot is general-purpose; this section uses Doma as the example, but the same flow discovers capabilities published at any domain.

## The two pieces

| Piece                               | Where it lives    | Purpose                                                             |
| ----------------------------------- | ----------------- | ------------------------------------------------------------------- |
| TXT record at `_agentroot.<domain>` | Your DNS provider | Points discoverers to the zone file URL. Format: `v=ar1 zone=<url>` |
| `.well-known/agentroot.json`        | Your web server   | The actual list of records the domain offers                        |

## Record types

Five types are built into the protocol:

| Type      | Purpose                               | Required fields                                                                                 |
| --------- | ------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `agent`   | Stand-alone AI agent endpoint         | `endpoint`, `protocol`, `capabilities`                                                          |
| `mcp`     | Model Context Protocol server         | `endpoint` (or `install` for stdio), `transport` (`stdio` / `sse` / `streamable-http`), `tools` |
| `skill`   | A `SKILL.md` collection (one or many) | One of: `index` URL, `skill_md` URL, or inline `skills` array                                   |
| `a2a`     | Agent-to-Agent communication endpoint | `endpoint`, `capabilities`                                                                      |
| `payment` | Payment endpoint (e.g. MPP, x402)     | `endpoint`, `protocols`, `methods`, `assets`                                                    |

**Custom types are welcome.** Any string is a valid `type`. The five above are conventions, not a closed set. Publish whatever your domain offers.

For the full schema (including optional base fields like `auth`, `pricing`, `category`, `docs`), see [Zone File Reference](/agentic-commerce/agentroot/zone-file-reference).

## Two ways to publish

| Mode                        | When to use                                                                                                                                                                          |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Zone file** (recommended) | Most domains. One JSON file at `.well-known/agentroot.json` describes everything.                                                                                                    |
| **Inline TXT**              | Single-capability domains, or tokenized domains advertising one skill plus one payment endpoint with no infrastructure to host JSON. The whole record lives in the TXT record value. |

A single `_agentroot` name can also carry **multiple TXT records** (one skill + one payment + one MCP, for example). The resolver indexes each as a separate record. See [Zone File Reference → Inline Mode](/agentic-commerce/agentroot/zone-file-reference#inline-mode) for the wire format.

## Design principles

1. **DNS is the source of truth.** A TXT record proves domain ownership. No accounts needed.
2. **One domain, many records.** Any number of capabilities, any types, one zone file.
3. **Subdomains are first-class.** Each gets its own zone. Parent zones can reference children but each subdomain's DNS is authoritative.
4. **Types are extensible.** The five built-ins are conventions; any string is a valid type.
5. **Keep it simple.** Two required fields on a zone, four required fields on a record, everything else optional.
6. **Open and forkable.** The protocol is DNS + JSON. Works without AgentRoot the registry. Works without any registry at all.

## What's next

* [Discover & Use Skills](/agentic-commerce/agentroot/discover-and-use): install AgentRoot in your AI agent and resolve / install skills from any domain.
* [Zone File Reference](/agentic-commerce/agentroot/zone-file-reference): full `agentroot.json` schema, inline-mode TXT format, optional base fields, validation rules.
* [Publish Your Own](/agentic-commerce/agentroot/publish-your-own): three steps to put your domain on the agentic web.

For the full protocol specification, the live registry, and reference implementations, see [agentroot.io](https://agentroot.io).


# Discover & Use Skills

This page walks through installing AgentRoot in your AI agent, resolving a domain's published records, and installing a skill into your agent's local skills folder.

## Install AgentRoot in your agent

Three options depending on how your agent loads tools:

### Option 1: As a CLI

```bash
npm install -g agent-root
agent-root --version
```

Then call `agent-root resolve <domain>` directly, or wrap it in a tool definition for your agent.

### Option 2: As an MCP server

Add AgentRoot to your agent's MCP config. The exact location varies per tool:

| Tool           | Config file                                                       |
| -------------- | ----------------------------------------------------------------- |
| Claude Code    | `~/.claude.json` (`mcpServers` block)                             |
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Cursor         | `~/.cursor/mcp.json`                                              |
| Codex CLI      | `~/.codex/mcp.json`                                               |

Generic config snippet:

```json
{
  "mcpServers": {
    "agent-root": {
      "command": "npx",
      "args": ["-y", "agent-root", "mcp"]
    }
  }
}
```

After restart, the agent has access to `agentroot_resolve`, `agentroot_install_record`, and `agentroot_list` tools.

### Option 3: As a library

```bash
npm install @agentroot/core
```

Use programmatically when building your own agent or backend integration.

## Resolve a domain

Discover what a domain offers:

```bash
npx agent-root resolve doma.xyz
```

Sample output:

```json
{
  "domain": "doma.xyz",
  "records": [
    { "type": "skill", "id": "doma-protocol", "name": "Doma Protocol", "skill_md": "..." },
    { "type": "skill", "id": "doma-mpp",      "name": "Doma MPP",      "skill_md": "..." },
    { "type": "skill", "id": "secondary-sales", "name": "Secondary Sales", "skill_md": "..." },
    { "type": "skill", "id": "trade-tokens",  "name": "Trade Tokens",  "skill_md": "..." },
    { "type": "agent", "id": "doma-mpp-payment", "endpoint": "https://mpp.doma.xyz" }
  ]
}
```

This is the same data the agent uses internally to decide which skill to install for a given user intent.

## Install a skill

Pick a record by `id` and install it:

```bash
npx agent-root install doma.xyz/trade-tokens
```

Where the skill lands depends on which agent tool is detected first on `PATH` / config:

| Tool                         | Skill install path                          |
| ---------------------------- | ------------------------------------------- |
| Claude Code / Claude Desktop | `~/.claude/skills/doma.xyz/trade-tokens/`   |
| Cursor                       | `~/.cursor/skills/doma.xyz/trade-tokens/`   |
| Codex CLI                    | `~/.codex/skills/doma.xyz/trade-tokens/`    |
| Gemini CLI                   | `~/.gemini/skills/doma.xyz/trade-tokens/`   |
| OpenCode                     | `~/.opencode/skills/doma.xyz/trade-tokens/` |

Force a specific tool with `--tool=<name>`:

```bash
npx agent-root install doma.xyz/trade-tokens --tool=cursor
```

## List installed skills

```bash
npx agent-root list
```

Output shows skill ID, source domain, install path, and verification status.

{% hint style="info" %}
Skills are downloaded to disk and become part of your agent's runtime context. They update only when you re-install them; there is no auto-refresh. Re-run `agent-root install` after a publisher updates their `SKILL.md`.
{% endhint %}

## Verify the install in your agent

After installing, prompt your agent with one of the skill's trigger phrases (listed in each skill's [reference page](/agentic-commerce/skills)). For example, after installing `trade-tokens`:

{% hint style="info" %}
**Example prompt:** Buy 10 USDC worth of `<token-name>` token.
{% endhint %}

The agent should pick up the trigger, follow the workflow encoded in `SKILL.md`, and walk you through prerequisites + execution.

## What's next

* [Zone File Reference](/agentic-commerce/agentroot/zone-file-reference): understand what's in the JSON the resolver returns.
* [Publish Your Own](/agentic-commerce/agentroot/publish-your-own): author a zone for your own domain.
* [Skills](/agentic-commerce/skills): reference pages for the three skills Doma publishes.


# Zone File Reference

The `.well-known/agentroot.json` zone file is the canonical declaration of what AI capabilities a domain offers. This page documents the full schema, the inline-TXT alternative, and the validation rules a publisher must respect.

## Top-level structure

```json
{
  "domain": "string",
  "records": [ /* array of records */ ],
  "subdomains": [ "optional", "list", "of", "subdomains" ]
}
```

| Field        | Type      | Required | Notes                                                                                                                |
| ------------ | --------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `domain`     | string    | yes      | Apex domain. Must exactly match the host serving the file.                                                           |
| `records`    | array     | yes      | List of capability records (see types below).                                                                        |
| `subdomains` | string\[] | no       | Discovery hint: subdomains that publish their own `_agentroot` records. Each subdomain's DNS is the source of truth. |

Additional top-level fields (e.g. `version`, `contact`) are preserved but not part of the protocol.

## TXT record format

Independent of the zone file. Set this DNS record at `_agentroot.<domain>`:

```
v=ar1 zone=https://<domain>/.well-known/agentroot.json
```

Field descriptions:

* `v=ar1`: protocol version, always literal `ar1` for V1.
* `zone=<url>`: absolute HTTPS URL to the zone file. Must be reachable without authentication.

## Common base fields

Every record (regardless of `type`) shares the same required base, plus a set of optional metadata fields any consumer can read.

### Required base

| Field         | Type   | Notes                                                            |
| ------------- | ------ | ---------------------------------------------------------------- |
| `type`        | string | `agent`, `mcp`, `skill`, `a2a`, `payment`, or any custom string. |
| `id`          | string | Unique within the zone. Lowercase alphanumeric + hyphens.        |
| `name`        | string | Human-readable display name.                                     |
| `description` | string | One or two sentences describing what it does.                    |

### Optional base

These are **labels**, not protocol definitions. `"auth": "api-key"` means "you'll need an API key" (it does not define the auth flow). Link to your docs for details.

| Field      | Type      | Description                                                              |
| ---------- | --------- | ------------------------------------------------------------------------ |
| `auth`     | string    | Auth hint: `"none"`, `"api-key"`, `"bearer"`, `"oauth2"`.                |
| `pricing`  | string    | Pricing hint: `"free"`, `"freemium"`, `"paid"`.                          |
| `payments` | string\[] | Payment protocols accepted, e.g. `["mpp", "x402", "stripe-acp", "ap2"]`. |
| `docs`     | string    | URL to documentation.                                                    |
| `source`   | string    | URL to source code.                                                      |
| `category` | string    | Semantic category, e.g. `"devtools"`, `"data"`, `"finance"`.             |

## Record types

### `skill` record

Declares a `SKILL.md` collection (instructions for AI agents).

```json
{
  "type": "skill",
  "id": "coding-helpers",
  "name": "Coding Helpers",
  "description": "Skills for linting, testing, and deployment workflows.",
  "index": "https://examplecorp.com/.agents/skills/index.json"
}
```

| Field      | Type  | Required     | Notes                                                    |
| ---------- | ----- | ------------ | -------------------------------------------------------- |
| `skill_md` | URL   | one of these | Absolute URL to a single `SKILL.md`.                     |
| `index`    | URL   | one of these | Absolute URL to an `index.json` listing multiple skills. |
| `skills`   | array | one of these | Inline list of skill objects (alternative to fetching).  |

Exactly one of `skill_md`, `index`, or `skills` must be present.

**`index.json` format** (when using `index`):

```json
{
  "schema": "agent-skills/1.0",
  "domain": "examplecorp.com",
  "skills": [
    {
      "id": "lint-fix",
      "name": "Lint Fixer",
      "description": "Auto-fix common linting issues.",
      "skill_md": "https://examplecorp.com/.agents/skills/lint-fix/SKILL.md"
    }
  ]
}
```

### `mcp` record

Declares a Model Context Protocol server.

```json
{
  "type": "mcp",
  "id": "examplecorp-tools",
  "name": "ExampleCorp Tools",
  "description": "Database query and visualization tools.",
  "endpoint": "https://api.examplecorp.com/mcp",
  "transport": "streamable-http",
  "tools": [
    { "name": "search_catalog", "description": "Search the product catalog." }
  ]
}
```

| Field       | Type   | Required              | Notes                                                                                  |
| ----------- | ------ | --------------------- | -------------------------------------------------------------------------------------- |
| `endpoint`  | URL    | for remote transports | URL of the MCP server. Required when `transport` is `sse` or `streamable-http`.        |
| `transport` | string | yes                   | One of `stdio`, `sse`, `streamable-http`.                                              |
| `install`   | object | for stdio             | When `transport` is `stdio`: `{ "package": "@pkg/name", "command": "npx @pkg/name" }`. |
| `tools`     | array  | no                    | List of tool definitions exposed by the server.                                        |

Each entry in `tools` is an object with at minimum:

| Sub-field     | Type   | Required | Notes                                                    |
| ------------- | ------ | -------- | -------------------------------------------------------- |
| `name`        | string | yes      | Tool identifier; unique within the `tools` array.        |
| `description` | string | yes      | One-line summary, shown to agents during tool selection. |

Additional MCP tool fields (input schema, output schema) follow the [MCP specification](https://modelcontextprotocol.io/specification).

### `agent` record

Declares an AI agent endpoint.

```json
{
  "type": "agent",
  "id": "support-agent",
  "name": "ExampleCorp Support",
  "description": "Answers questions about ExampleCorp integration.",
  "endpoint": "https://api.examplecorp.com/agent",
  "protocol": "a2a",
  "capabilities": ["customer-support", "order-status"]
}
```

| Field          | Type      | Required | Notes                                                                         |
| -------------- | --------- | -------- | ----------------------------------------------------------------------------- |
| `endpoint`     | URL       | yes      | URL to reach the agent.                                                       |
| `protocol`     | string    | no       | `"a2a"` (default), `"rest"`, `"graphql"`, `"websocket"`, or any custom value. |
| `capabilities` | string\[] | no       | Free-form capability tags.                                                    |
| `card`         | URL       | no       | URL to a full agent-card JSON.                                                |

### `a2a` record

Declares an Agent-to-Agent communication endpoint.

```json
{
  "type": "a2a",
  "id": "examplecorp-a2a",
  "name": "ExampleCorp A2A",
  "description": "Negotiate and execute deals with other agents.",
  "endpoint": "https://api.examplecorp.com/a2a",
  "capabilities": ["negotiate", "quote", "execute"]
}
```

| Field          | Type      | Required | Notes                      |
| -------------- | --------- | -------- | -------------------------- |
| `endpoint`     | URL       | yes      | URL of the A2A endpoint.   |
| `capabilities` | string\[] | yes      | Free-form capability tags. |

### `payment` record

Declares a payment endpoint that agents can call to settle (e.g. for paying gated APIs).

```json
{
  "type": "payment",
  "id": "doma-mpp-payment",
  "name": "Doma MPP Payment",
  "description": "Settle MPP payments for tokenized domain registration.",
  "endpoint": "https://mpp.doma.xyz",
  "api_spec": "https://mpp.doma.xyz/openapi.json",
  "protocols": ["mpp"],
  "methods": ["tempo"],
  "assets": ["USDC"]
}
```

| Field       | Type      | Required | Notes                                          |
| ----------- | --------- | -------- | ---------------------------------------------- |
| `endpoint`  | URL       | yes      | URL of the payment endpoint.                   |
| `api_spec`  | URL       | no       | OpenAPI document describing the endpoint.      |
| `protocols` | string\[] | yes      | Payment protocols, e.g. `["mpp"]`, `["x402"]`. |
| `methods`   | string\[] | yes      | Settlement methods (chain or rail names).      |
| `assets`    | string\[] | yes      | Accepted assets, e.g. `["USDC", "ETH"]`.       |

### Custom types

Any string is a valid `type` value. The five above are conventions; the protocol does not gate publishers to a closed set. A consumer that doesn't understand a custom type ignores the record.

## Worked example: full zone

A fictional `examplecorp.com` publishing one of each built-in record type:

```json
{
  "domain": "examplecorp.com",
  "records": [
    {
      "type": "skill",
      "id": "coding-helpers",
      "name": "Coding Helpers",
      "description": "Skills for linting, testing, and deployment workflows.",
      "index": "https://examplecorp.com/.agents/skills/index.json"
    },
    {
      "type": "mcp",
      "id": "examplecorp-tools",
      "name": "ExampleCorp Tools",
      "description": "Database query and visualization tools.",
      "endpoint": "https://api.examplecorp.com/mcp",
      "transport": "streamable-http",
      "auth": "api-key",
      "pricing": "freemium",
      "tools": [
        { "name": "search_catalog", "description": "Search the product catalog." }
      ]
    },
    {
      "type": "agent",
      "id": "support-agent",
      "name": "ExampleCorp Support",
      "description": "Answers questions about ExampleCorp integration.",
      "endpoint": "https://api.examplecorp.com/agent",
      "protocol": "a2a",
      "capabilities": ["customer-support", "order-status"]
    },
    {
      "type": "a2a",
      "id": "examplecorp-a2a",
      "name": "ExampleCorp A2A",
      "description": "Negotiate and execute deals with other agents.",
      "endpoint": "https://api.examplecorp.com/a2a",
      "capabilities": ["negotiate", "quote", "execute"]
    }
  ],
  "subdomains": ["api.examplecorp.com"]
}
```

## Inline mode

For domains with one or two capabilities, the record can live entirely in the DNS TXT value: no zone file, no web hosting required. Tokenized domains use this pattern (one skill record + one payment record on a single `_agentroot` name).

### Format

```
_agentroot.<domain>  IN  TXT  "v=ar1 type=<type> name=<name> [field=value ...]"
```

| Field  | Required | Description                              |
| ------ | -------- | ---------------------------------------- |
| `v`    | yes      | Always `ar1`.                            |
| `type` | yes      | Record type.                             |
| `name` | yes      | Display name (use `\` to escape spaces). |

All other fields from the record schema are passed as `key=value` pairs.

### Examples

```dns
; Inline agent
_agentroot.example.com  IN TXT "v=ar1 type=agent name=My\ Bot endpoint=https://example.com/agent protocol=a2a"

; Inline MCP server
_agentroot.example.com  IN TXT "v=ar1 type=mcp name=DB\ Tools endpoint=https://example.com/mcp transport=sse"

; Inline skill collection
_agentroot.example.com  IN TXT "v=ar1 type=skill name=Helpers index=https://example.com/.agents/skills/index.json"

; Inline payment (tokenized-domain pattern)
_agentroot.example.com  IN TXT "v=ar1 type=payment id=doma-mpp-payment endpoint=https://mpp.doma.xyz api_spec=https://mpp.doma.xyz/openapi.json protocols=mpp methods=tempo assets=USDC"
```

### Parsing rules

1. Fields are space-separated `key=value` pairs.
2. Spaces within values are escaped with `\` (backslash-space).
3. Array fields (`capabilities`, `payments`, `protocols`, `methods`, `assets`, `caps`) are comma-separated: `protocols=mpp,x402`. AgentRoot normalizes these to arrays in the stored record.
4. A single TXT string must be ≤ 255 bytes. If a record's content exceeds 255 bytes, drop verbose fields like `name` or `description`. Richer metadata is available via `api_spec`, `zone=`, or `skill_md` endpoints.

### Multi-record discovery

A single `_agentroot` name can carry several TXT records of different types. The resolver applies this rule:

1. Fetch all `v=ar1` TXT records at `_agentroot.<domain>`.
2. **If any record is `zone=<url>`**: the zone file is authoritative. Index every record from the JSON; ignore sibling inline TXTs on the same name.
3. **Otherwise**: iterate every remaining `v=ar1` record, parse each, dedupe by `id`, and index each as a separate record.

```dns
; Tokenized domain with skill + payment inline
_agentroot.alice.xyz  IN TXT "v=ar1 skill=https://doma.xyz/.well-known/skills/secondary-sales/SKILL.md"
_agentroot.alice.xyz  IN TXT "v=ar1 type=payment id=doma-mpp-payment endpoint=https://mpp.doma.xyz protocols=mpp methods=tempo assets=USDC"
```

`dig +short TXT _agentroot.alice.xyz` returns two distinct strings; AgentRoot stores both as separate records (one skill, one payment).

## Validation and hosting rules

* All URLs in the zone file must be absolute and HTTPS (`http://` is rejected by resolvers).
* `id` slugs must be URL-safe (`[a-z0-9-]+`) and unique within `records[]`.
* `domain` must exactly match the host serving the file. Mismatches cause the zone to be rejected.
* For `skill` records, exactly one of `skill_md`, `index`, or `skills` must be present.
* For `mcp` records, `transport` must be one of `stdio`, `sse`, `streamable-http`.
* The zone file must be served with `Content-Type: application/json`.
* The zone file must be ≤ **1 MB**.
* Each inline TXT string must be ≤ **255 bytes**. Use multiple separate TXT records for multiple capabilities (not one record split across strings).

{% hint style="info" %}
Use `npx agent-root validate <path-to-zone.json>` to check a zone file locally before publishing.
{% endhint %}

## What's next

* [Publish Your Own](/agentic-commerce/agentroot/publish-your-own): three steps to put your domain on the agentic web.
* [Discover & Use Skills](/agentic-commerce/agentroot/discover-and-use): how agents consume the records you publish.


# Publish Your Own

Publishing AgentRoot for a domain you own is three steps: host a zone file, add a DNS TXT record, verify resolution. After that, your domain's AI capabilities are discoverable by any tool that speaks the protocol.

## Prerequisites

* You own a domain.
* You can publish a static file at `https://<your-domain>/.well-known/agentroot.json` (any web host).
* You can edit DNS records at your registrar / DNS provider (e.g. Cloudflare, Route 53, Squarespace, GoDaddy).

## The three steps

{% stepper %}
{% step %}

#### Step 1: Host a zone file

Create `agentroot.json` declaring whatever capabilities your domain offers. Refer to the [Zone File Reference](/agentic-commerce/agentroot/zone-file-reference) for the full schema.

A minimum viable zone with one skill record:

```json
{
  "domain": "<your-domain>",
  "records": [
    {
      "type": "skill",
      "id": "my-skill",
      "name": "My First Skill",
      "description": "What this skill does in one sentence.",
      "skill_md": "https://<your-domain>/.well-known/skills/my-skill/SKILL.md"
    }
  ]
}
```

Save the file at:

```
<your-web-root>/.well-known/agentroot.json
```

Confirm it's reachable:

```bash
curl -sf https://<your-domain>/.well-known/agentroot.json | head
```

It must return HTTP 200 and `Content-Type: application/json`.
{% endstep %}

{% step %}

#### Step 2: Add a DNS TXT record

At your DNS provider, create a TXT record:

| Field       | Value                                                         |
| ----------- | ------------------------------------------------------------- |
| Name / Host | `_agentroot`                                                  |
| Type        | `TXT`                                                         |
| Value       | `v=ar1 zone=https://<your-domain>/.well-known/agentroot.json` |
| TTL         | `3600` (default is fine)                                      |

The full record is `_agentroot.<your-domain>`.

{% hint style="info" %}
Some DNS UIs append the apex automatically (you enter `_agentroot`); others require the FQDN (`_agentroot.your-domain.com`). Check what your provider expects: getting this wrong is the most common publishing error.
{% endhint %}

Wait for DNS propagation (usually under a minute, occasionally up to your TTL).
{% endstep %}

{% step %}

#### Step 3: Verify resolution

Confirm the TXT record is published:

```bash
dig +short TXT _agentroot.<your-domain>
```

Expected output:

```
"v=ar1 zone=https://<your-domain>/.well-known/agentroot.json"
```

Then run the AgentRoot resolver end-to-end:

```bash
npx agent-root resolve <your-domain>
```

Expected: a JSON object containing your declared records. If the command errors with "no TXT record found" or "zone fetch failed", recheck the TXT value and the zone URL.

That's it. **Done.** Your domain's capabilities are now discoverable.
{% endstep %}
{% endstepper %}

## Alternative: inline mode (no zone file)

For domains with one or two capabilities, the entire record can live in the DNS TXT value (no web hosting needed). This is the pattern tokenized domains use to advertise a single skill plus a payment endpoint.

```dns
; Single capability inline
_agentroot.<your-domain>  IN  TXT  "v=ar1 type=mcp name=My\ Tools endpoint=https://<your-domain>/mcp transport=sse"

; Multiple capabilities = multiple TXT records on the same name
_agentroot.<your-domain>  IN  TXT  "v=ar1 skill=https://<your-domain>/.well-known/skills/my-skill/SKILL.md"
_agentroot.<your-domain>  IN  TXT  "v=ar1 type=payment id=my-pay endpoint=https://pay.<your-domain> protocols=mpp methods=tempo assets=USDC"
```

Inline records skip Step 1 (no zone file) and replace Step 2 (the TXT value carries the full record). See [Zone File Reference → Inline mode](/agentic-commerce/agentroot/zone-file-reference#inline-mode) for the full wire format and parsing rules.

## Optional: submit to the agentroot.io directory

Submission is **not required**. Your zone resolves for any agent that knows your domain whether or not it appears in any directory. Submitting to [agentroot.io](https://agentroot.io) only adds discoverability for agents browsing the catalog.

To submit, visit [agentroot.io](https://agentroot.io) and use the submit flow to provide:

* Your domain (apex)
* Optional: a category / tags
* Optional: contact email for verification follow-ups

The directory verifies your zone and indexes its records. Updates propagate hourly.

## Optional: host `SKILL.md` files alongside your zone

If you advertised a `skill` record with `skill_md`, you also need to publish that file. The convention is:

```
<your-web-root>/.well-known/skills/<skill-id>/SKILL.md
<your-web-root>/.well-known/skills/<skill-id>/examples/...
<your-web-root>/.well-known/skills/<skill-id>/reference/...
```

For the `SKILL.md` format itself, see the [Skills overview](/agentic-commerce/skills). Doma's published skills are good reference implementations:

* [Secondary Sales](https://doma.xyz/.well-known/skills/secondary-sales/SKILL.md)
* [Trade Tokens](https://doma.xyz/.well-known/skills/trade-tokens/SKILL.md)

## Updating your zone

Resolvers cache zone fetches for the TXT record's TTL (default 3600s = 1 hour). To force a refresh:

* Update the file at `.well-known/agentroot.json`.
* Bump the TXT record TTL to a low value (e.g. 60s) before edits if you need fast iteration; restore to 3600 once stable.

{% hint style="warning" %}
Removing or renaming a skill `id` is a breaking change for anyone with the skill installed. Prefer adding new IDs over mutating existing ones.
{% endhint %}

## What's next

* [Zone File Reference](/agentic-commerce/agentroot/zone-file-reference): full schema for everything you can publish.
* [Skills overview](/agentic-commerce/skills): `SKILL.md` format and how to author your own skills.


# Doma CLI

The Doma CLI (`@doma-protocol/cli`, distributed as the `doma` binary) is the local TypeScript tool that executes everything skills decide on: token swaps, marketplace orders, DNS edits, bridges, subdomain operations, and authentication. It is designed to be both human-driven (interactive prompts, pretty output) and AI-driven (`--quiet --format json` for clean machine-readable I/O).

## Install

```bash
npm install -g @doma-protocol/cli
doma --version
```

Requires **Node.js ≥ 20**.

## Configure

Configuration is stored in `~/.doma/config.json`. Set values either via the CLI:

```bash
doma config set <key> <value>
```

Or by editing `~/.doma/config.json` directly:

```json
{
  "privateKey": "<keychain-on-macos>",
  "apiKey": "<your-doma-api-key>",
  "chainId": 97477,
  "testnet": false,
  "walletMode": "private-key"
}
```

### Config keys

| Key             | Description                                                        | Default                |
| --------------- | ------------------------------------------------------------------ | ---------------------- |
| `privateKey`    | Wallet private key (required for transactions in private-key mode) | (none)                 |
| `apiKey`        | API key for the Doma GraphQL endpoint                              | (none)                 |
| `apiUrl`        | Override the API endpoint (`--testnet` flag overrides this)        | (none)                 |
| `routingApiUrl` | Override the routing API endpoint                                  | (none)                 |
| `chainId`       | Default chain ID for writes                                        | `97477` (Doma mainnet) |
| `testnet`       | Use Doma testnet instead of mainnet                                | `false`                |
| `walletMode`    | `agent` (Privy delegated) or `private-key` (local)                 | `private-key`          |

### Environment variable overrides

Env vars take precedence over `~/.doma/config.json`:

| Config key      | Env var                |
| --------------- | ---------------------- |
| `privateKey`    | `DOMA_PRIVATE_KEY`     |
| `apiKey`        | `DOMA_API_KEY`         |
| `apiUrl`        | `DOMA_API_URL`         |
| `routingApiUrl` | `DOMA_ROUTING_API_URL` |
| `chainId`       | `DOMA_CHAIN_ID`        |
| `testnet`       | `DOMA_TESTNET`         |

`walletMode` is config-only (no env-var override), change it via `doma config set walletMode <agent|private-key>`.

### macOS Keychain

On macOS, `doma config set privateKey` stores the key in the system Keychain rather than `config.json`. To force plain-text storage instead:

```bash
doma config set privateKey 0x… --plaintext
```

On Linux and Windows, `config.json` is used unconditionally.

{% hint style="warning" %}
Never paste a private key into chat or commit it to a repo. Use a dedicated wallet for testing and rotate it after.
{% endhint %}

## Network selection

Use `--testnet` on any command to target Doma Testnet:

```bash
doma --testnet token <token-name>
doma --testnet swap USDC <token-name> 10
```

Or persist the choice:

```bash
doma config set testnet true
```

## Supported chains

| Chain        | Chain ID |
| ------------ | -------- |
| Doma Mainnet | `97477`  |
| Doma Testnet | `97476`  |
| Ethereum     | `1`      |
| Base         | `8453`   |

## Sponsored gas

DNS and nameserver writes on the Doma chain use **sponsored gas** via ERC-4337 + EIP-7702. You don't need ETH on Doma chain to set DNS records or rotate nameservers. Other operations (swaps, bridges, marketplace transactions) require gas in the chain's native token.

## What's next

* [Wallet Modes](/agentic-commerce/doma-cli/wallet-modes): choose between agent (Privy) and private-key signing.
* [Commands](/agentic-commerce/doma-cli/commands): full per-command reference with examples.


# Wallet Modes

The Doma CLI signs writes in one of two modes. The mode you pick determines **what holds the signing authority**, **what scope it has**, and **how revocable it is**.

## The two modes

| Mode                      | Signs via                                     | Setup                               | Spend authority                           |
| ------------------------- | --------------------------------------------- | ----------------------------------- | ----------------------------------------- |
| `agent` (Privy delegated) | Doma launchpad → Privy delegated signer       | `doma auth login` (browser consent) | USD-capped, scoped to Doma chains         |
| `private-key` (default)   | Local raw key in `~/.doma/config.json` or env | `export DOMA_PRIVATE_KEY=0x…`       | Unbounded, limited only by wallet balance |

Both modes are first-class. The transactional flow is byte-identical; only the authentication setup and revocation story differ.

## Which mode should I pick?

Find your situation in the table:

| Situation                                               | Pick          |
| ------------------------------------------------------- | ------------- |
| CI, cron, or any non-interactive run                    | `private-key` |
| Need a USD spend cap or infra-enforced chain allowlist  | `agent`       |
| Personal dev box, want hands-off browser consent        | `agent`       |
| Personal dev box, prefer a dedicated test wallet        | `private-key` |
| Production server with a secrets manager                | `private-key` |
| Already use Doma in your browser, want one-command auth | `agent`       |

When in doubt: **start with `private-key` on testnet**, switch to `agent` for any wallet that holds value worth protecting.

## Setting up agent mode

Requires Doma CLI **≥ 0.5.0**. The CLI runtime-detects support via `doma auth --help` (older versions return non-zero).

Agent mode is supported on both **Doma Mainnet** (chain `97477`) and **Doma Testnet** (chain `97476`). The active network is whichever your config / `--testnet` flag selects; `doma auth login` mints a session for that network. To use both networks, run `doma auth login` once per network.

```bash
doma auth login
```

The CLI prints a localhost URL. Open it in your browser. You'll see a Privy-hosted consent screen. Review the wallet being authorized, the allowed chains and RPC methods, and the spend ceiling (default `$200 USD`), then confirm. Once approved, the CLI prints `Authorized.` and writes a session JWT to `~/.doma/credentials.json` (mode `0600`).

Persist the choice so future commands use agent mode by default:

```bash
doma config set walletMode agent
```

{% hint style="info" %}
The browser consent screen is the **actual authorization moment**. The CLI does not prompt again. If you change your mind, click **Cancel** in the browser and the session is never minted.
{% endhint %}

## Setting up private-key mode

Set the key as an environment variable in your shell:

```bash
export DOMA_PRIVATE_KEY=0x<64-hex-chars>
```

Or persist it via the CLI (macOS uses Keychain by default, see [Install & Configure](/agentic-commerce/doma-cli#macos-keychain)):

```bash
doma config set privateKey 0x<64-hex-chars>
```

Then mark the mode explicitly (this is the default, but explicit beats implicit):

```bash
doma config set walletMode private-key
```

{% hint style="warning" %}
Use a dedicated wallet for the CLI. The key on disk is fully empowered: anything that can read the file can drain the wallet. Don't reuse a personal hardware-wallet-derived key here.
{% endhint %}

## Switching modes

```bash
doma config set walletMode agent          # switch to agent
doma config set walletMode private-key    # switch back
```

The CLI uses whichever mode is active at command time. Read commands (`token`, `quote`, `balance`) work in either mode without auth.

## Revoking an agent session

```bash
doma auth revoke
```

This calls Privy to detach the agent's authorization key from your wallet, deletes `~/.doma/credentials.json`, and clears `walletMode`. Future writes require a fresh `doma auth login`.

You can also revoke from the launchpad's "Authorized agents" panel at any time. Useful if your machine is lost.

## Checking session status

```bash
doma auth status
```

Output:

```
Mode:        agent
Wallet:      0xabc…123
Session:     active
Expires:     <timestamp>
Spend cap:   $200 (used: $7.32)
```

## Trade-offs at a glance

|                               | `agent`                      | `private-key`          |
| ----------------------------- | ---------------------------- | ---------------------- |
| Key on disk                   | No (Privy HSM)               | Yes                    |
| Spend cap                     | Hard-enforced ($200 default) | None                   |
| Chain allowlist               | Doma chains only             | All chains             |
| Revoke from another machine   | Yes (launchpad UI)           | Need to rotate the key |
| Works in CI / non-interactive | No (needs browser consent)   | Yes                    |
| Available since               | CLI 0.5.0                    | All versions           |

## What's next

* [Commands](/agentic-commerce/doma-cli/commands): full reference of what the CLI can do.
* [Agentic Wallet](/agentic-commerce/agentic-wallet): deeper architectural look at the Privy delegated signer.


# Commands

Per-command reference. All commands accept `--quiet --format json` for clean machine-readable output (no color, no spinner, no narration on stdout). Errors still go to stderr.

## Global flags

| Flag                   | Purpose                                                                |
| ---------------------- | ---------------------------------------------------------------------- |
| `--testnet`            | Run against Doma Testnet (chain `97476`) instead of mainnet (`97477`). |
| `-q`, `--quiet`        | Suppress decorative output (spinners, banners).                        |
| `-f`, `--format <fmt>` | Output format: `pretty` (default), `json`, `csv`.                      |
| `-y`, `--yes`          | Skip confirmation prompts (use with care in agent contexts).           |

## `token`

Fetch detailed token information.

```bash
doma token <token-name>
```

Returns: price, 24h change, volume, trading venue (`Launchpad` / `UniswapV3` / `BoughtOut`), fees, liquidity, status (`FRACTIONALIZED`, `GRADUATION_SUCCESSFUL`, `GRADUATION_FAILED`, `BOUGHT_OUT`), `fillPercent`, `graduatedAt`.

## `balance`

Show wallet balances. Defaults to ETH + USDC if no specific token.

```bash
doma balance              # ETH + USDC
doma balance USDC         # specific token
doma balance <token-name> # named token
```

## `quote`

Get a swap quote without executing.

```bash
doma quote <tokenIn> <tokenOut> <amount>
```

Returns the route the CLI will use (Launchpad bonding curve, Uniswap V3, or multi-step), the expected amount-out, price impact, slippage, and gas estimate.

## `swap`

Execute a token swap. Auto-routes via Launchpad bonding curve, Uniswap V3, or a multi-step path.

```bash
doma swap <tokenIn> <tokenOut> <amount>
```

| Option                   | Default       | Notes                               |
| ------------------------ | ------------- | ----------------------------------- |
| `-s`, `--slippage <bps>` | `50` (= 0.5%) | Slippage tolerance in basis points. |
| `-y`, `--yes`            | off           | Skip the confirmation prompt.       |

## `marketplace`

Subcommands for the Seaport-based domain marketplace.

```bash
doma marketplace get <domain>          # active listing for a domain
doma marketplace buy <domain>          # fill a listing (fulfillOrder)
doma marketplace listing <domain> ...  # create an offchain listing
doma marketplace offer <domain> ...    # create an offchain offer
doma marketplace accept <orderId>      # accept an offer
doma marketplace cancel <orderId>      # cancel a listing or offer
```

`doma marketplace get` is read-only and works in either wallet mode without authentication. The other subcommands require a configured wallet.

The `cancel` subcommand has a `--type` flag (`on-chain` vs `off-chain`); off-chain is the default and is faster but uses `eth_signTypedData_v4`. **In agent mode, only `--type on-chain` is supported today.** See the [coverage matrix](#coverage-matrix-what-works-in-agent-mode) below for the full list of agent-mode constraints.

## `domain`

Read domain ownership and registration data.

```bash
doma domain <domain-name>
```

Returns owner address, expiry, registrar, claim status.

## `dns`

Manage DNS records on the Doma chain (sponsored gas).

```bash
doma dns list <domain>
doma dns set <domain> <type> <name> <value> [--ttl 3600]
doma dns delete <domain> <type> <name>
```

## `subdomain`

Claim or release a staked subdomain on the Doma chain.

```bash
doma subdomain claim <subdomain>
doma subdomain unstake <subdomain>
```

## `nameservers`

Read or update the authoritative nameservers for a tokenized domain.

```bash
doma nameservers get <domain>
doma nameservers set <domain> <ns1> <ns2> ...
```

## `bridge`

Move a name token across chains (uses Relay).

```bash
doma bridge <domain> --to <chainId>
```

## `auth`

Agent-mode session management. See [Wallet Modes](/agentic-commerce/doma-cli/wallet-modes).

```bash
doma auth login
doma auth status
doma auth revoke
```

## `config`

Read and write `~/.doma/config.json`.

```bash
doma config get <key>
doma config set <key> <value>
doma config list
```

## Coverage matrix: what works in agent mode

| Command                                  | Underlying RPC method                        | Agent mode | Notes                                      |
| ---------------------------------------- | -------------------------------------------- | ---------- | ------------------------------------------ |
| `marketplace buy`                        | `eth_sendTransaction` (Seaport.fulfillOrder) | ✅          |                                            |
| `marketplace accept`                     | `eth_sendTransaction`                        | ✅          | Same path as buy.                          |
| `marketplace cancel --type on-chain`     | `eth_sendTransaction`                        | ✅          |                                            |
| `swap` (Launchpad)                       | `eth_sendTransaction` × 2                    | ✅          |                                            |
| `swap` (Uniswap V3)                      | `eth_sendTransaction` × 3                    | ✅          |                                            |
| `swap` (multi-step)                      | `eth_sendTransaction` × 4–5                  | ✅          |                                            |
| `bridge` (Relay)                         | `eth_sendTransaction` × N                    | ✅          |                                            |
| `dns set` / `dns delete`                 | `eth_sendTransaction` (sponsored)            | ✅          |                                            |
| `subdomain claim` / `unstake`            | `eth_sendTransaction` (sponsored)            | ✅          |                                            |
| `nameservers set`                        | `eth_sendTransaction` (sponsored)            | ✅          |                                            |
| `marketplace offer` (gasless)            | `eth_signTypedData_v4`                       | ❌          | Off-chain signature limitation, see below. |
| `marketplace listing` (gasless)          | `eth_signTypedData_v4`                       | ❌          | Same.                                      |
| `marketplace cancel` (default off-chain) | `eth_signTypedData_v4`                       | ❌          | Same.                                      |

**10 of the 13 listed operations** work in agent mode today.

The 3 gasless paths use `eth_signTypedData_v4`. The agent's authorization key (not the wallet's underlying key) would sign. Off-chain protocols like Seaport reject this because the signing address ≠ the wallet address. The fix is a smart-wallet migration with [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271); see [Known limitation on the Agentic Wallet page](/agentic-commerce/agentic-wallet#known-limitation).

## What's next

* [Wallet Modes](/agentic-commerce/doma-cli/wallet-modes): pick the right authentication path.
* [Agentic Wallet](/agentic-commerce/agentic-wallet): architectural model behind agent mode.
* [Skills](/agentic-commerce/skills): see how published skills compose these commands.


# Agentic Wallet

The agentic wallet is the architecture behind agent mode in the Doma CLI. It lets an AI agent execute on-chain actions on the user's behalf (within a hard policy) without ever holding the wallet's private key.

This page describes the model. For the user-facing CLI flow, see [Wallet Modes](/agentic-commerce/doma-cli/wallet-modes).

## What it is

Doma's launchpad uses **Privy embedded wallets**. Each user has an EVM wallet whose private key is generated and held inside Privy's HSM. Neither the user nor Doma ever see it. The wallet is available across both **Doma Mainnet** (chain `97477`) and **Doma Testnet** (chain `97476`); agent mode works on either.

For agent commerce, we attach a **delegated agent signer** to the wallet, governed by a per-wallet **policy**. The agent never holds the user's key, and Privy refuses any signature request that falls outside the policy.

## Authority model

<figure><img src="/files/ssjMxcEibOD9NCfTgLuT" alt="Delegated-signer authority. The user wallet (EOA, key in Privy HSM) has the user&#x27;s Privy DID as owner and an additional signer: the agent authorization key, server-side in launchpad infra, bound to the policy doma-agentic-wallet-v1, which allows eth_sendTransaction on Doma mainnet (97477) and testnet (97476), and eth_signTypedData_v4 on Doma testnet (97476) only."><figcaption></figcaption></figure>

The policy `doma-agentic-wallet-v1` is what makes the agent signer safe. Where `<Doma chain IDs>` in the diagram resolves to `97477` (mainnet) and `97476` (testnet), and `<Doma testnet>` is `97476` alone. Typed-data signatures are scoped to testnet today (see [Known limitation](#known-limitation)).

* The **wallet's own private key** stays in Privy's HSM. It signs every transaction (so on-chain validators see `from = wallet_address`).
* The **agent authorization key** lives server-side in Doma's launchpad infra. It does not sign transactions itself; it tells Privy "this user authorized me, please sign on their behalf."
* The **policy** is enforced by Privy. Anything the agent requests outside the policy is rejected before signing.

## Policy fields

| Field               | What it controls                                        | Example                                       |
| ------------------- | ------------------------------------------------------- | --------------------------------------------- |
| `chain_allowlist`   | Which chain IDs the agent can target                    | Doma mainnet, Doma testnet, Base              |
| `method_allowlist`  | Which RPC methods the agent can call                    | `eth_sendTransaction`, `eth_signTypedData_v4` |
| `usd_spend_ceiling` | Cumulative USD spend before the agent must re-authorize | `$200`                                        |
| `allowance_window`  | Refill window for the spend ceiling                     | rolling 24h                                   |
| `revocable_by_user` | User can detach the signer at any time                  | `true`                                        |

The defaults above describe Doma's `doma-agentic-wallet-v1` policy. Future versions may tighten or relax fields.

## Session lifecycle

{% stepper %}
{% step %}

#### Login

User runs `doma auth login`. The CLI opens a localhost callback URL in the user's browser, which loads Doma's launchpad consent screen.

The launchpad calls Privy's `wallets.update()` to **attach the agent authorization key** as an additional signer with the `doma-agentic-wallet-v1` policy, then mints a short-lived **session JWT** keyed to a fresh allowance row in KV (default `$200` budget).

The CLI persists the JWT to `~/.doma/credentials.json` (mode `0600`).

Sessions are **network-scoped.** To authorize agent mode on both Doma Mainnet and Doma Testnet, run `doma auth login` once per network (toggle the network with the `--testnet` flag or `doma config set testnet <bool>` between runs).
{% endstep %}

{% step %}

#### Use

Every CLI write command POSTs the session JWT plus the intended transaction payload to the launchpad's `/api/agent/execute` endpoint. The launchpad:

1. Validates the JWT.
2. Decodes the calldata, computes the USD value of the transaction (using oracle prices).
3. Checks the policy: chain allowed? method allowed? cumulative spend within ceiling?
4. If all checks pass, instructs Privy to sign on behalf of the user.
5. Submits the signed transaction to the chain RPC.
6. Decrements the allowance row by the USD value.

If any check fails, the launchpad returns a structured error and the CLI surfaces it cleanly to the user.
{% endstep %}

{% step %}

#### Revoke

User runs `doma auth revoke`. The launchpad calls Privy's `removeSigners` to detach the agent authorization key from the user's wallet, deletes the allowance row, invalidates the session JWT, and clears `~/.doma/credentials.json`.

The user can also revoke from the launchpad's "Authorized agents" panel. Useful if the CLI machine is lost or compromised.
{% endstep %}
{% endstepper %}

## The three signing paths

A given Doma CLI action falls into one of three patterns based on what kind of signature it needs:

| Action                                                            | RPC method             | Signed by                                   | Status                            |
| ----------------------------------------------------------------- | ---------------------- | ------------------------------------------- | --------------------------------- |
| `marketplace buy`, `swap`, `bridge`, `dns set`, `subdomain claim` | `eth_sendTransaction`  | wallet's own key (via Privy)                | ✅ working                         |
| `marketplace cancel --type on-chain`                              | `eth_sendTransaction`  | wallet's own key                            | ✅ working                         |
| `marketplace offer`, `listing`, off-chain `cancel`                | `eth_signTypedData_v4` | agent authorization key, **not** the wallet | ⚠️ off-chain signature limitation |

The first two rows are "transactions": they're submitted on-chain and validators only care about `from = wallet_address`, which is true.

The third row is "off-chain typed data" used by gasless protocols like Seaport. The signing address must equal the wallet address. Today Privy uses the agent authorization key for these, so the signing address ≠ the wallet address, and Seaport rejects the signature.

## Known limitation

Gasless typed-data flows (`marketplace offer`, `listing`, off-chain `cancel`) don't work in agent mode today. **Workaround:** use `private-key` mode for those specific actions.

The path forward is a **smart-wallet migration**: instead of the user's EOA, the wallet becomes a smart contract that validates signatures via on-chain logic ([ERC-1271](https://eips.ethereum.org/EIPS/eip-1271)). The contract can be programmed to accept signatures from the agent authorization key as if they were the wallet's own. This eliminates the off-chain asymmetry entirely.

Tracked separately; not in scope for V1 of agent mode.

## Security posture

{% hint style="warning" %}
Agent mode is a **guardrail**, not a vault. The policy bounds blast radius, but a compromise of the launchpad's agent authorization key or the user's session JWT still allows actions within the policy. Use a dedicated wallet for agent mode and keep the spend ceiling tight.
{% endhint %}

* The launchpad's agent authorization key is rotated on a schedule and stored in HSM-backed cloud KMS. Compromise requires breaching both Privy and the launchpad infra.
* Session JWTs are short-lived and tied to a specific wallet + allowance row. Theft of a JWT bounds the attacker by the remaining ceiling.
* The user's wallet key remains in Privy's HSM at all times. Even an attacker with full launchpad access cannot exfiltrate it.

## What's next

* [Wallet Modes](/agentic-commerce/doma-cli/wallet-modes): user-facing flow for `agent` vs `private-key`.
* [Commands → coverage matrix](/agentic-commerce/doma-cli/commands#coverage-matrix-what-works-in-agent-mode): per-command status of agent-mode support.
* [ERC-1271 specification](https://eips.ethereum.org/EIPS/eip-1271): the migration target for gasless flows.


# Skills

A **Doma skill** is a markdown file (`SKILL.md`) plus optional supporting files (`examples/`, `reference/`) hosted at a public URL. Once installed in an AI agent, the skill becomes the agent's instruction manual for a specific kind of task, buying domains, swapping tokens, paying for a registration.

Skills are **portable**: the same `SKILL.md` works in Claude Code, Cursor, Codex CLI, Gemini, OpenCode. They are **stateless on the publisher side**: once installed, the skill runs locally in the agent and only calls back to public APIs and CLIs.

## The `SKILL.md` format

A `SKILL.md` is YAML frontmatter followed by Markdown body. The frontmatter declares metadata; the body contains the workflow logic the agent follows.

```markdown
---
name: my-skill
description: >
  One-line summary of what this skill does. Trigger on: phrase one,
  phrase two, phrase three.
license: MIT
metadata:
  author: <publisher>
  version: "1.0.0"
compatibility: Requires Node.js 18+ and the foo CLI.
allowed-tools: Bash(foo *), Bash(npx -y foo *)
argument-hint: [domain-name]
---

# My Skill

Body markdown describing the workflow the agent follows when triggered.

## Rules
1. Always ask before spending.
2. ...

## Flow
### 1. Setup
...
```

| Frontmatter field  | Purpose                                                                              |
| ------------------ | ------------------------------------------------------------------------------------ |
| `name`             | Canonical skill ID. Must match the install path slug.                                |
| `description`      | One-line summary plus the **trigger phrases** the agent matches against user intent. |
| `license`          | SPDX license identifier (MIT recommended for public skills).                         |
| `metadata.author`  | Publisher name.                                                                      |
| `metadata.version` | SemVer string.                                                                       |
| `compatibility`    | Required tooling on the user's machine.                                              |
| `allowed-tools`    | Subset of agent tools the skill is permitted to invoke.                              |
| `argument-hint`    | Slot template for arguments the user may pass.                                       |

## Trigger phrases

The `description` field is how the agent decides to load a skill for a given user prompt. Phrases like "buy tokens", "swap on Uniswap", "make an offer" are matched against installed skills' descriptions; the best match wins.

For Doma's published skills, the trigger phrases are documented on each skill's reference page below.

## Currently published skills

| Skill                                                       | What it does                                                                          | Manifest URL                                                   |
| ----------------------------------------------------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| [Secondary Sales](/agentic-commerce/skills/secondary-sales) | Buy listed domains via Doma's marketplace (Seaport NFT)                               | `https://doma.xyz/.well-known/skills/secondary-sales/SKILL.md` |
| [Trade Tokens](/agentic-commerce/skills/trade-tokens)       | Buy / sell fractional domain tokens (Launchpad bonding curve, Uniswap V3, sellOnFail) | `https://doma.xyz/.well-known/skills/trade-tokens/SKILL.md`    |
| [MPP](/agentic-commerce/skills/mpp)                         | Pay HTTP 402 endpoints for domain registration via the Machine Payments Protocol      | `https://doma.xyz/.agents/skills/doma-mpp/SKILL.md`            |

## Adding your own skill

If you publish your own AgentRoot zone, you can include a `skill` record pointing to your own `SKILL.md`. See [Publish Your Own](/agentic-commerce/agentroot/publish-your-own#optional-host-skillmd-files-alongside-your-zone).

## What's next

* [Secondary Sales](/agentic-commerce/skills/secondary-sales): buy domains.
* [Trade Tokens](/agentic-commerce/skills/trade-tokens): swap fractional tokens.
* [MPP](/agentic-commerce/skills/mpp): payment-gated registration.
* [AgentRoot → Publish Your Own](/agentic-commerce/agentroot/publish-your-own): author and publish your own skill.


# Secondary Sales

The `secondary-sales` skill lets an AI agent buy listed domains on Doma's marketplace, accept offers, create listings or offers, and cancel orders. All flows are powered by [Seaport](https://github.com/ProjectOpenSea/seaport) NFT primitives.

## At a glance

|                 |                                                                                    |
| --------------- | ---------------------------------------------------------------------------------- |
| Manifest URL    | `https://doma.xyz/.well-known/skills/secondary-sales/SKILL.md`                     |
| Backing CLI     | `@doma-protocol/cli` (`doma marketplace …`)                                        |
| Wallet modes    | `agent` (Privy) and `private-key`                                                  |
| Trigger phrases | `buy domain`, `purchase domain`, `secondary sale`, `make offer`, `Doma`, `Seaport` |

## What it does

The skill encodes a complete buy / sell workflow:

1. Asks the user which network (mainnet / testnet).
2. Verifies prerequisites (CLI installed, wallet configured, sufficient funds + gas).
3. Fetches the active listing for the requested domain.
4. Surfaces price, currency, and expiry to the user for explicit confirmation.
5. Executes via `doma marketplace buy` (or `accept`, `cancel`, `offer`, `listing`).
6. Verifies ownership transfer and reports back.

## Prerequisites

* Node.js ≥ 20
* `@doma-protocol/cli` installed (`npm install -g @doma-protocol/cli`)
* A configured wallet (see [Wallet Modes](/agentic-commerce/doma-cli/wallet-modes))
* Sufficient balance in the listing's currency, plus gas

## Wallet mode notes

|                                          | `agent` | `private-key` |
| ---------------------------------------- | ------- | ------------- |
| `marketplace buy`                        | ✅       | ✅             |
| `marketplace accept`                     | ✅       | ✅             |
| `marketplace cancel --type on-chain`     | ✅       | ✅             |
| `marketplace offer` (gasless)            | ❌ today | ✅             |
| `marketplace listing` (gasless)          | ❌ today | ✅             |
| `marketplace cancel` (default off-chain) | ❌ today | ✅             |

The gasless paths use `eth_signTypedData_v4` and don't yet work in agent mode. See [Agentic Wallet → Known limitation](/agentic-commerce/agentic-wallet#known-limitation).

## Example prompt

After installing the skill (see [Discover & Use Skills](/agentic-commerce/agentroot/discover-and-use#install-a-skill)):

> "Buy `<your-domain>.fyi` on Doma using the listing currency."

The agent walks through:

1. Network selection.
2. Listing fetch (`doma marketplace get <your-domain>.fyi --format json`).
3. Confirmation: "List price is `5.00 USDTEST` (Doma testnet stablecoin), expires `<date>`. Buy now?"
4. Execution (`doma marketplace buy <your-domain>.fyi --yes --format json`).
5. Ownership verification (`doma domain <your-domain>.fyi`).

## Supported scenarios

| Scenario                            | Subcommand                           | Agent mode |
| ----------------------------------- | ------------------------------------ | ---------- |
| Fill an active listing              | `marketplace buy`                    | ✅          |
| Accept an offer made on your domain | `marketplace accept`                 | ✅          |
| Cancel a listing on-chain           | `marketplace cancel --type on-chain` | ✅          |
| Create a listing (gasless)          | `marketplace listing`                | ❌ today    |
| Make an offer on a domain (gasless) | `marketplace offer`                  | ❌ today    |
| Cancel a listing or offer off-chain | `marketplace cancel`                 | ❌ today    |

## Cross-references

* [Doma Marketplace](/doma-marketplace): protocol-level fees, currencies, and Seaport integration details.
* [Orderbook API](/api-reference/orderbook-api): direct REST API the CLI calls under the hood.
* [Commands → marketplace](/agentic-commerce/doma-cli/commands#marketplace): full command reference.


# Trade Tokens

The `trade-tokens` skill lets an AI agent buy or sell **fractionalized domain tokens** across every venue Doma supports: bonding-curve launchpad, post-graduation Uniswap V3, and `sellOnFail` for failed launches. The skill auto-routes based on the token's on-chain state.

## At a glance

|                 |                                                                                                                                                                                  |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Manifest URL    | `https://doma.xyz/.well-known/skills/trade-tokens/SKILL.md`                                                                                                                      |
| Backing CLI     | `@doma-protocol/cli` (`doma swap …`, `doma token …`, `doma quote …`)                                                                                                             |
| Wallet modes    | `agent` (Privy) and `private-key`                                                                                                                                                |
| Trigger phrases | `buy tokens`, `sell tokens`, `swap tokens`, `trade fractional`, `purchase fractional`, `bonding curve`, `sellOnFail`, `graduated`, `Uniswap`, `post-graduation`, `failed launch` |

## What it does

`doma swap` already auto-detects the right venue. The skill wraps it with a discover → preview → confirm → execute flow:

1. Read the token's on-chain `status` and `tradingVenue` via `doma token --quiet --format json`.
2. Pick the correct venue (Launchpad / Uniswap / sellOnFail) based on the state matrix below.
3. Fetch a quote (`doma quote`) and surface amount-out, price impact, and gas to the user.
4. Confirm before executing.
5. Execute (`doma swap --yes`) and report the on-chain result.

## State × action matrix

<figure><img src="/files/emyTA3ASpl2kSX2ubNMP" alt="Trade-tokens routing state machine. FRACTIONALIZED with fillPercent under 100% trades on the Launchpad bonding curve (buy and sell). When fillPercent reaches 100%, the token enters FRACTIONALIZED at 100% and trades pause pending graduation. Graduation either succeeds (transition to GRADUATION_SUCCESSFUL on Uniswap V3) or fails (transition to GRADUATION_FAILED with sellOnFail fixed-rate refunds only). After redemption, GRADUATION_SUCCESSFUL transitions to BOUGHT_OUT, a terminal state with no further trades."><figcaption></figcaption></figure>

This is the routing logic the skill encodes. **Always check `status` first.** A `BOUGHT_OUT` token can have `graduatedAt` set, which would otherwise mislead the agent.

| `status`                | `tradingVenue` | `fillPercent` | Buy                        | Sell                             |
| ----------------------- | -------------- | ------------- | -------------------------- | -------------------------------- |
| `BOUGHT_OUT`            | any            | n/a           | refuse, already redeemed   | refuse, already redeemed         |
| `FRACTIONALIZED`        | `Launchpad`    | < 100%        | bonding-curve buy          | bonding-curve sell               |
| `FRACTIONALIZED`        | `Launchpad`    | = 100%        | refuse, graduation pending | refuse, graduation pending       |
| `GRADUATION_FAILED`     | `Launchpad`    | n/a           | refuse, launch failed      | `sellOnFail` (fixed-rate refund) |
| `GRADUATION_SUCCESSFUL` | `UniswapV3`    | n/a           | Uniswap V3 buy             | Uniswap V3 sell                  |

## Prerequisites

* Node.js ≥ 20
* `@doma-protocol/cli` ≥ **0.4.0** for read-only commands; ≥ **0.5.0** for `agent` mode
* A configured wallet (see [Wallet Modes](/agentic-commerce/doma-cli/wallet-modes))
* Sufficient input-token balance plus gas

## Example prompt

After installing the skill (see [Discover & Use Skills](/agentic-commerce/agentroot/discover-and-use#install-a-skill)):

> "Buy 10 USDC worth of `<token-name>` token on Doma testnet."

(Mainnet works the same way: drop `testnet` from the prompt and the skill targets Doma Mainnet via your CLI config.)

The agent walks through:

1. Network selection (testnet, since the prompt asked for it).
2. State read (`doma token <token-name> --testnet --quiet --format json`) → for example `status: GRADUATION_SUCCESSFUL`, `tradingVenue: UniswapV3`.
3. Quote (`doma quote USDC <token-name> 10`) → expected amount-out, \~0.31% price impact, \~$0.16 gas, 0.5% slippage default.
4. Confirmation prompt with all numbers.
5. Execution (`doma swap USDC <token-name> 10 --yes --quiet --slippage 50`).
6. Report `Status: Success`, tx hash, block, gas used, and final received amount.

## Cross-references

* [Doma Fractionalization](/api-reference/doma-fractionalization): the on-chain primitives the skill operates against.
* Underlying CLI commands: [Commands → swap](/agentic-commerce/doma-cli/commands#swap) and [Commands → token](/agentic-commerce/doma-cli/commands#token).
* [Wallet Modes](/agentic-commerce/doma-cli/wallet-modes): set up agent mode for hands-off swaps.


# MPP

The `doma-mpp` skill teaches an AI agent how to call **HTTP 402 payment-gated APIs** for domain registration, using the [Machine Payments Protocol](https://github.com/tempoxyz/mpp-specs) and the `mppx` client library.

## At a glance

|                       |                                                                                             |
| --------------------- | ------------------------------------------------------------------------------------------- |
| Manifest URL          | `https://doma.xyz/.agents/skills/doma-mpp/SKILL.md`                                         |
| Backing protocol      | [Machine Payments Protocol](https://github.com/tempoxyz/mpp-specs) (HTTP 402)               |
| Backing CLI / library | `mppx` client                                                                               |
| Trigger phrases       | `MPP`, `paid API`, `payment-gated`, `register domain via MPP`, `HTTP 402`, `Doma /register` |

## What MPP is (briefly)

MPP is a protocol for **micropayments to APIs**. A service exposes endpoints that respond with HTTP `402 Payment Required` until the caller attaches a valid payment receipt. The receipt is computed against a payment endpoint advertised in the response headers.

Doma exposes one such endpoint at `https://mpp.doma.xyz/register` for tokenized domain registration. The skill wraps the HTTP 402 dance: detect the response, request a quote, generate a payment, attach the receipt, retry.

## What the skill does

1. Resolves the MPP endpoint and its supported payment options (currencies, chains).
2. Asks the user which option to use (e.g. `USDC` on `Base`).
3. Computes the price for the requested registration.
4. Confirms with the user before paying.
5. Submits the payment, receives a receipt.
6. Retries the registration request with the receipt attached.
7. Reports the registration result.

## Prerequisites

* Node.js ≥ 20
* `@doma-protocol/cli` for ancillary domain operations (optional)
* `mppx` client (the skill installs / invokes it as needed)
* A wallet with a balance in one of the supported payment currencies on a supported chain

## Example prompt

After installing the skill:

> "Register `<your-domain>.io` via MPP on Base using USDC."

The agent:

1. Hits `https://mpp.doma.xyz/register` to discover supported payment options.
2. Confirms the price and currency with you.
3. Submits the payment via `mppx`.
4. Retries the registration with the receipt and reports success / failure.

## Cross-references

* [Doma Marketplace](/doma-marketplace): alternative path for buying already-tokenized domains.
* [Building on Doma](/building-on-doma): broader integration overview.
* [MPP specification](https://github.com/tempoxyz/mpp-specs): protocol-level details.

{% hint style="info" %}
The MPP skill is served at `.agents/skills/` (not `.well-known/skills/` like Doma's other skills) because it predates the standard AgentRoot location. It ships as a git submodule under Doma's published skills tree at `apps/doma-xyz/public/.agents/skills/doma-mpp/`.
{% endhint %}


# FAQ

### Table of Contents

* [Interstellar](#testnet-d3)
* [Doma and Differences with Interstellar](#doma-and-differences-with-d3)
* [Doma Testnet](#doma-testnet)
* [Buying, Selling, Bridging and Multi-Chain](#buying-selling-bridging-and-multi-chain)
* [Support](#support)

## Doma FAQ

Welcome to the Doma FAQ! This guide will help you understand how Interstellar, Doma, and the Doma Protocol work together to power domain tokenization, management, and trading in Web3.

Interstellar is where you buy or reserve domains (including Name Tokens for future TLDs like \*ape, \*shib, or \*vic). You can then tokenize these domains to unlock Web3 capabilities.

Doma is where you manage your tokenized domains across registrars — you can list them, place and accept offers, bridge them across chains, detokenize them, and monitor ownership status. Doma acts as your domain wallet and portfolio hub.

Doma Protocol is the blockchain infrastructure that enables ICANN accredited Registrars to securely tokenize their domains onto the blockchain, turning them into dynamic programmable assets in web3 dApps.

This FAQ is designed for developers, domainers, and early partners exploring the future of tokenized domains. If you have questions beyond this FAQ, please reach out to `support@doma.xyz` or join our [Discord](https://discord.gg/doma).

### Interstellar

#### What is Interstellar.xyz?

[Interstellar.xyz](https://interstellar.xyz/) is an ICANN accredited Registrar created to prove out the Doma Protocol. You can think of it as a reference implementation of a web3-forward Registrar where you can experiment with tokenizing domains. It is designed for early users and developers to test tokenization flows, provide feedback, and contribute to building the DomainFi ecosystem.

#### What is a Name Token on Interstellar?

Name Tokens (denoted by `*` instead of `.`) are web3 names that you can reserve for future top-level domains (TLDs) that have not yet launched — for example, \*ape, \*shib, or \*vic. These TLDs are expected to go live in Summer 2026, pending ICANN approval. When the registry for these TLDs gets approved, folks who have registered a Name Token will get first dibs on claiming those domains.

#### What happens to my domain after registration/transfer to Interstellar?

Domains that are registered or transferred to Interstellar are automatically tokenized onto the blockchain (on a supported chain of your choice). Once tokenized, the domain is automatically made available in Doma, a separate platform built for managing, claiming, bridging, and listing tokenized domains across the ecosystem. Doma acts as an aggregator, showing tokenized domains from any Registrar that supports Doma Protocol.

#### Why does Interstellar exist?

The Interstellar registrar was created as a way to "dogfood" Doma Protocol and get a head start on onboarding real registrars by demonstrating how domain tokenization can work. It is a working playground, but one with a purpose: to make sure the experience is smooth before integration with other Doma partners.

#### Who is Interstellar for?

Right now, Interstellar is focused on:

* Developers building on Doma Protocol
* Domainers who want to explore tokenization
* Partners who want to see a working integration before going live

Interstellar also has a free [Testnet environment](https://testnet.interstellar.xyz/), so it’s perfect for experimentation.

### Doma and Differences with Interstellar

#### What is Doma?

[Doma](https://doma.xyz) is your universal domain management hub designed specifically for tokenized domains across various registrars. You cannot directly buy or register new domains on Doma. Instead, it serves as a portfolio manager and Web3 domain wallet, allowing you to list domains for sale, place offers, accept offers, claim ownership, bridge domains to other chains, detokenize them, and track their activity and status in one place.

#### How are domains tokenized on Doma?

Doma receives tokenized domains from registrars that integrate with the Doma Protocol — for example, registrars like Interstellar, Encirca, NicNames, and others. Once a domain is tokenized with any of these registrars, it automatically appears on Doma and becomes available for further actions, such as listing on a marketplace or claiming it into your wallet.

#### What is the difference between Interstellar and Doma?

Think of Interstellar as the platform where you can buy or reserve a domain name — especially for domains launching as Name Tokens with future TLD applications targeted around Summer 2026. After you tokenize the domain through Interstellar, you can then manage it on Doma. That means you can list it for sale, bridge it across chains, claim it, or monitor its analytics and ownership status from within Doma.

#### How does multi-wallet and multi-chain support work?

Doma allows you to link multiple wallets to a single email address, making it simple to manage your entire domain portfolio from one unified account. With this feature, you can control domains across supported blockchains, including Doma Chain, Base, Avalanche, and soon Solana. The same Doma account gives you seamless oversight of your domains no matter which wallet or chain they reside on.

#### What if I lose my wallet or change my email?

If you ever lose access to your wallet, you can reconnect a new one after verifying ownership of your linked email address, which ensures the security of your domain portfolio. Similarly, if you change your email address, you will be prompted to verify the new email before regaining access to manage your domains.

#### Do I pay gas fees?

Yes, when operating on Mainnet, you will be responsible for covering standard blockchain gas fees, just like with most decentralized services. If you bridge funds over from our bridging partner [Stargate.finance](https://stargate.finance/?srcChain=ethereum\&srcToken=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\&dstChain=doma\&dstToken=0x31EEf89D5215C305304a2fA5376a1f1b6C5dc477), you will receive a small deposit of ETH on Doma Chain to perform your first few transactions. On Testnet, however, you can use faucet tokens to execute transactions without spending real crypto.

#### Is Doma secure?

Doma is built with a strong focus on security. No one can list, transfer, or bridge your domain without you signing the relevant transaction through your connected wallet. In addition, Doma follows robust compliance frameworks and uses secure wallet-based authentication to protect your assets.

#### How is the registrar notified after a claim?

When you claim a domain on Doma, the platform automatically communicates with the registrar, for example, Interstellar, to update their records, ensuring you are listed as the official owner of the domain. This process guarantees compliance with ICANN rules and helps maintain accuracy in the registrar’s ownership records.

#### Who owns my data on Doma?

You are the owner of your domain data. Doma and the integrated registrars only store data that is strictly necessary for compliance, domain transfers, and user support. This information is never sold to third parties, preserving your data privacy.

#### Which chains does Doma prioritize?

Currently, Doma fully supports Doma Chain, Base and Avalanche. Solana is prioritized next for bridging and future integrations. Beyond that, other EVM-compatible chains could be added over time as Doma continues to expand its ecosystem.

### Doma Testnet

#### How can I access the Testnet environment?

You can access the Testnet environment by either registering a domain on [Interstellar Testnet](https://testnet.interstellar.xyz/) or managing your tokenized domains on [Doma Testnet](https://dashboard-testnet.doma.xyz/). Any domains you buy, tokenize, bridge, or claim in the Testnet environment are purely for testing and learning purposes. These domains are not real production domains and cannot be used on the global internet. They serve as a way for users to practice and familiarize themselves with the system without spending real funds.

#### Is the money real?

No, there is no real money involved on Testnet. Instead, Doma uses test currencies, such as Test ETH or Test USD, which you can receive through faucets. These tokens are only for simulating transactions and have no actual monetary value or exchangeability.

#### Will my Testnet domains carry over to Mainnet?

No, your Testnet domains will not transfer to Mainnet. Testnet and Mainnet are completely separate environments. Think of Testnet domains as a safe playground or temporary “training wheels” to help you get comfortable with the process before handling real assets.

#### Why do I see faucets mentioned in the dashboard?

You may see references to faucets on the Doma dashboard. Faucets are tools that provide you with free Testnet tokens so you can pay for simulated gas fees during testing. These faucet tokens have zero monetary value and are strictly for use in the Testnet environment.

#### Are Name Tokens real domains?

No, Name Tokens are not real domains and only work in web3. They serve only as placeholders and do not carry any legal standing today. These names will remain in placeholder status until the related TLD is fully approved by ICANN, which is expected around Summer 2026.

#### Any legal disclaimers I should know?

Yes — it is important to understand that any domains, balances, offers, or listings on Testnet are strictly for development, quality assurance, and demonstration purposes only. There is no warranty, no transfer of actual ownership, and no financial settlement tied to Testnet assets. You should always treat Testnet activities and assets as purely experimental.

### Buying, Selling, Bridging and Multi-Chain

#### How do I buy a tokenized domain?

To buy a tokenized domain, you start by purchasing a domain through a participating registrar such as Interstellar, Encirca, or NicNames. Once the domain is tokenized, you can list it on marketplaces, including Doma, OpenSea, or Magic Eden. Other users interested in your domain can then buy it directly from these marketplaces, giving you options to sell or trade.

#### How to buy and tokenize domains on Testnet

1. **Go to testnet interstellar.xyz at** [**https://testnet.interstellar.xyz**](https://testnet.interstellar.xyz/)

* Create an account (or log in)
* Link your wallet

2. **Use the search bar to find a domain**

* Search for domains ending in: .com, .ai, .io, .xyz, .shib, .core, .ape, .vic
* At this moment, only Web2 domains (like `.com`, `.ai`, `.io`, `.xyz`) can interact with the Doma Testnet.

<figure><img src="/files/73PjWfr5uqsI8J1gMyrm" alt=""><figcaption></figcaption></figure>

3. **Buy with test credit card**

* Use a Stripe [test card numbers](https://docs.stripe.com/testing#cards) (example: 4242 4242 4242 4242)
* No real money needed

<figure><img src="/files/kER7zstsiRJZEJY1d1Fh" alt=""><figcaption></figcaption></figure>

4. **Tokenize your domain**

* After buying, choose where to tokenize:
  * Doma Testnet
  * Sepolia Testnet
  * Base Testnet
* This turns your domain into an NFT linked to your wallet

<figure><img src="/files/nuwAQEpRKWckEOBVNt16" alt=""><figcaption></figcaption></figure>

*Note: Easiest method is to tokenize directly to the Doma Testnet. This will require no bridging.*

<figure><img src="/files/slutZ23FAPUTrGHB9EvZ" alt=""><figcaption></figcaption></figure>

5. **See it on Doma Testnet**

* If you tokenized on Doma Testnet (or bridged later), view it here: [dashboard-testnet.doma.xyz](https://dashboard-testnet.doma.xyz/)

**ALTERNATIVE: Buy with Sepolia ETH**

* Mine Sepolia ETH
* [Bridge](https://bridge-testnet.doma.xyz) Sepolia ETH to Doma Testnet
* Use it to buy domains

*Tip: Use the same wallet across Interstellar and Doma so everything appears correctly.*

#### What happens after I buy a tokenized domain?

After purchasing a tokenized domain NFT on a marketplace, you will need to claim it on Doma. The claim process links your wallet to the domain and requires you to provide your email address. This allows the registrar to finalize the ownership records and ensures that the domain is properly attributed to you on-chain and in registrar systems.

#### Can I place or accept offers?

Absolutely — you can place offers or bids on tokenized domains through Doma. If you are the owner of a tokenized domain, you can also accept offers directly from your Doma dashboard. Once an offer is accepted, the transaction is confirmed and finalized on-chain, ensuring a secure and transparent transfer of ownership.

#### How do I detokenize a domain?

Detokenizing a domain means you remove the token from the blockchain, reverting the ownership record back to a traditional registrar-only state. After you detokenize, your domain cannot be listed or traded on Web3 marketplaces until you choose to re-tokenize it again. This provides flexibility if you wish to keep the domain purely within the traditional domain system.

#### What happens if my claim fails?

If a claim fails because of a wrong email, wallet mismatch, or other issues, you can always restart the claim process directly on Doma. In certain situations, the registrar may ask you to go through manual verification steps to confirm your ownership before finalizing the claim.

#### What is bridging in Doma?

Bridging in Doma allows you to move your domain token from one blockchain network to another. For example, you might bridge a domain token from the Doma chain to Base or to Solana. Bridging gives you access to different liquidity pools and marketplaces on those networks, expanding your trading and ownership opportunities.

#### How do I bridge a tokenized domain?

To bridge a tokenized domain, go to your Doma Dashboard, select the domain you wish to bridge, click the Bridge option, then choose your target chain and target wallet. After confirming the transaction, you’ll wait for the bridge to finalize. If you are on Testnet, you can use faucet tokens to pay for the necessary gas fees, making it simple to experiment before moving to Mainnet.

#### Which chains are supported for bridging?

Today, Doma supports bridging across the Doma chain, Base, and Avalanche. Solana is the next chain prioritized for full bridging support, with other EVM-compatible chains also being evaluated for future integrations.

#### Do I pay gas fees when bridging?

Yes, you do pay network gas fees when bridging your domain. On Testnet, faucet tokens are provided for free to cover these simulated gas fees.

#### Can I bridge directly after buying on OpenSea or Magic Eden?

Yes, you can bridge your domain to another supported chain at any time after you claim it on Doma. This flexibility unlocks broader liquidity and trading opportunities across supported blockchain networks, giving you more ways to manage and profit from your tokenized domains.

### Support

#### Where can I get help?

If you need technical or product assistance while using Doma, you can reach out anytime by emailing <support@doma.xyz>. The support team is ready to help you troubleshoot issues or answer questions about the platform.

#### Is there a community?

Yes, Doma has an active Discord community where you can connect with other developers, ask questions, and get real-time help. You can join the discussion and become part of the network by visiting this [Discord link](https://discord.gg/doma).

#### How do I report bugs?

If you encounter a bug, you can report it through the #support channel on Discord or by sending an email to the support team. It is helpful to include your wallet address, browser type, and any screenshots so the team can investigate more efficiently.

#### Who do I contact for partnerships?

If you are interested in registrar integrations or exploring enterprise-level collaboration opportunities, you can email <partners@doma.xyz>. The partnerships team will be happy to discuss opportunities with you.

#### Are Testnet funds real?

No, Testnet funds are not real. Testnet uses faucet-based tokens with no monetary value, so you cannot withdraw or convert them to actual funds. They are intended purely for testing and learning.

#### What if Testnet resets?

Since Testnet is a developer sandbox, all assets there are temporary and data loss is possible if the network resets. You should treat anything on Testnet as experimental and not permanent.

#### Are Name Tokens guaranteed to become Domains?

No, Name Tokens (denoted by `*` instead of `.` in the name) are not guaranteed to become real domains. These TLDs are still pending ICANN approval, with a target timeline around Summer 2026, but there is no legal guarantee until final approval is in place.

#### Who owns my data on Doma?

You own your data on Doma. Both registrars and Doma only store the data needed to support claims, transfers, and compliance. They do not sell or share user data with third parties.

#### Is my domain secure on Doma?

Yes, Doma is built with a strong focus on security. Only your connected wallet can sign transactions, ensuring you are in control of listing, bridging, and claiming your domains. In addition, Doma uses strict compliance frameworks to protect your assets.


