# NFDomains API

### Getting started

Welcome to an overview, and detailed description of the public API for [NFDomains](https://app.nf.domains).

NFDomains .algo names are controlled by the NFD Registry Contract which controls pricing, and the minting of new NFDs.  Each NFD is a standalone contract instance, with its own Algorand account (Vault) it controls.  The registry acts as a contract factory, creating the NFDs and acting as the registrar and registry of record.  Creation and resolving of names and addresses to names is via the registry.  See [On-Chain Reference](/reference/on-chain-reference) for more details.

The UI for NFDomains uses the very same public API being discussed here.

The NFDomains REST API is a public API that is for integrators and the community to use. It provides read-only methods, as well as methods that modify the NFD contract states (minting / claiming / updating / selling, etc.) which require the caller to sign returned transactions.

The *latest* full Swagger spec will always be provided by the API itself, and is available from every API endpoint (per environment):

* **mainnet**: <https://api.nf.domains/info/openapi3.yaml>
* **testnet**: <https://api.testnet.nf.domains/info/openapi3.yaml>

{% hint style="info" %}
While we don't want to break the API, there are currently no guarantees the API won't change in the future.

Significant changes will either receive new endpoints, or /v2/ path.

High level changes will be described in [API Changelog](/reference/api-changelog)
{% endhint %}

### Interactive Swagger

The specifications listed above can be loaded directly into Swagger for **viewing**, and **executing,** and **generating SDKs**.

Below are direct links, which load the specific specs into an interactive page:

* **mainnet**: <https://editor.swagger.io/?url=https://api.nf.domains/info/openapi3.yaml>
* **testnet**: <https://editor.swagger.io/?url=https://api.testnet.nf.domains/info/openapi3.yaml>

### Generating SDKs

Use the above links to go to the Swagger editor, and you can generate SDKs for 20+ languages.

![SDK Generation](https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2FWzPR3SuFPtI5nLKVmv1C%2Fimage.png?alt=media\&token=89f65f5a-c754-480b-8bd9-dff3312cfd86)

TxnLab may provide more formal SDKs at some point (perhaps the generated Typescript bindings which our UI uses for example), but given the simplicity of the API, Swagger's rich generation capabilities, and our small team, it seems unnecessary.

## Want to jump right in?

Start with the quick start for an important API overview and get making your first request:

{% content-ref url="/pages/D17hhD69M33Fcx19eoGe" %}
[Quick Start](/quick-start)
{% endcontent-ref %}

## Want to deep dive?

Dive a little deeper and start exploring our API reference to get an idea of everything that's possible with the API:

{% content-ref url="/pages/tf1AQiuLxdCZzWFnUQO3" %}
[NFDomains REST API Reference](/reference/nfdomains-rest-api-reference)
{% endcontent-ref %}

Perhaps you want to understand how some of the on-chain mechanics work, or prefer to trust only the chain, using your own nodes ?

{% content-ref url="/pages/zYPL9zevAJiSryAII093" %}
[On-Chain Reference](/reference/on-chain-reference)
{% endcontent-ref %}

## Integrators

Are you an integrator looking to use NFD, see:

{% content-ref url="/pages/RQzP6nSaQv9YmzWLoiS3" %}
[Integrators Guide](/reference/integrators-guide)
{% endcontent-ref %}


# Quick Start

{% hint style="info" %}
For local testing, we **highly** recommend something like HTTPIE.\
It's much nicer to use than CURL, provides clean json body creation and automatically displays json responses in a clean format.

See: <https://httpie.io/cli>

The examples will reference using httpie via the 'http' command.
{% endhint %}

## VIEWS in GET requests

First, a quick note about the 'view' property in many of the APIs.  For efficiency, many API requests have a default 'view' that returns a focused amount of data more appropriate for certain requestors.  This view is what will be returned if no view parameter is explicitly set.  Typically this is the 'brief' view.  Some queries return a special 'thumbnail' view that is meant to to be very simple - often used for interactive 'search by name' types of queries.  Thumbnail is basically 'tiny' + avatar links and is only available via some endpoints.

Example views differences for an NFD (nfdomains.algo in this example):

{% tabs %}
{% tab title="Tiny" %}

```json
{
    "caAlgo": [
        "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I"
    ],
    "depositAccount": "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I",
    "name": "nfdomains.algo",
    "owner": "NRLA7VZ2YV6WOS2LBS3UK25DB463XRSNG63BYNQI6CSGGROFWVQ2EKBQSI",
    "properties": {},
    "unverifiedCaAlgo": [
        "ABHE544MXL2CWMIZONAIUBNVELWYMKYKWBDNRLOEFQJN3LNF2ZWSMDEKBQ"
    ]
}
```

{% endtab %}

{% tab title="Thumbnail" %}

```json
 {
        "name": "nfdomains.algo",
        "depositAccount": "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I",
        "properties": {
            "userDefined": {
                "avatar": "https://images.nf.domains/avatar/9f1940b9-2cf5-43cb-96e5-f3b36385fc90"
            }
        },
        "unverifiedCaAlgo": [
            "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I",
            "ABHE544MXL2CWMIZONAIUBNVELWYMKYKWBDNRLOEFQJN3LNF2ZWSMDEKBQ",
            "OMXLQTI5ZSMWTCIZA3O3YBW74BTCOI67SZTOEDHK3ZEDZ34Z3DEOQD4PW4"
        ]
    }
```

{% endtab %}

{% tab title="Brief" %}

```json
{
    "appID": 763844423,
    "asaID": 763844429,
    "caAlgo": [
        "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I"
    ],
    "category": "common",
    "depositAccount": "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I",
    "metaTags": [
        "9_letters",
        "pristine"
    ],
    "name": "nfdomains.algo",
    "nfdAccount": "FQDCZYHSK6CHLESEN76AGMSZ4JMT2H5BKWSV6NN2YAKDF22NUAJAOZ4ZIY",
    "owner": "NRLA7VZ2YV6WOS2LBS3UK25DB463XRSNG63BYNQI6CSGGROFWVQ2EKBQSI",
    "properties": {
        "userDefined": {
            "avatar": "https://images.nf.domains/avatar/9f1940b9-2cf5-43cb-96e5-f3b36385fc90"
        }
    },
    "saleType": "buyItNow",
    "state": "owned",
    "unverifiedCaAlgo": [
        "ABHE544MXL2CWMIZONAIUBNVELWYMKYKWBDNRLOEFQJN3LNF2ZWSMDEKBQ"
    ]
}
```

{% endtab %}

{% tab title="Full" %}

```json
{
    "appID": 763844423,
    "asaID": 763844429,
    "caAlgo": [
        "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I"
    ],
    "category": "common",
    "currentAsOfBlock": 22754901,
    "depositAccount": "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I",
    "metaTags": [
        "9_letters",
        "pristine"
    ],
    "name": "nfdomains.algo",
    "nfdAccount": "FQDCZYHSK6CHLESEN76AGMSZ4JMT2H5BKWSV6NN2YAKDF22NUAJAOZ4ZIY",
    "owner": "NRLA7VZ2YV6WOS2LBS3UK25DB463XRSNG63BYNQI6CSGGROFWVQ2EKBQSI",
    "properties": {
        "internal": {
            "asaid": "763844429",
            "category": "common",
            "commission1": "50",
            "commission1Agent": "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I",
            "contractLocked": "0",
            "highestSoldAmt": "1000000",
            "name": "nfdomains.algo",
            "owner": "NRLA7VZ2YV6WOS2LBS3UK25DB463XRSNG63BYNQI6CSGGROFWVQ2EKBQSI",
            "saleType": "buyItNow",
            "seller": "NRLA7VZ2YV6WOS2LBS3UK25DB463XRSNG63BYNQI6CSGGROFWVQ2EKBQSI",
            "timeChanged": "1660161129",
            "timeCreated": "1654229863",
            "timePurchased": "1654233465",
            "ver": "1.08"
        },
        "userDefined": {
            "avatar": "https://images.nf.domains/avatar/9f1940b9-2cf5-43cb-96e5-f3b36385fc90",
            "banner": "https://images.nf.domains/banner/da11c55f-8b85-416d-9361-612082d26f03",
            "bio": "Discord: https://discord.gg/7XcuMTfeZP",
            "caalgo": "ABHE544MXL2CWMIZONAIUBNVELWYMKYKWBDNRLOEFQJN3LNF2ZWSMDEKBQ",
            "github": "https://github.com/TxnLab",
            "name": "NFDomains",
            "website": "https://app.nf.domains"
        },
        "verified": {
            "caAlgo": "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I",
            "twitter": "@nfdomains"
        }
    },
    "saleType": "buyItNow",
    "seller": "NRLA7VZ2YV6WOS2LBS3UK25DB463XRSNG63BYNQI6CSGGROFWVQ2EKBQSI",
    "state": "owned",
    "timeChanged": "2022-08-10T19:52:09Z",
    "timeCreated": "2022-06-03T04:17:43Z",
    "timePurchased": "2022-06-03T05:17:45Z",
    "unverifiedCaAlgo": [
        "ABHE544MXL2CWMIZONAIUBNVELWYMKYKWBDNRLOEFQJN3LNF2ZWSMDEKBQ"
    ]
}
```

{% endtab %}
{% endtabs %}

## GET an NFD by NAME

To fetch an NFD by explicit name, make an HTTP GET call to the /nfd/*{name}* endpoint.

No parameters are required.  Fetching a name will return the 'brief' view by default which is sufficient for most fetches a wallet would use for example.

{% openapi src="/files/LUX7Ji30E1eiEMcVZkUr" path="/nfd/{nameOrID}" method="get" %}
[openapi3.yaml](https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2FW7CFlA4K7snWCT3liMou%2Fopenapi3.yaml?alt=media\&token=0490decf-f3b6-4911-88c6-ed5a87e7872d)
{% endopenapi %}

{% hint style="warning" %}
Please don't use the 'poll' or 'nocache' query parameters, except for specific UI/UX transitions as part of a minting process.  Ask in the dev channels for details.
{% endhint %}

### Get examples

<details>

<summary>Get name using  defaults (brief view)</summary>

```json
http https://api.nf.domains/nfd/nfdomains.algo
{
    "appID": 763844423,
    "category": "common",
    "metaTags": [
        "9_letters"
    ],
    "name": "nfdomains.algo",
    "depositAccount": "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I",
    "nfdAccount": "FQDCZYHSK6CHLESEN76AGMSZ4JMT2H5BKWSV6NN2YAKDF22NUAJAOZ4ZIY",
    "owner": "NRLA7VZ2YV6WOS2LBS3UK25DB463XRSNG63BYNQI6CSGGROFWVQ2EKBQSI",
    "properties": {
        "userDefined": {
            "avatar": "https://images.nf.domains/avatar/9f1940b9-2cf5-43cb-96e5-f3b36385fc90"
        }
    },
    "saleType": "buyItNow",
    "state": "owned",
    "unverifiedCaAlgo": [
        "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I",
        "ABHE544MXL2CWMIZONAIUBNVELWYMKYKWBDNRLOEFQJN3LNF2ZWSMDEKBQ",
        "OMXLQTI5ZSMWTCIZA3O3YBW74BTCOI67SZTOEDHK3ZEDZ34Z3DEOQD4PW4"
    ]
}
```

</details>

<details>

<summary>Get name using "full" view</summary>

```json
http 'https://api.nf.domains/nfd/nfdomains.algo?view=full'
{
    "appID": 763844423,
    "asaID": 763844429,
    "category": "common",
    "currentAsOfBlock": 21686682,
    "metaTags": [
        "9_letters"
    ],
    "name": "nfdomains.algo",
    "depositAccount": "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I",
    "nfdAccount": "FQDCZYHSK6CHLESEN76AGMSZ4JMT2H5BKWSV6NN2YAKDF22NUAJAOZ4ZIY",
    "owner": "NRLA7VZ2YV6WOS2LBS3UK25DB463XRSNG63BYNQI6CSGGROFWVQ2EKBQSI",
    "properties": {
        "internal": {
            "asaid": "763844429",
            "category": "common",
            "commission1": "50",
            "commission1Agent": "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I",
            "contractLocked": "0",
            "highestSoldAmt": "1000000",
            "name": "nfdomains.algo",
            "owner": "NRLA7VZ2YV6WOS2LBS3UK25DB463XRSNG63BYNQI6CSGGROFWVQ2EKBQSI",
            "saleType": "buyItNow",
            "seller": "NRLA7VZ2YV6WOS2LBS3UK25DB463XRSNG63BYNQI6CSGGROFWVQ2EKBQSI",
            "timeChanged": "1655530642",
            "timeCreated": "1654229863",
            "timePurchased": "1654233465",
            "ver": "1.08"
        },
        "userDefined": {
            "avatar": "https://images.nf.domains/avatar/9f1940b9-2cf5-43cb-96e5-f3b36385fc90",
            "banner": "https://images.nf.domains/banner/da11c55f-8b85-416d-9361-612082d26f03",
            "bio": "Discord: https://discord.gg/7XcuMTfeZP",
            "caalgo": "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I,ABHE544MXL2CWMIZONAIUBNVELWYMKYKWBDNRLOEFQJN3LNF2ZWSMDEKBQ,OMXLQTI5ZSMWTCIZA3O3YBW74BTCOI67SZTOEDHK3ZEDZ34Z3DEOQD4PW4",
            "github": "https://github.com/TxnLab",
            "website": "https://app.nf.domains"
        },
        "verified": {
            "twitter": "@nfdomains"
        }
    },
    "saleType": "buyItNow",
    "seller": "NRLA7VZ2YV6WOS2LBS3UK25DB463XRSNG63BYNQI6CSGGROFWVQ2EKBQSI",
    "state": "owned",
    "timeChanged": "2022-06-18T05:37:22Z",
    "timeCreated": "2022-06-03T04:17:43Z",
    "timePurchased": "2022-06-03T05:17:45Z",
    "unverifiedCaAlgo": [
        "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I",
        "ABHE544MXL2CWMIZONAIUBNVELWYMKYKWBDNRLOEFQJN3LNF2ZWSMDEKBQ",
        "OMXLQTI5ZSMWTCIZA3O3YBW74BTCOI67SZTOEDHK3ZEDZ34Z3DEOQD4PW4"
    ]
}
```

##

</details>

## GET NFDs for an Algorand Account Address (reverse lookup)

To fetch NFDs that an an address points to, make an HTTP GET call to the /nfd/lookup?address=xxxxx\[\&address=yyyyy] endpoint.&#x20;

Multiple addresses can be passed to the endpoint, with each address specified as an additional address=xxx query parameter.

If nothing is matched, a 404 (Not Found) is returned.  This is a valid return status, and also helps performance due to CDN negative caching.

{% hint style="info" %}
For more detailed information on this endpoint, please see some of the details described in [Resolving an Algorand address to an NFD name / avatar](/reference/integrators-guide/resolving-an-algorand-address-to-an-nfd-name-avatar)
{% endhint %}

{% openapi src="/files/LUX7Ji30E1eiEMcVZkUr" path="/nfd/lookup" method="get" %}
[openapi3.yaml](https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2FW7CFlA4K7snWCT3liMou%2Fopenapi3.yaml?alt=media\&token=0490decf-f3b6-4911-88c6-ed5a87e7872d)
{% endopenapi %}


# On-Chain Reference

NFDomains .algo names are controlled by the NFD Registry Contract which controls pricing, and the minting of new NFDs. &#x20;

Each NFD is a standalone contract instance, with its own Algorand account (Vault) it controls.  The registry acts as a contract factory, creating the NFDs and acting as the registrar and registry of record.  Creation and resolving of names and addresses to names is via the registry.

The following pages provide details on the on-chain storage of NFDs and the manner in which they're located.


# On-Chain Diagram V3+

NFD V3.x has on-chain storage identical to V2.  Internal properties are still in global state.  User-defined and Verified properties are box values.

The key difference with V3 is the registry now stores box values so that the NFDs a particular address OWNS may be easily determined off-chain (using direct chain state).  Future algod endpoints allowing the fetching of box keys/values with boxes having a particular prefix will be required for this to be efficient.  Until that endpoint is available, every single box value has to be fetched and filtered client-side which is completely unworkable.  Fetching hundreds of thousands, or even millions of box keys to filter down to a small number shouldn't be attempted.

<figure><img src="https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2FMwkO2jLj9FaHLzrqRpQN%2FNFD%20v3.png?alt=media&amp;token=0f2d061d-95d5-4dbb-a957-e48e0ce7457a" alt=""><figcaption></figcaption></figure>

1. Master Registry Contract for all NFDs.\
   This single smart-contract is global registration arbiter of NFDs. There is one NFD registry per Algorand network (betanet, testnet, mainnet comprising 3 independent registries). The application ID of this contract is part of part defines unique and authentic NFDs.
2. NFD Name registrations are stored in boxes within the registry contract. This is the 'forward lookup' mechanism which allows resolving a name to a NFD.\
   The box name is resolved by hashing the name with a prefix: SHA2\_256("name/{nfd name}"). ie: nfdomains.algo is hashed using "name/nfdomains.algo". The 32-byte hash is then used as the box name.\
   The value in the box consists of the ASA ID (of the NFDs NFT) and Application ID (of the NFD Smart-Contract) - each 8-byte 64-bit Big-Endian values appended together. `{ASA ID}{App ID}`
3. Algorand address linkages (for verified addresses) to NFDs are stored in boxes within the registry contract. This is the 'reverse address lookup' mechanism which resolves an Address to one or more NFDs which point to it.\
   The box name is resolved by hashing the address with a prefix: SHA2\_256("addr/{token}/{adddr bytes}"). ie: An algorand address would be hashed using addr/algo/{32 byte algo PK}. The 32-byte hash is then used as the box name.\
   The value in the box consists of a variable sized 'set' of NFD Application IDs (8-byte 64-bit Big-Endian values) appended together. A 0 value respresents an empty slot. Unique values are appended, or replaced in empty slots if found. The set and box will shrink/delete if values are removed from the end.
4. Tracking of NFDs 'Owned' by a particular address are stored in boxes within the registry contract.  Because of box limitations, this value can't be set at mint time, but are initialized post-mint by a watcher process (but anything can call the ownershipAssign contract method to set initially).  Future transfers are handled by the NFD calling the registry to remove the key from the seller and set for the new owner.
5. Is a smart contract where the data for a specific NFD is stored in global state and box values. A distinct instance and new application ID exists of this contract for every NFD. Global state is used for 'internal' properties. User-defined and verified properties are stored in distinct per-field boxes. 'Verified' fields are settable only by the NFD service itself, such as fields requiring some sort of external verification/validation. Once an application id is determined via (#2) or (#3) lookup, then all current NFD data for a particular xxx.algo NFD can be read solely by getting application/box state info from this contract, from any Algorand node.
6. Is a unique ASA created per-NFD to represent visual ‘ownership’ of a NFD to a user in their Algorand wallet. It will be created defaultFrozen and placed into their wallet as part of an initial series of minting operations and grouped transactions. The ASA can only move to a new owner including the setting of ownership fields in (#4) via a distinct ‘transfer’ operation controlled by (#4). As shown in the ASA once added to the owners Account, the URL will point to an ARC19 template-ipfs link which will resolve to a json body that is kept updated as NFD data is changed by its owner. The metadata will be in an ARC03 equivalent format so that Wallet/Gallery integration showing NFT holdings will show the NFD as an NFT, including its image being the Avatar chosen by the user (which itself could be an NFT).
7. Represents the Algorand account that is the NFD ‘Owner’
8. Represents an Algorand account the NFD owner chose to 'link' (also called a Verified address) with their NFD. This address is stored in the NFD but also in the registry so addresses can be looked up to determine the NFDs it is linked to.


# On-Chain Diagram V2+

![](https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2Fgit-blob-60e3830826731b78bf1908f4e02fa3e851990f69%2FNFD%20v2.png?alt=media)

1. Master Registry Contract for all NFDs.\
   This single smart-contract is global registration arbiter of NFDs. There is one NFD registry per Algorand network (betanet, testnet, mainnet comprising 3 independent registries). The application ID of this contract is part of part defines unique and authentic NFDs.
2. NFD Name registrations are stored in boxes within the registry contract. This is the 'forward lookup' mechanism which allows resolving a name to a NFD.\
   The box name is resolved by hashing the name with a prefix: SHA2\_256("name/{nfd name}"). ie: nfdomains.algo is hashed using "name/nfdomains.algo". The 32-byte hash is then used as the box name.\
   The value in the box consists of the ASA ID (of the NFDs NFT) and Application ID (of the NFD Smart-Contract) - each 8-byte 64-bit Big-Endian values appended together. `{ASA ID}{App ID}`
3. Algorand address linkages (for verified addresses) to NFDs are stored in boxes within the registry contract. This is the 'reverse address lookup' mechanism which resolves an Address to one or more NFDs which point to it.\
   The box name is resolved by hashing the address with a prefix: SHA2\_256("addr/{token}/{adddr bytes}"). ie: An algorand address would be hashed using addr/algo/{32 byte algo PK}. The 32-byte hash is then used as the box name.\
   The value in the box consists of a variable sized 'set' of NFD Application IDs (8-byte 64-bit Big-Endian values) appended together. A 0 value respresents an empty slot. Unique values are appended, or replaced in empty slots if found. The set and box will shrink/delete if values are removed from the end.
4. Is a smart contract where the data for a specific NFD is stored in global state and box values. A distinct instance and new application ID exists of this contract for every NFD. Global state is used for 'internal' properties. User-defined and verified properties are stored in distinct per-field boxes. 'Verified' fields are settable only by the NFD service itself, such as fields requiring some sort of external verification/validation. Once an application id is determined via (#2) or (#3) lookup, then all current NFD data for a particular xxx.algo NFD can be read solely by getting application/box state info from this contract, from any Algorand node.
5. Is a unique ASA created per-NFD to represent visual ‘ownership’ of a NFD to a user in their Algorand wallet. It will be created defaultFrozen and placed into their wallet as part of an initial series of minting operations and grouped transactions. The ASA can only move to a new owner including the setting of ownership fields in (#4) via a distinct ‘transfer’ operation controlled by (#4). As shown in the ASA once added to the owners Account, the URL will point to an ARC19 template-ipfs link which will resolve to a json body that is kept updated as NFD data is changed by its owner. The metadata will be in an ARC03 equivalent format so that Wallet/Gallery integration showing NFT holdings will show the NFD as an NFT, including its image being the Avatar chosen by the user (which itself could be an NFT).
6. Represents the Algorand account that is the NFD ‘Owner’
7. Represents an Algorand account the NFD owner chose to 'link' (also called a Verified address) with their NFD. This address is stored in the NFD but also in the registry so addresses can be looked up to determine the NFDs it is linked to.


# V1 On-Chain Diagram

![](https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2FOW3rMOMdsBwzg7YXDEhe%2FNFD%20Contracts%20v4.png?alt=media\&token=ecb48382-70c7-4fb2-aa30-3eba58450556)

1. Master Registry Contract for all NFDs.\
   This single smart-contract is global registration arbiter of NFDs. The application ID of this contract is part of what makes the smart-signature contracts (#2) and (#3) unique and authentic NFDs.
2. A smart signature used for resolving a NFD name. The bytes of the NFD name and the registry contract id (#1) are used in templatized TEAL to derive a unique smart signature address. That account is opted into (#1) and its local state references the App ID of (#4) and the ASA ID of (#5)
3. A smart signature used for resolving an Algorand Account address back to one or more NFDs that reference it. This is the ’reverse lookup’ smart signature. Like (#2), this account is opted into (#1) and its local state references (#4) and (#5).
4. Is a smart contract where all global state contains the data for a specified NFD. A distinct instance and new application ID exists of this contract for every NFD. A number of fields are settable only by the NFD service itself, such as fields requiring some sort of external verification/validation. Once an application id is determined via (#2) or (#3) lookup, then all current NFD data for a particular xxx.algo NFD can be read solely by getting application state info from this contract, from any Algorand node.
5. Is a unique ASA created per-NFD to represent visual ‘ownership’ of a NFD to a user in their Algorand wallet. It will be created defaultFrozen and placed into their wallet as part of an initial series of minting operations and grouped transactions. The ASA can only move to a new owner including the setting of ownership fields in (#4) via a distinct ‘transfer’ operation controlled by (#4). As shown in the ASA once added to the owners Account, the URL will point to an ARC19 template-ipfs link which will resolve to a json body that is kept updated as NFD data is changed by its owner. The metadata will be in an ARC03 equivalent format so that Wallet/Gallery integration showing NFT holdings will show the NFD as an NFT, including its image being the Avatar chosen by the user (which itself could be an NFT).
6. Represents the Algorand account that is the NFD ‘Owner’
7. Represents an Algorand account the NFD owner chose to set one of their crypto account addresses to for the Algorand chain. This is where payments would go if a user chose to ‘send’ ALGO to a NFD - the currently registered ALGO target address in the NFD.


# Registry Application IDs

These are the Algorand Application IDs of the NFD registry contract, per Algorand network.

These IDs are used in the name and address lookup smart-signature replacement.

Mainnet: **760937186**

Testnet: **84366825**

Betanet: **842656530**


# Contract methods

For more explicit help, pflease reach out on our discord and the nfd-api channel

This list is just for reference.  There is no formal compatibility guarantee of these contract calls into the future as new NFD versions are created, particularly any methods that are part of minting/registration as they will evolve/change greatly (as they're often called only once - at mint time).

If you plan to read NFDs entirely on-chain, please let us know as the exposed methods are assumed to evolve as the AVM conventions evolve and mature (notably ABI specs) likely only being put into a versioned compatibility guarantee at that point.

### V3 ARC 32 ABI

{% file src="/files/ojq0Gl2qkEnJLYWvY34J" %}
Registry ABI Calls
{% endfile %}

{% file src="/files/c6aSSdVAxe8ZJ77s6DbI" %}
NFD V3 ABI Calls
{% endfile %}

### V3 ARC56 ABI

{% file src="/files/5YJUMJRcl5NpQVroLfNL" %}
Registry ABI Calls
{% endfile %}

{% file src="/files/GHOAkXYuOjLi8X3IIF4t" %}
NFD V3 ABI Calls
{% endfile %}

### V2 Non-ABI Methods

<figure><img src="https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2FOP6gmf3syXZDSurZDkOp%2Fcontract_calls.png?alt=media&amp;token=d685cdcc-1524-4c40-b446-b2d64d44191f" alt=""><figcaption></figcaption></figure>


# Name & Address Lookup

The following Go code and example command-line application demonstrates how to efficiently  resolve the smart-signature account address for a **name**, or an **address** on-chain.

See <https://go.dev/learn/> if you want a quick start with go.  Other implementations will be provided, but this code should be fairly understandable and easily translated to other languages.

{% hint style="warning" %}
**See** [**https://github.com/TxnLab/nfd-cli-examples**](https://github.com/TxnLab/nfd-cli-examples) **for a simple CLI program that provides, name, reverse-address lookup (verified, linked addresses), 'owned by' lookup (3.4+), as well as decoding/displaying of all the metadata within an NFD.**

**This program supports looking up / reading V2+ NFDs as well as V1.**
{% endhint %}

{% tabs %}
{% tab title="Name Lookup" %}
To lookup the smart-signature account address for nfdomains.algo, you could use the following example command-line application and run:

`go run ./main.go -name nfdomains.algo`

Which (for mainnet) will output:

Found as V2 name, ASA ID:763844429, APP ID:763844423\
...

This can be verified against the API, by using the nameSig REST API, a GET request to <https://api.nf.domains/nfd/nameSig/nfdomains.algo> which should return the exact same address.
{% endtab %}

{% tab title="Address Lookup" %}
To be able to look up the NFD app ids for a particular address, run:

`go run ./main.go -addr` RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I

Which will output:

Found 1 NFDs linked as V2 address\
\[...]

The reverse address verification API for the same:\
<https://api.nf.domains/nfd/revAddressSig/RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I>

{% endtab %}

{% tab title="NFDs owned By" %}
To be able to look up the NFD app ids owned by a particular address, run:

`go run ./main.go -addr` RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I -owns

This will display all 3.x NFDs owned by a particular address.  V1/V2 NFDs are not fetchable using this solution.  The NFD API will need called instead (or you will need to index NFDs yourself).
{% endtab %}
{% endtabs %}


# V1 Resolution example (go)

The following Go code and example command-line application demonstrates how to efficiently  resolve the smart-signature account address for a **name**, or an **address** on-chain.

{% hint style="info" %}
This code is only for NFDs registered prior to the deployment of the 2.x NFD registry.
{% endhint %}

```go
/*
 * Copyright (c) 2022. TxnLab Inc.
 * All Rights reserved.
 */

package main

import (
	"bytes"
	"encoding/binary"
	"errors"
	"flag"
	"fmt"
	"log"
	"reflect"

	"github.com/algorand/go-algorand-sdk/crypto"
	"github.com/algorand/go-algorand-sdk/types"
)

func main() {
	var (
		lsig     crypto.LogicSigAccount
		lsigAddr types.Address
		err      error
	)
	name := flag.String("name", "", ".Algo Name for forward lookup - invalid names can be passed here but would never be allowed to be minted...")
	address := flag.String("addr", "", "Algorand address for reverse-address lookup")
	regAppID := flag.Uint64("id", 760937186, "Registry application id (mainnet defaulted)")
	flag.Parse()

	if *name == "" && *address == "" {
		flag.Usage()
		log.Fatalln("You must specify a name, or an address")
	}
	if *name != "" {
		lsig, err = GetNFDSigNameLSIG(*name, *regAppID)
	}
	if *address != "" {
		addr, err := types.DecodeAddress(*address)
		if err != nil {
			log.Fatalln("Error decoding algoand address parameter:", err)
		}
		lsig, err = GetNFDSigRevAddressLSIG(addr, *regAppID)
	}
	if err != nil {
		log.Fatalln("error in lsig calculation:", err)
	}
	lsigAddr, err = lsig.Address()
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Println("Registration account:", lsigAddr.String())
}

func getLookupLSIG(prefixBytes, lookupBytes string, registryAppID uint64) (crypto.LogicSigAccount, error) {
	/*
		#pragma version 5
		intcblock 1
		pushbytes 0x0102030405060708
		btoi
		store 0
		txn ApplicationID
		load 0
		==
		txn TypeEnum
		pushint 6
		==
		&&
		txn OnCompletion
		intc_0 // 1
		==
		txn OnCompletion
		pushint 0
		==
		||
		&&
		bnz label1
		err
		label1:
		intc_0 // 1
		return
		bytecblock "xxx"
	*/
	sigLookupByteCode := []byte{
		0x05, 0x20, 0x01, 0x01, 0x80, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
		0x07, 0x08, 0x17, 0x35, 0x00, 0x31, 0x18, 0x34, 0x00, 0x12, 0x31, 0x10,
		0x81, 0x06, 0x12, 0x10, 0x31, 0x19, 0x22, 0x12, 0x31, 0x19, 0x81, 0x00,
		0x12, 0x11, 0x10, 0x40, 0x00, 0x01, 0x00, 0x22, 0x43, 0x26, 0x01,
	}
	contractSlice := sigLookupByteCode[6:14]
	if !reflect.DeepEqual(contractSlice, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}) {
		return crypto.LogicSigAccount{}, errors.New("Lookup template doesn't match expectation")
	}
	// Bytes 6-13 [0-index] with 0x01-0x08 placeholders is where we put the Registry Contract App ID bytes in big-endian
	binary.BigEndian.PutUint64(contractSlice, registryAppID)

	// We then 'append' the bytes of the prefix + lookup to the end in a bytecblock chunk
	// ie: name/patrick.algo, or address/RXZRFW26WYHFV44APFAK4BEMU3P54OBK47LCAZQJPXOTZ4AZPSFDAKLIQY
	// - the 0x26 0x01 at end of sigLookupByteCode is the bytecblock opcode and specifying a single value is being added

	// We write the uvarint length of our lookup bytes.. then append the bytes of that lookpup string..
	bytesToAppend := bytes.Join([][]byte{[]byte(prefixBytes), []byte(lookupBytes)}, nil)
	uvarIntBytes := make([]byte, binary.MaxVarintLen64)
	nBytes := binary.PutUvarint(uvarIntBytes, uint64(len(bytesToAppend)))
	composedBytecode := bytes.Join([][]byte{sigLookupByteCode, uvarIntBytes[:nBytes], bytesToAppend}, nil)

	logicSig := crypto.MakeLogicSigAccountEscrow(composedBytecode, [][]byte{})
	return logicSig, nil
}

func GetNFDSigNameLSIG(nfdName string, registryAppID uint64) (crypto.LogicSigAccount, error) {
	return getLookupLSIG("name/", nfdName, registryAppID)
}

func GetNFDSigRevAddressLSIG(pointedToAddress types.Address, registryAppID uint64) (crypto.LogicSigAccount, error) {
	return getLookupLSIG("address/", pointedToAddress.String(), registryAppID)
}

```

{% hint style="info" %}
To be clear, this "lookup" is a hash to get to an Algorand account that *may* contain state.  It does not mean data exists there at the moment.
{% endhint %}


# Example PyTeal name/ID validity check

The following PyTeal code can be used to validate that the specified NFD name matches the passed NFD App ID and that it's an authentic NFD.

```python
# is_nfd_id_authentic composes the name lookup LSIG and checks if the app id stored in the passed-in NFD name's
# registry-contract local state matches the passed Nfd App ID.
# ie: patrick.algo -> check registry contract discovery mechanism -> read app id.. does it match.
@Subroutine(TealType.uint64)
def is_nfd_id_authentic(nfd_name, nfd_app_id):
    lsigByteCode = ScratchVar(TealType.bytes)
    nameBytes = ScratchVar(TealType.bytes)
    progHash = ScratchVar(TealType.bytes)

    localGetMv = App.localGetEx(progHash.load(), Tmpl.Int("TMPL_NFD_RC_ID"), REGISTRY_KEY_APPID)
    return Seq(
        nameBytes.store(Concat(Bytes("name/"), nfd_name)),

        lsigByteCode.store(Concat(Bytes("base16", "0x052001018008010203040506"),
                                  Bytes("base16", "0x070817350031183400123110"),
                                  Bytes("base16", "0x810612103119221231198100"),
                                  Bytes("base16", "0x1211104000010022432601"))),
        # Now replace bytes 6-13 w/ NFD's registry contract id
        lsigByteCode.store(Replace(lsigByteCode.load(), Int(6), Itob(Tmpl.Int("TMPL_NFD_RC_ID")))),
        # then concat {length}name/{nfd_name} - uvar int (just last byte in BE value) - then bytes of string
        lsigByteCode.store(
            Concat(lsigByteCode.load(), encode_uvarint(Len(nameBytes.load())), nameBytes.load())),
        # we can now hash these bytes to get our LSIG address
        progHash.store(Sha512_256(Concat(Bytes("Program"), lsigByteCode.load()))),
        localGetMv,
        Assert(localGetMv.hasValue(),
               comment="lsig has to have i.appid localstate"),
        Return(Btoi(localGetMv.value()) == nfd_app_id))


```

See [Registry Application IDs](/reference/on-chain-reference/registry-application-ids)for the correct registry ID to substitute in the TMPL\_NFD\_RC\_ID parameter.


# NFDs owned by Address

Use the /nfd/v2/search?owner=***address*** endpoint to fetch all NFDs a particular address *owns*.

For fetching large numbers of results, set limit=200 (maximum limit) and page through the results using the offset parameter.

{% openapi src="/files/LUX7Ji30E1eiEMcVZkUr" path="/nfd/v2/search" method="get" %}
[openapi3.yaml](https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2FW7CFlA4K7snWCT3liMou%2Fopenapi3.yaml?alt=media\&token=0490decf-f3b6-4911-88c6-ed5a87e7872d)
{% endopenapi %}


# Reading of NFD metadata

Every NFD is a distinct instance of an Algorand Smart Contract. It has a distinct Application ID, and because it is a contract account, it can also independently hold and control its own assets.

Every NFD is created using a fixed amount of global state keys, storing only 'internal' properties (i.\*) in global state. Starting in version 2.x(+) of the NFD contracts, all user-defined and verified properties are stored in [box storage](https://developer.algorand.org/docs/get-details/dapps/smart-contracts/apps/state/#box-storage).  Prior to 2.x, all properties were stored in global state.

All NFD data is stored with prefixed keys, where the 2 character prefix defines the type and *permission* level on the key.

### Property Types

The 3 different types are:

* **"i." - Internal**
  * Internal properties can ONLY be set by the *contract itself* as part of creation, and various state changes - minting, reservations / claiming, offering up for sale, etc.
  * These are all stored in global state.
* **"u." - User-Defined**
  * User defined properties can only be set by the Owner.
  * NFDomains can *clear* u. fields as part of putting up an NFD for sale (as part of clearing all metadata), or when a property is verified \[moving from u. to v. - which is deleting the u.xxx value and moving to its corresponding v.xx value].
* **"v." - Verified**
  * Verified properties can currently only be set by NFDomains.
  * In the future, as Off-chain/On-Chain Oracle contracts become available that provide equivalent (and trustable) services, then NFD contracts will hopefully be able to allow specific oracles to set specific verified properties.

Internal, and Verified properties will often be camelCase.

All user-defined properties will always be lowercase (at least as set in transactions provided by the API/UI). The contracts themselves don't care - users own their data - but differing from conventions may cause problems in integrations.

### Property Schema

In the majority of cases, properties are simply 'bit buckets' - often just string values. Some values (only internal properties) are stored natively as integers (in Algorand 64-bit Big-Endian format - compatible with itob / btoi TEAL operations). Some are packed Algorand 32-byte PKs (not including the checksum).

Aside from some specific internal properties, properties will have special suffixes to denote different encodings.

**Encoding Suffix hints**

* **.a** - This represents an Algorand Account address. The value will be the 32-byte public-key of the account. The Algorand SDK EncodeAddress method can be used to convert to an Algorand account.
  * An example would be i.owner.a - the owner of the NFD.
* **.#.as** - this represents a 'set' of Algorand addresses. There will be up to 3 per value packed together.
  * An example would be v.caAlgo.0.as - this would represent the first (up to 10 - 0 through 9) of a set of packed algorand addresses . This particularly property contains the 'verified' linked 'Crypto Address' Algo addresses. v.caAlgo.0.as would contain 1, 2, or 3 addresses. If there were more, then v.caAlgo.1.as would be present with addresses 4+ (up to #6) and so on. The user linking 10 addresses would create calls to the update\_field method, setting v.caAlgo.0.as, v.caAlgo.1.as, v.caAlgo.2.as, and v.caAlgo.3.as to fit all 10 addresses. If the user removed 9 leaving just 1, then the API wouold generate calls to remove all but v.caAlgo.0.as - setting it to just the 1 address.
* ***\_##** - This represents a value that's been extended across keys. The boxes used for properties are constrained to 1988 bytes (allowing for 60 char key name), with larger values spread across keys.*
  * An example would be a long u.metadata field (perhaps 3K characters). It would be stored as **u.bio\_00** with (8+1988 bytes \[u.bio\_00 being 8 bytes] and then 1988 bytes of the first part of its value). **u.bio\_01** would then contain the remaining 1084 bytes of the 3K valuen. In this 3072 byte example, 1988 chars in 00, and 1084 in 01.
  * On update, the NFDomains API will create update\_field transactions to remove any excess fields if the value is shortened, and also the extra fields when the value is longer. During reading, all of these extended values are concatenated together and returned via the API as a single field, stripping off the suffix. ie, just u.bio in this example.


# Properties

Be sure to see [Reading of NFD metadata](/reference/on-chain-reference/reading-of-nfd-metadata)for descriptions of how to read the metadata. This merely lists the explicit properties and some conventions

### Internal Properties

{% hint style="info" %}
All of these properties will be prefixed with 'i.' - so a field listed as asaid is really stored as i.asaid

ONLY the Contract itself can alter these properties.
{% endhint %}

| Field                     | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| i.asaid                   | <p>ASA ID (8-byte BE). The id of the NFT created by the NFD and set into the owners account.<br>Upon claiming, or purchase, the NFT is clawed by the NFD from seller to buyer.</p>                                                                                                                                                                                                                                                                               |
| i.name                    | The name of the NFD (may not be full portion - details in the future)                                                                                                                                                                                                                                                                                                                                                                                            |
| i.category                | A category as set by NFDomains at mint time. Current possible values are: curated, premium, and common.                                                                                                                                                                                                                                                                                                                                                          |
| i.commission1Agent.a      | <p>If set, account that will receive a commission upon sale. If commission2Agent is set, then after sale, commission2Agent replaces commission1Agent (and percentage) and is cleared. This allows primary and secondary sales to have different commission agents.<br>(32-byte PK)</p>                                                                                                                                                                           |
| i.commisson1              | <p>commission % (in tenths). 10 would be 1 %.<br>(8-byte BE)</p>                                                                                                                                                                                                                                                                                                                                                                                                 |
| i.mintingKickoffCreator.a | <p>If set, this account has placed Algo into the NFD's account in advance to be used in later claiming. Upon the NFD being purchased (initial claim only), then if the buyer is the same as the kickoff creator the amount needed to claim is their kickoff amount + the amount they're offering on the purchase.<br>If the purchaser is not the same as the kickoff creator, then the kickofCreator account is refunded their kickoffAmount<br>(32-byte PK)</p> |
| i.mintingKickoffAmount    | <p>The amount the KickoffCreator account placed into the NFD.<br>(8-byte BE)</p>                                                                                                                                                                                                                                                                                                                                                                                 |
| i.highestSoldAmt          | <p>Highest sale price for this NFD in its lifetime (in microAlgos)<br>(8-byte BE)</p>                                                                                                                                                                                                                                                                                                                                                                            |
| i.owner.a                 | The 32-byte PK of the Algorand account that owns this NFD                                                                                                                                                                                                                                                                                                                                                                                                        |
| i.parentAppID             | <p>If this is a segment, this will be set to the App ID of this NFD's root NFD.<br>(8-byte BE)</p>                                                                                                                                                                                                                                                                                                                                                               |
| i.sellamt                 | <p>Only set if NFD is for sale.<br>This is effectively a state unto itself. A for-sale NFD should NOT accept deposits.<br>The sale price in microAlgos. (8-byte BE)</p>                                                                                                                                                                                                                                                                                          |
| i.saleType                | The sales method in which this NFD was originally minted - currently either auction, or buyItNow                                                                                                                                                                                                                                                                                                                                                                 |
| i.seller.a                | The 32-byte PK of the 'seller' of this NFD. This is the account paid upon any sale. At mint time, the seller is NFDomains and then upon claiming, the seller is always set to the new owner.                                                                                                                                                                                                                                                                     |
| i.timeCreated             | The on-chain 'last timestamp' \[seconds since epoch] when the NFD was created via its app create/creation contract call (8-byte BE)                                                                                                                                                                                                                                                                                                                              |
| i.timeChanged             | The on-chain 'last timestamp' for updates of the NFD - sales, metadata updates, etc.  (8-byte BE)                                                                                                                                                                                                                                                                                                                                                                |
| i.timePurchased           | Timestamp of most recent purchase  (8-byte BE)                                                                                                                                                                                                                                                                                                                                                                                                                   |
| i.expirationTime          | If present, indicates a renewal-based NFD with the value being set to the 8-byte BE timestamp (seconds unixtime) of expiration.                                                                                                                                                                                                                                                                                                                                  |
| i.ver                     | The 'NFD contract' version. Set at creation time, and if upgraded (to version of new contract)                                                                                                                                                                                                                                                                                                                                                                   |
| i.contractLocked          | <p>0 for unlocked, 1 for locked. Defaults to 0.<br>If set to 1 by NFD owner, the contract of that NFD is completely blocked from being changed. Owner may later unlock, and upgrade on-demand if desired.</p>                                                                                                                                                                                                                                                    |
| i.segmentLocked           | <p>0 for unlocked, Undefined or 1 for locked. Defaults to locked.<br>If NOT set to 0 by NFD owner, the NFD root is locked and a segment of the root can't be minted</p>                                                                                                                                                                                                                                                                                          |
| i.segmentPriceUsd         | For unlocked segment, the price in USD cents to mint a segment off this unlocked root. 400 would be $4.00. (8-byte BE)                                                                                                                                                                                                                                                                                                                                           |
| i.segmentCount            | If a root, the number of segments that have been minted from this NFD. (8-byte BE)                                                                                                                                                                                                                                                                                                                                                                               |
| i.vaultOptInLocked        | <p>"0" for unlocked, Undefined or "1" for locked. Defaults to UNLOCKED.<br>Can also be set to a Itob(xx) (64-bit Big-Endian value) representing the timestamp at which the vault should 're-lock'.  If < current timestamp, then vault is UNLOCKED.  If > current timestamp, then vault is locked.<br>This is useful for 'allow airdrops for... 1 hour' sort of requests.<br>If locked, only the owner can send new assets into the vault</p>                    |

### User-Defined Properties

{% hint style="info" %}
User properties are arbitrary and can be set to anything by the user.

All of these properties will be prefixed by 'u.'.

Some properties will be treated specially by the NFDomains UI, but there are no explicit rules for them. Owners may cause problems with some integrations if the conventions aren't followed though.
{% endhint %}

| Field        | Description                                                                                                                                                                                             |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| u.name       | User / Company name                                                                                                                                                                                     |
| u.bio        |                                                                                                                                                                                                         |
| u.address    | Company address, etc.                                                                                                                                                                                   |
| u.email      | Email address that can optionally be set as a 'verified' email if user verifies their email address.                                                                                                    |
| u.url        | Url to redirect to via xxxx.algo.xyz domain, can also be ipfs\://XXXXX link as well.                                                                                                                    |
| u.avatar     | <p>Url to avatar image (ipfs\:// supported and preferred).<br>User could technically set this to anything, interpreting is up to reader.  In general, will be an https\:// link or ipfs\:// link.  </p> |
| u.banner     | Url to banner image.  Issues similar to u.avatar.                                                                                                                                                       |
| u.twitter    | Twitter handle - preferably an intermediate field set prior to verification.                                                                                                                            |
| u.discord    | intermediate field - preferably used to specify the discord user (snowflake) id as part of verification process.                                                                                        |
| u.telegram   | intermediate field - preferably used to specify the telegram id as part of verification process                                                                                                         |
| u.domain     | DNS Zone name (ie: nf.domains) for possible verification.                                                                                                                                               |
| u.website    | Simple link to show on profile for viewers to click-through                                                                                                                                             |
| u.caalgo     | User-defined Algorand Addresses (C]rypto A]ddress Algo]rand), stored comma-delimited. Exposed as top-level unverifiedCaAlgo array in API.                                                               |
| u.ca.{token} | <p>User Defined C]rypto A]ddress. Use token name for type.<br>ie:<br><strong>ca.btc</strong> for Bitcoin deposit address.<br><strong>ca.eth</strong> for Ethereum, etc.</p>                             |
| ...          |                                                                                                                                                                                                         |

### Verified Properties

{% hint style="info" %}
All of these properties will be prefixed by 'v.'

ONLY NFDomains can currently set verified properties.

As decentralized services become available which can be trusted with these off-chain/on-chain services, NFDomains will strive to remove to remove this requirement and support these services being given the trust to set certain verified properties.
{% endhint %}

| Field         | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| v.avatar      | <p>Link to verified avatar - typically ipfs\:// link.<br>Will either be https\:// link (most likely direct to image), or ipfs\://{cid}\[/path] link pointing either to an image, or to ARC3/ARC19 style metadata (where you would use the image property from there).<br><br><a href="https://github.com/barnjamin/arc3.xyz"><https://github.com/barnjamin/arc3.xyz></a> is a simple example (view an asset at <strong><https://arc3.xyz/nft/asset-id></strong> )<br>Basically, do HEAD fetch of ifps content from gateway of choice, if json, fetch the json and (simplest) look for "image" property - if there - use that.  If not json, use what's fetched as image result.</p> |
| v.avatarasaid | The ASA ID of the avatar. NFDomains will re-assert that this ASA is still owned by accounts related to the NFD and will clear the avatar fields if not.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| v.banner      | Link to verified banner - typically ipfs\:// link.  See comments for v.avatar for reading.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| v.bannerasaid | The ASA ID of the banner. Re-asserted same as avatar.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| v.caAlgo.0.as | See [Reading of NFD metadata](/reference/on-chain-reference/reading-of-nfd-metadata)for details. Contains packed 32-byte PKs of Algorand addresses which were verified by the owner as an address they can sign for.                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| v.domain      | DNS Zone that was verified                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| v.twittter    | Telegram @handle that was verified                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| v.discord     | Discord snowflake ID that was verified                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| v.telegram    | Telegram user id that was verified.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
|               |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |


# Contract Changelog

Available updates to NFD Contracts will be documented here.

Users may 'lock' their NFD via Edit->Contract Version - 'Toggle Contract Lock'. If this is set, the contract can not be upgrade by NFDomains to add new features or fix security issues.  If new contract versions are available and your NFD isn't expired, you can always unlock, and upgrade.

{% hint style="danger" %}
All 1.x NFDs MUST upgrade to 2.X (currently 2.12) in order to allow any edits or segments to be minted from roots, because of new storage model and changes for new V3 mints.
{% endhint %}

Current deployed contract versions:

**betanet / testnet / mainnet : 3.9** \[all new NFDs]

### 3.9

* Fixed bug in clearing verified fields by owner - the check if != v.caAlgo prefix needed to handle short v.xx names.
* Fixed bug in vault sends where 'send all' wasn't closing out the asset as well (leaving excess MBR trapped in vault).  Owner can upgrade NFD, send asset in and back out to close out the asset.

### 3.8

* Fixed bug in segment pricing where treasury wasn't getting proper amounts compared to root owner for multi-year mints.

### 3.5 - 3.7: Released as 3.7

* Fixed bug in setPrimaryAddress - it allowed someone other than owner to change the order (couldn't 'change/add/remove addresses' - could only change the order)
* If NFD is for sale and then expires, disallow purchase, and clear 'for-sale' info when renewed.
* If NFD was still for sale once it expired, then clearing of fields (necessary to renew by another user) was being blocked.
* Fixed bug in not being able to unlink an address for an expired NFD via registry.  Clearing of all data in an NFD is necessary before it can be renewed by a different owner (to take over ownership).   Unlinking being blocked unless owner did it over which would happen if different owner tries to buy expired nfd)

### 3.4 Tracking/Updating of 'nfds owned by' box storage

* The registry and NFDs will now create box values that will make it easier in the future to fetch all NFDs owned by a particular address via off-chain algod state fetches.  While it won't support NFDs prior to 3.x, it provides a path forward for fetching owned NFDs without having to maintain an independent index and tracking of on-chain state changes.

### 3.3 Renewal model and Self-Minting GA (09/02/2024)

* The changes are quite significant (rewriting from PyTeal to Tealscript for one) but at a high level, NFDs themselves are the same with use of global state and box storage not changing.
* A new internal property was added in NFDS to track expiration time.  If it doesn't exist, then the NFD is v1 or v2 and doesn't expire.  V3 NFDs will always have an expiration set in the i.expirationTime property.
* Expired NFDs can be renewed by the owner without having to first clear data.  Anyone else can renew the NFD if expired but the NFD must be clear of all data first.  If someone not the existing owner tries to renew the NFD, there is a 28 day reverse-auction pricing model where the price to renew scales from $100K down to the regular price over 28 days.  The existing owner can just renew at base price at any time.
* The registry itself now contains the byte code for NFDs and acts as a factory, controlling the minting of new NFDs, creating and assigning into the registry as part of a single mintNfd method.  Previously TxnLab had to sign certain transactions as part of minting to ensure contract authenticity.  With box storage, the Registry itself can now handle everything.  All pricing and all minting is controlled entirely through the registry contract alone.
* New NFDs will always be renewal based, with an expiration time equal to the proportional number of days the paid amount buys vs the yearly cost of that NFD.  Expiration times can't go beyond 20 years into the future.

### 2.11 Box storage & NFD Vaults

* **Box Storage** in NFDs and the NFD Global Registry contract.
* The registry goes from local state in logic-sig accounts (per name and address lookup) to box storage per name and address in the registry.
* Moving away from 'Global/Local State' in NFDs to new Algorand 'Box' storage. This will lower the carry cost as well as allow near unlimited properties in an NFD. Users will now 'pay as they go' for the MBR (Minimum Balance Requirement) of box storage for their metadata. Field additions/updates will add ALGO to the NFD contract account (only) as necessary to cover the required MBR of the additions to storage.
  * Internal properties remain in global state. (ie: i.name, i.owner, etc.)
  * User-defined and Verified NFD properties (u.xxxx / v.xxx) move to box storage
    * Limited to just under 2K each (up from 128 bytes). 2K is maximum size of arguments for any application call. The backend/api will still handle breaking larger values across properties - so an 7K value would be transparently split across 4 boxes (and combined on read) ie: u.preferences\_00, u.preferences\_01, u.preferences\_02, u.preferences\_03
    * 1.x contracts all go completely read-only in the interface. Users upgrading their NFD to 2.x contract will migrate the name and addresses to the new registry (box) storage as well as all of the fields from global state to boxes.
* **Vault** support added
  * Allows sending assets TO the NFD Vault account (the contract account that's 1:1 tied to each NFD - controlled by the NFD owner). The NFD Vault will auto opt-in to assets, allowing airdrops for the first time in Algorand.
  * The vault is 'locked' by default. When locked, only the owner can send assets into their vault (from owner account).
    * The owner can unlock the vault, in which case anyone can send to the vault, but they must also cover the .1 MBR cost of the vault in order to send the asset by sending .1 in prior transaction in an atomic transaction group.
* Pricing slightly changed. The carry cost (MBR) of the contracts is now charged on top of the USD price of NFD roots. Previously it was part of the gross amount. However, the carry cost has dropped from 5 ALGO to 2 ALGO because of the switch to box storage (which is now 'pay as you go').
* Additional updates during betanet/testnet:
* 2.2 Adds ability to set optionally set note in 'send from vault' transactions.
* 2.3 The box name for algo addresses is now based on a hash sha256(addr/algo/{32 byte pk})
* 2.4
  * Change vault\_optin to allow multiple assets to be opted-in as a batch
  * vault\_optin no longer pays back if already opted-in - its callers responsibility to know. Expectation is .1 \* asset(s) opted-in
* 2.5
  * Changed to allow NFDomains to set verified avatar of an NFD still in pre-claim state, allowing avatars to be set for auction items
  * When putting up for sale, don't force '0 boxes' rule if still pre-claim and nfdomains is caller (this is so avatar can be set for nfd during auction re-pricing]
* 2.6 - Allow batch sends of multiple assets per txn 'from' a vault
* 2.7 - Added method to allow combining global state key values into new single box - used for v1->v2 migration of v.caAlgo.\*.as to v.caAlgo.0.as
* 2.8 - Changed vault lock/unlock to allow setting a 'unlock UNTIL x' option in parameter.  str:0 to unlock, str:1 to lock, or int:xxxx (timestamp in seconds) to unlock but specify a time at which the vault automatically re-locks.  This is useful for 'allow airdrops for... 1 hour' sort of requests.
* 2.9 - Added read\_property method for reading an NFD property via simulate calls or via c2c calls.
* 2.10 - Modified the fieldname used for 2-stage address verification - u.cav.algo is now u.cav.algo.a to properly reflect the 32-byte PK storage type.
* 2.11 - the default for Vaults will now be UNLOCKED. Purchasing also resets to the UNLOCKED default.

### 1.16 GA rollout of Segments feature

* Added additional asserts in segment\_lock / contract\_lock calls.
* Updated **make\_offer** method (AKA: Purchase / Claim) to accept additional optional parameter, on whether to 'opt out' of receiving the NFD's NFT. Pass string "0" to opt-out. This is mainly for large projects, minting a significant number of segments in a central account and not wanting the extra MBR requirements on the owning account.

### 1.13 / 1.14 / 1.15 (beta segments)

* Prevent segment minting, even by owner, if root is for sale.
* Minimum segment price is now 400 ($4.00)\
  Segment Discount table is now:
* | Number Minted | Discount Rate | Price (USD) |
  | ------------- | ------------- | ----------- |
  |               |               | $ 4.00      |
  | 10            | 20%           | $ 3.20      |
  | 100           | 30%           | $ 2.80      |
  | 1,000         | 40%           | $ 2.40      |
  | 10,000        | 50%           | $ 2.00      |
  | 100,000       | 60%           | $ 1.60      |
  | 1,000,000     | 70%           | $ 1.20      |
* Added ability to control/mint segments off of root NFDs - price is set in USD.
* ALGO/USD price retrieved from new ALGO/USD contract w/ price regularly updated by NFDomains.
* Added **segment\_lock** method - allowing locking of NFD, or unlocking of NFD with USD price in cents.
  * str: segment\_lock
  * str: 1 (to lock), 0 (to unlock)
  * int: {usd price in cents - ie: 300 for $3.00)
* Added **update\_segment\_count** method to NFD - for updating i.segmentCount of parent when a segment is minted from it. Part of mint txn group - validates already created (in group) child is proper subset and is formally an NFD and registered properly on-chain via LSIG lookup/state data.
  * str: update\_segment\_count (parent of segment is called)
  * str: {child nfd name}
* Updated **create** method to take additional arguments related to segment minting (if a segment) - parent NFD ID and address to get excess proceeds from sale (segment seller) if parent was open segment.
  * additional args:
  * int: {parent app id of segment or 0}
  * addr: {open segment parent seller or zero address}

### 1.12

* Added **escrow\_offer** contract call - which is only allowed pre-claim Auction NFDs and which provides ability for users to escrow sequentially higher 'floor' bids for auctions.
* Two paired transactions in a group are required in the call.
* \[0]: payment to NFD's Account (nfdAccount property in metadata - the "Application account" natively)) of the offer.
* \[1]: Contract call to the NFD w/ 2 args:
  * **escrow\_offer**
  * **{offer amount as 8-byte Big-Endian - Itob format}**
  * fee: 1 extra txn of fee needs to be added (ie: .002 total) to cover possible inner transaction cost inside the escrow\_offer method to refund prior escrow.
* Assuming the offer is at at least+10 the current highest floor (if set), then the sender is set in the i.mintingKickoffCreator.a property and the amount is set in i.mintingKickoffAmount.
* If there was another floor already set in mintingKickoffCreator.a then they're refunded their bid as only one highest bid at a time is allowed for the 'floor' price of the dutch auction.

### 1.11

* If buyer pays more than the sell amount of the NFD, refund the difference to the buyer via an inner-transaction in the make\_offer NFD contract call. This should only happen if the user is paying for a dutch auction and the user pays the higher 'tick' amount yet the contract has just been re-priced. Think of it somewhat like slippage in DEXs. Now if the user ever overpays it just sends the excess immediately back in the same transaction. The user only pays .001 extra to cover the possible inner-transaction fee.

### 1.10

* If NFD ASA is not in seller account, check NFD account instead when clawing to the buyer.\
  The ASA can only be returned to the NFD account (the ASA's creator) if the owner 'closed the asset' - sending it back to its creator - the NFD account. In 1.08, this prevents the NFD from being sold, as the NFD will fail trying to send the asset from the seller to the buyer, as the asset won't be in the sellers account.

### 1.09

* Update contract so that NFDomains can **ONLY** *clear* userDefined fields (as part of moving to verified, or to clear metadata prior to sale).
* Previously, NFDomains could change the value even though this was never done. This change ensures it is enforced in the contract code.

### 1.08

* The GA release contract version.


# NFDomains REST API Reference

A more detailed reference will be forthcoming.

For now, please see the swagger links and the descriptions of all the APIs within it.  See [NFDomains API](/), and the subsequent quick start.

Understanding of Views when fetching an NFD is essential as well (as mentioned in [Quick Start](/quick-start#views-in-get-requests))


# Integrators Guide

Integrators, please take note of the guidance here as a consistent experience across the Algorand ecosystem is critical for users.

Make sure you're familiar with the 'views' of data as referenced at [Quick Start](/quick-start#views-in-get-requests)

Remember, NFDs are user-owned metadata on-chain, discoverable by name or Algorand address (including contract accounts !).  Users and services (with users permission) can store whatever they want in their NFDs.

Services may use NFDs as user profiles if they wish.  The user has already done the work.  Just use that instead of creating your own profile system that is isolated to just your product.

Services may have preferences or other metadata that they want to share with other protocols they control, or even the broader ecosystem.  They can provide transactions for the user to sign to update their NFD with that metadata and use that on-chain data easily via any Algorand node or smart-contract.  Decentralized on-chain storage, owned by the user.

Refer to the subsequent sections for common access use-cases.


# Using an NFD for sending assets to Algorand accounts

Wallet 'send'

{% hint style="warning" %}
TLDR: Just use the [#depositaccount-property](#depositaccount-property "mention")
{% endhint %}

Every NFD can have multiple Algorand accounts associated with it, as well as addresses for other crypto accounts, they are:

* **caAlgo\[\*]**  (**C**]rypto **A**]ccount **Algo**]rand)
  * This is really the verified 'v.caAlgo' property expanded into a top-level property for integration ease.
  * These addresses are accounts the owner had to sign for in order to add, thus they are accounts the user has keys for.
* **unverifiedCaAlgo\[\*]**&#x20;
  * This is the user-defined caalgo property expanded into a top-level property.
  * These are accounts the **owner** has entered into the NFD but for whatever reasons aren't accounts they can sign for.  It might be because they're custodial accounts (exchanges, etc.), multisig (because of poor wallet support/timing issues), or contract accounts.
  * One thing to note is, these ARE accounts the owner had to actually enter.  So there is a certain level of trust in that the owner of the NFD is saying these are valid accounts for their NFD.
    * However, they *can* reference accounts someone else owns though (!) - *so for reverse lookups (ie: NFT creator wallets)* - these have a different trust level.
* **Owner (the actual 'owner' of the NFD)**
  * This is the account that owns the NFD and is able to make changes to it, or to sell it.  Listing an NFD for sale clears all metadata and can technically happen at any time, so be careful with cache lengths.  An NFD and its app ID will never change, nor its name, but the data within it can change over time.

Based on the above, for **SENDING ALGORAND TO AN NFD NAME**, the guidance of what account to use is as follows and in the following order (only one can be chosen!).

* <mark style="color:red;">**If the NFD is for sale, or EXPIRED, do not allow sending to the NFD.**</mark>
  * The NFD state property should be "owned" before accepting deposits.  If not in 'owned state' then sending should be blocked.&#x20;
* **If caAlgo contains entries, use the FIRST account - caAlgo\[0]**.  The UI shows this to users as the deposit address.
* **If no caAlgo is set, then use unverifiedCaAlgo\[0]**.  This is still an address the owner set so it should be safe to send to.
* If caAlgo and unverifiedCaAlgo are empty, **fall back to owner.**&#x20;

### depositAccount Property

All of the above choices are resolved into a single top-level property: **depositAccount** and if using the API, simply using the depositAccount property is all that is required.

It will be set to caAlgo\[0], unverifiedCaAlgo\[0], owner, or if sending should be blocked will be missing.

{% hint style="danger" %}
**Note: If the NFD is not formally owned** (state: *owned*), or if the NFD is **expired**, the **depositAccount property will be missing** and **sending to this NFD should be blocked.**
{% endhint %}

### Using on-chain is still preferred...

{% hint style="info" %}
To fetch the name, wallets ***should*** use the on-chain mechanics as described in [Name & Address Lookup](/reference/on-chain-reference/name-and-address-lookup).  The API forces HTTPS and the domain is protected by DNSSEC, so MITM attacks shouldn't be an issue, but using independent Algorand nodes or your own nodes that you trust is probably best, ***particularly for large transfers***.

The above properties will be in v.caAlgo.0.as, u.caalgo (comma delimited), i.owner.a, and code to handle the .as \[algorand set] and possible merging of values will be necessary. &#x20;

Make note of the exception that if i.sellamt is set to block sends as the NFD is up for sale.

See the linked github repo for details.
{% endhint %}

If this isn't something easily done, then use the REST API and the Get NFD API as described below.  The 'brief' view for an NFD is sufficient to provide all of the above addresses.&#x20;

See [Quick Start](/quick-start#get-an-nfd-by-name) for more details, but an example is provided here as well.

<details>

<summary>Get name using  defaults (brief view)</summary>

```json
http https://api.nf.domains/nfd/nfdomains.algo
{
    "appID": 763844423,
    "category": "common",
    "metaTags": [
        "9_letters"
    ],
    "name": "nfdomains.algo",
    "depositAccount":  "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I",
    "nfdAccount": "FQDCZYHSK6CHLESEN76AGMSZ4JMT2H5BKWSV6NN2YAKDF22NUAJAOZ4ZIY",
    "owner": "NRLA7VZ2YV6WOS2LBS3UK25DB463XRSNG63BYNQI6CSGGROFWVQ2EKBQSI",
    "properties": {
        "userDefined": {
            "avatar": "https://images.nf.domains/avatar/9f1940b9-2cf5-43cb-96e5-f3b36385fc90"
        }
    },
    "saleType": "buyItNow",
    "state": "owned",
    "unverifiedCaAlgo": [
        "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I",
        "ABHE544MXL2CWMIZONAIUBNVELWYMKYKWBDNRLOEFQJN3LNF2ZWSMDEKBQ",
        "OMXLQTI5ZSMWTCIZA3O3YBW74BTCOI67SZTOEDHK3ZEDZ34Z3DEOQD4PW4"
    ]
}
```

</details>

##


# Using an NFD to sending to non-Algorand chains.

Fetching an NFD via the GET api will return any user-defined addresses for other chains the owner may have set.

They will each be returned as an array of addresses in the top-level property "**unverifiedCa**"\
ie:

```json
    "unverifiedCa": {
        "btc": ["xxxxxxxxxxxxxxxx"],
        "eth": ["xxxxxxxxxxxxxxxx"],
    }
```

All of these values are retrieved from the corresponding userDefined.ca.{token} property.  If the full **view** of the NFD is used, then these properties would be within the properties -> userDefined key.\
ie:

```json
{
    "appID": 763844423,
    "asaID": 763844429,
    "category": "common",
    "currentAsOfBlock": 21686682,
    "metaTags": [
        "9_letters"
    ],
    "name": "nfdomains.algo",
    "depositAccount": "RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I",
    "nfdAccount": "FQDCZYHSK6CHLESEN76AGMSZ4JMT2H5BKWSV6NN2YAKDF22NUAJAOZ4ZIY",
    "owner": "NRLA7VZ2YV6WOS2LBS3UK25DB463XRSNG63BYNQI6CSGGROFWVQ2EKBQSI",
    "properties": {
...
        "userDefined": {
...
            "ca.btc": "xxxxxx"
        }
    },
...
}

```


# Resolving an Algorand address to an NFD name / avatar

The API used for reverse address lookup is quite simple. &#x20;

1. For any given address, return the preferred verified linked address associated with that account. &#x20;
2. If there are no verified addresses, there is exactly 1 unverified address **and allowUnverified=true is passed** to the lookup endpoint, then return that.  \ <mark style="color:red;">**Integrators ideally should have some indicator to show the address is unverified !**</mark>

   \
   If more than 1 unverified match is found and only unverified, return nothing - it will be treated as if nothing was found.

The **/nfd/lookup** endpoint returns a single NFD match PER provided Address.  The 'tiny' view is used by default - with the  information returned per match.   Use other views like thumbnail to also include basic avatar info.  See [Quick Start](/quick-start#views-in-get-requests)for information on the view parameter.

If nothing is matched, a 404 (Not Found) is returned.  This is a valid return status, and also helps performance due to CDN negative caching.

{% hint style="info" %}
Up to 20 addresses can be batched per GET request.
{% endhint %}

{% hint style="warning" %}
Results are cached by the CDN for up to 2 minutes so changes to linked NFDs may appear delayed.
{% endhint %}

{% hint style="info" %}
In NFD V1:  If multiple NFDs are linked to the same address, the 'most recently changed' NFD is what's used.\
In NFD v2: The 'primary NFD' chosen by the user in the manage account dropdown is what is used for the specified address.
{% endhint %}

### REST API Call

## Reverse Address lookup with results returned per address

> Get the primary NFD for an address.  Must be verified address, or if allowUnverified is set, it may match against an unverified address

```json
{"openapi":"3.0.3","info":{"title":"NFD Management Service","version":"1.0"},"tags":[{"name":"nfd","description":"The nfd service provides apis for creating/searching/updating NFDs.  All changes occur via smart-contract calls with the endpoints returning transactions to sign for submission to an Algorand node"}],"servers":[{"url":"https://api.nf.domains"},{"url":"https://api.testnet.nf.domains"},{"url":"https://api.betanet.nf.domains"},{"url":"http://localhost:80"}],"paths":{"/nfd/lookup":{"get":{"tags":["nfd"],"summary":"Reverse Address lookup with results returned per address","description":"Get the primary NFD for an address.  Must be verified address, or if allowUnverified is set, it may match against an unverified address","operationId":"nfd#getLookup","parameters":[{"name":"address","in":"query","description":"one or more addresses (algo or otherwise) to look up, maximum of 20 can be defined.  Specify the same query parameter multiple times for each address, ie: address=xxx&address=yyy&address=zzz","allowEmptyValue":true,"required":true,"schema":{"type":"array","items":{"type":"string"},"description":"one or more addresses (algo or otherwise) to look up, maximum of 20 can be defined.  Specify the same query parameter multiple times for each address, ie: address=xxx&address=yyy&address=zzz","minItems":1,"maxItems":20}},{"name":"view","in":"query","description":"View of data to return, tiny (name, owner, caAlgo, unverifiedCaAlgo only [default]), thumbnail (tiny + avatar), brief, or full","allowEmptyValue":true,"schema":{"type":"string","description":"View of data to return, tiny (name, owner, caAlgo, unverifiedCaAlgo only [default]), thumbnail (tiny + avatar), brief, or full","default":"tiny","enum":["tiny","thumbnail","brief","full"]}},{"name":"allowUnverified","in":"query","description":"Whether to allow unverified addresses to match (and only if its only match).  Defaults to false","allowEmptyValue":true,"schema":{"type":"boolean","description":"Whether to allow unverified addresses to match (and only if its only match).  Defaults to false","default":false}},{"name":"if-none-match","in":"header","description":"etag","allowEmptyValue":true,"schema":{"type":"string","description":"etag"}}],"responses":{"200":{"description":"OK response.","headers":{"ETag":{"description":"ETag","schema":{"type":"string","description":"ETag"}},"cache-control":{"description":"Cache-Control header","schema":{"type":"string","description":"Cache-Control header"}}},"content":{"application/json":{"schema":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/NfdRecordinaddress"}}}}},"304":{"description":"ETag not changed"},"400":{"description":"invalidAddress: invalidAddress is returned for an Algorand address that doesn't appear to be valid","content":{"application/vnd.goa.error":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"notFound: Not Found response.","content":{"application/vnd.goa.error":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rateLimited: Too Many Requests response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}}}}}},"components":{"schemas":{"NfdRecordinaddress":{"type":"object","properties":{"appID":{"type":"integer","description":"NFD Application ID","format":"int64"},"asaID":{"type":"integer","description":"NFD ASA ID","format":"int64"},"avatarOutdated":{"type":"boolean","description":"Whether the verified Avatar set in this NFD is newer (arc19) then is set into the NFD. This will only be present on direct NFD fetch and if true"},"caAlgo":{"type":"array","items":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"description":"Verified Algorand addresses for this NFD"},"cache-control":{"type":"string","description":"Cache-Control header"},"category":{"type":"string","description":"Category of NFD","enum":["curated","premium","common"]},"currentAsOfBlock":{"type":"integer","description":"Round this data was last fetched from","format":"int64"},"depositAccount":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"etag":{"type":"string","description":"ETag"},"expired":{"type":"boolean"},"match-check":{"type":"string","description":"Not returned, used in tagging for response to indicate if-none-match etag matched"},"metaTags":{"type":"array","items":{"type":"string"},"description":"Tags set by the system for tracking/analytics"},"name":{"type":"string"},"nfdAccount":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"owner":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"parentAppID":{"type":"integer","description":"NFD Application ID of Parent if this is a segment","format":"int64"},"properties":{"$ref":"#/components/schemas/NFDProperties"},"reservedFor":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"saleType":{"type":"string","description":"Sale type of NFD","enum":["auction","buyItNow"]},"sellAmount":{"type":"integer","description":"amount NFD is being sold for (microAlgos)","format":"int64"},"seller":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"sigNameAddress":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"state":{"type":"string","description":"State of NFD","enum":["available","minting","reserved","forSale","owned","expired"]},"tags":{"type":"array","items":{"type":"string"},"description":"Tags assigned to this NFD"},"timeChanged":{"type":"string","format":"date-time"},"timeCreated":{"type":"string","format":"date-time"},"timeExpires":{"type":"string","format":"date-time"},"timePurchased":{"type":"string","format":"date-time"},"unverifiedCa":{"type":"object","description":"Unverified (non-algo) Crypto addresses for this NFD","additionalProperties":{"type":"array","items":{"type":"string"}}},"unverifiedCaAlgo":{"type":"array","items":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"description":"Unverified Algorand addresses for this NFD"}},"description":"NFD contains all known information about an NFD record","required":["name"]},"NFDProperties":{"type":"object","properties":{"internal":{"type":"object","description":"Internal properties","additionalProperties":{"type":"string"}},"userDefined":{"type":"object","description":"User properties","additionalProperties":{"type":"string"}},"verified":{"type":"object","description":"Verified properties","additionalProperties":{"type":"string"}}},"description":"NFDProperties contains the expanded metadata stored within an NFD contracts' global-state"},"Error":{"type":"object","properties":{"fault":{"type":"boolean","description":"Is the error a server-side fault?"},"id":{"type":"string","description":"ID is a unique identifier for this particular occurrence of the problem."},"message":{"type":"string","description":"Message is a human-readable explanation specific to this occurrence of the problem."},"name":{"type":"string","description":"Name is the name of this class of errors."},"temporary":{"type":"boolean","description":"Is the error temporary?"},"timeout":{"type":"boolean","description":"Is the error a timeout?"}},"required":["name","id","message","temporary","timeout","fault"]},"RateLimited":{"type":"object","properties":{"limit":{"type":"integer","format":"int64"},"secsRemaining":{"type":"integer","format":"int64"}},"required":["limit","secsRemaining"]}}}}
```

Example request:\
<https://api.nf.domains/nfd/lookup?address=2MQZFP7LNZNQDMJO5F3SZDFVEEHOFH6G3NXSLASIJIB7OLDPVLSCX67QZ4&address=D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA&address=T3UL3YBJZILL2WGJ5HG3EAVAR6H4ETVZ4RKFGDFBWAFZSG7PEXSQRN2NGY>

{% code title="Example response " %}

```json
{
    "2MQZFP7LNZNQDMJO5F3SZDFVEEHOFH6G3NXSLASIJIB7OLDPVLSCX67QZ4": {
        "appID": 764594795,
        "caAlgo": [
            "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA",
            "2MQZFP7LNZNQDMJO5F3SZDFVEEHOFH6G3NXSLASIJIB7OLDPVLSCX67QZ4",
            "AW3ZJNEVJKKICLA6JQ6ME64J2BOJORTYLJ7EOOAVF4ROY2QLBCZWLZOHFM"
        ],
        "depositAccount": "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA",
        "name": "goanna.algo",
        "owner": "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA",
        "properties": {},
        "state": "owned",
        "timeChanged": "2023-01-18T00:00:30Z"
    },
    "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA": {
        "appID": 764594795,
        "caAlgo": [
            "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA",
            "2MQZFP7LNZNQDMJO5F3SZDFVEEHOFH6G3NXSLASIJIB7OLDPVLSCX67QZ4",
            "AW3ZJNEVJKKICLA6JQ6ME64J2BOJORTYLJ7EOOAVF4ROY2QLBCZWLZOHFM"
        ],
        "depositAccount": "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA",
        "name": "goanna.algo",
        "owner": "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA",
        "properties": {},
        "state": "owned",
        "timeChanged": "2023-01-18T00:00:30Z"
    },
    "T3UL3YBJZILL2WGJ5HG3EAVAR6H4ETVZ4RKFGDFBWAFZSG7PEXSQRN2NGY": {
        "appID": 764582734,
        "caAlgo": [
            "T3UL3YBJZILL2WGJ5HG3EAVAR6H4ETVZ4RKFGDFBWAFZSG7PEXSQRN2NGY",
            "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA",
            "DGOANM6JL4VNSBJW737T24V4WVQINFWELRE3OKHQQFZ2JFMVKUF52D4AY4",
            "JHZUJ66WV2IN2MN7L2L6OSJ6IE3ITOTUQNKVJELYZY4IPEECPVH2KOWSRE"
        ],
        "depositAccount": "T3UL3YBJZILL2WGJ5HG3EAVAR6H4ETVZ4RKFGDFBWAFZSG7PEXSQRN2NGY",
        "name": "artgoanna.algo",
        "owner": "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA",
        "properties": {},
        "state": "owned",
        "timeChanged": "2022-11-26T23:17:11Z"
    }
}

```

{% endcode %}

{% hint style="info" %}
If you have the ability, use the image link provided in properties.userDefined.avatar, or properties.verified.avatar (special badging for 'verified' avatars is preferable but not required).  The image links may often be ipfs\://xxxxxx\[/...] links so platforms that regularly deal with ipfs links \[nft marketplaces for eg] are likely better equipped to handle this improved UX.\
The ASA ID is also a property for verified avatars if you have your own caching by ID.
{% endhint %}

### Block explorers / Transaction histories

For block explorers such as algoexplorer, goalseeker, algoscan or dApps showing transaction histories for themselves, simply use the lookup endpoint batching up to 20 addresses per request.  If your site can easily show avatars, then use the thumbnail view to get the asaid and root url of the ASA assigned as the NFDs avatar.

### General dApps, showing a 'name' for a connected Wallet.

If an NFD is returned for the connected account, show the NFD name instead of or in addition to the account.

Here's a portion of the screenshot for the NFDomains wallet dropdown demonstrating this.

![](https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2FGj7EIuhW496AAVMcDrzZ%2Fimage.png?alt=media\&token=cdd72a8d-e675-4f3e-8192-909f25043e62)

### NFT Marketplaces / Trackers

For Algorand NFTs (ASAs), sites should use the **creator** address to look up the NFD associated with that creators account.  Because NFDs allow numerous verifications, they provide the means for users to see the creator of an NFT as an NFD name they recognize and if not, click through to that NFD name, and see all the additional information the creator added to their NFD to prove they are the real project creator/owner.

As this becomes prevalent, users can easily know that a particular NFT is a fraud, because the image from a known collection isn't shown as being created by that well known NFD, but instead a random address (or possibly some 'lookalike' NFD name, with nothing in it matching the twitter / website / discord etc of the creator).


# Fetching all NFDs owned by an address

In order to fetch all NFDs owned by a particular account, use the v2/search api on the account, searching on owner.\
eg: Showing all NFDs owned by the nfdomains account.

<https://api.nf.domains/nfd/v2/search?owner=RSV2YCHXA7MWGFTX3WYI7TVGAS5W5XH5M7ZQVXPPRQ7DNTNW36OW2TRR6I&state=owned>

This will return the first 50 (by default) NFDs owned (not expired or for-sale) by this NFD.  The limit parameter can be changed to a maximum of 200.  To iterate, set offset accordingly to walk the results.

## Search NFDs via various filters

> Search NFDs via various filters

```json
{"openapi":"3.0.3","info":{"title":"NFD Management Service","version":"1.0"},"tags":[{"name":"nfd","description":"The nfd service provides apis for creating/searching/updating NFDs.  All changes occur via smart-contract calls with the endpoints returning transactions to sign for submission to an Algorand node"}],"servers":[{"url":"https://api.nf.domains"},{"url":"https://api.testnet.nf.domains"},{"url":"https://api.betanet.nf.domains"},{"url":"http://localhost:80"}],"paths":{"/nfd/v2/search":{"get":{"tags":["nfd"],"summary":"Search NFDs via various filters","description":"Search NFDs via various filters","operationId":"nfd#searchV2","parameters":[{"name":"name","in":"query","description":"name or partial match of NFD name to filter on","allowEmptyValue":true,"schema":{"type":"string","description":"name or partial match of NFD name to filter on"}},{"name":"category","in":"query","allowEmptyValue":true,"schema":{"type":"array","items":{"type":"string","description":"Category of NFD","enum":["curated","premium","common"]}}},{"name":"saleType","in":"query","allowEmptyValue":true,"schema":{"type":"array","items":{"type":"string","description":"Sale type of NFD","enum":["auction","buyItNow"]}}},{"name":"state","in":"query","allowEmptyValue":true,"schema":{"type":"array","items":{"type":"string","description":"State of NFD","enum":["reserved","forSale","owned","expired"]}}},{"name":"parentAppID","in":"query","description":"The parent NFD Application ID to find. Used for fetching segments of an NFD","allowEmptyValue":true,"schema":{"type":"integer","description":"The parent NFD Application ID to find. Used for fetching segments of an NFD","format":"int64"}},{"name":"length","in":"query","description":"Length of NFD","allowEmptyValue":true,"schema":{"type":"array","items":{"type":"string","enum":["1_letters","2_letters","3_letters","4_letters","5_letters","6_letters","7_letters","8_letters","9_letters","10+_letters"]},"description":"Length of NFD"}},{"name":"traits","in":"query","description":"Traits of NFD","allowEmptyValue":true,"schema":{"type":"array","items":{"type":"string","enum":["emoji","pristine","segment"]},"description":"Traits of NFD"}},{"name":"owner","in":"query","description":"An Algorand account address to find all NFDs owned by that address","allowEmptyValue":true,"schema":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"}},{"name":"reservedFor","in":"query","description":"An Algorand account address to find all NFDs reserved for that address","allowEmptyValue":true,"schema":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"}},{"name":"excludeUserReserved","in":"query","description":"Should NFDs reserved for an account (transfers for example or unclaimed winning auctions) be excluded","allowEmptyValue":true,"schema":{"type":"boolean","description":"Should NFDs reserved for an account (transfers for example or unclaimed winning auctions) be excluded"}},{"name":"prefix","in":"query","description":"The start of an NFD name, fetching multiple NFDs that have that prefix","allowEmptyValue":true,"schema":{"type":"string","description":"The start of an NFD name, fetching multiple NFDs that have that prefix","minLength":1,"maxLength":32}},{"name":"substring","in":"query","description":"Part of an NFD name, fetching multiple NFDs that have that substring (minimum 3 characters)","allowEmptyValue":true,"schema":{"type":"string","description":"Part of an NFD name, fetching multiple NFDs that have that substring (minimum 3 characters)","minLength":3,"maxLength":32}},{"name":"vproperty","in":"query","description":"Verified property name to search on - specify value with vvalue","allowEmptyValue":true,"schema":{"type":"string","description":"Verified property name to search on - specify value with vvalue","enum":["blueskydid","discord","telegram","twitter","github","email","domain","nostrpubkey"]}},{"name":"vvalue","in":"query","description":"Value to find in the vproperty field specified with the vproperty parameter","allowEmptyValue":true,"schema":{"type":"string","description":"Value to find in the vproperty field specified with the vproperty parameter"}},{"name":"segmentLocked","in":"query","description":"Whether to explicitly filter on segments being locked or unlocked.  Typically only valuable when filtering on unlocked","allowEmptyValue":true,"schema":{"type":"boolean","description":"Whether to explicitly filter on segments being locked or unlocked.  Typically only valuable when filtering on unlocked"}},{"name":"segmentRoot","in":"query","description":"Whether to explicitly filter on NFD roots or segments.  True to only see roots, False to only see segments.","allowEmptyValue":true,"schema":{"type":"boolean","description":"Whether to explicitly filter on NFD roots or segments.  True to only see roots, False to only see segments."}},{"name":"minPrice","in":"query","description":"Minimum price of NFD","allowEmptyValue":true,"schema":{"type":"integer","description":"Minimum price of NFD","format":"int64"}},{"name":"maxPrice","in":"query","description":"Maximum price of NFD","allowEmptyValue":true,"schema":{"type":"integer","description":"Maximum price of NFD","format":"int64"}},{"name":"minPriceUsd","in":"query","description":"Minimum price of NFD Segment in USD (cents)","allowEmptyValue":true,"schema":{"type":"integer","description":"Minimum price of NFD Segment in USD (cents)","format":"int64"}},{"name":"maxPriceUsd","in":"query","description":"Maximum price of NFD Segment in USD (cents)","allowEmptyValue":true,"schema":{"type":"integer","description":"Maximum price of NFD Segment in USD (cents)","format":"int64"}},{"name":"changedAfter","in":"query","description":"Fetch NFDs that changed after the specified timestamp","allowEmptyValue":true,"schema":{"type":"string","description":"Fetch NFDs that changed after the specified timestamp","format":"date-time"}},{"name":"expiresBefore","in":"query","description":"Return only NFDs with an expiration time at or before the specified timestamp","allowEmptyValue":true,"schema":{"type":"string","description":"Return only NFDs with an expiration time at or before the specified timestamp","format":"date-time"}},{"name":"limit","in":"query","description":"Limit the number of results returned - max 200","allowEmptyValue":true,"schema":{"type":"integer","description":"Limit the number of results returned - max 200","default":50,"format":"int64","minimum":1,"maximum":200}},{"name":"offset","in":"query","description":"Starting document in large list.  Fetch 1-100 [limit 100], pass offset 100 to fetch 100-200","allowEmptyValue":true,"schema":{"type":"integer","description":"Starting document in large list.  Fetch 1-100 [limit 100], pass offset 100 to fetch 100-200","default":0,"format":"int64","minimum":0}},{"name":"sort","in":"query","description":"What to sort on","allowEmptyValue":true,"schema":{"type":"string","description":"What to sort on","default":"createdDesc","enum":["createdDesc","timeChangedDesc","soldDesc","priceAsc","priceDesc","highestSaleDesc","saleTypeAsc","nameAsc","expiresAsc","expiresDesc"]}},{"name":"view","in":"query","description":"View of data to return, tiny (name, owner, caAlgo, unverifiedCaAlgo only), brief (default), or full","allowEmptyValue":true,"schema":{"type":"string","description":"View of data to return, tiny (name, owner, caAlgo, unverifiedCaAlgo only), brief (default), or full","default":"brief","enum":["tiny","thumbnail","brief","full"]}},{"name":"if-none-match","in":"header","description":"etag","allowEmptyValue":true,"schema":{"type":"string","description":"etag"}}],"responses":{"200":{"description":"OK response.","headers":{"ETag":{"description":"ETag","schema":{"type":"string","description":"ETag"}},"cache-control":{"description":"Cache-Control header","schema":{"type":"string","description":"Cache-Control header"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NfdV2SearchRecords"}}}},"304":{"description":"ETag not changed"},"400":{"description":"invalidAddress: invalidAddress is returned for an Algorand address that doesn't appear to be valid","content":{"application/vnd.goa.error":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"notFound: Not Found response.","content":{"application/vnd.goa.error":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"rateLimited: Too Many Requests response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimited"}}}}}}}},"components":{"schemas":{"NfdV2SearchRecords":{"type":"object","properties":{"cache-control":{"type":"string","description":"Cache-Control header"},"etag":{"type":"string","description":"ETag"},"match-check":{"type":"string","description":"Not returned, used in tagging for response to indicate if-none-match etag matched"},"nfds":{"$ref":"#/components/schemas/NfdRecordCollection"},"total":{"type":"integer","description":"total number of results, with data containing paged amount based on offset/limit","format":"int64"}},"description":"Collection of NFD browse results","required":["total","nfds"]},"NfdRecordCollection":{"type":"array","items":{"$ref":"#/components/schemas/NfdRecord"}},"NfdRecord":{"type":"object","properties":{"appID":{"type":"integer","description":"NFD Application ID","format":"int64"},"asaID":{"type":"integer","description":"NFD ASA ID","format":"int64"},"avatarOutdated":{"type":"boolean","description":"Whether the verified Avatar set in this NFD is newer (arc19) then is set into the NFD. This will only be present on direct NFD fetch and if true"},"caAlgo":{"type":"array","items":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"description":"Verified Algorand addresses for this NFD"},"cache-control":{"type":"string","description":"Cache-Control header"},"category":{"type":"string","description":"Category of NFD","enum":["curated","premium","common"]},"currentAsOfBlock":{"type":"integer","description":"Round this data was last fetched from","format":"int64"},"depositAccount":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"etag":{"type":"string","description":"ETag"},"expired":{"type":"boolean"},"match-check":{"type":"string","description":"Not returned, used in tagging for response to indicate if-none-match etag matched"},"metaTags":{"type":"array","items":{"type":"string"},"description":"Tags set by the system for tracking/analytics"},"name":{"type":"string"},"nfdAccount":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"owner":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"parentAppID":{"type":"integer","description":"NFD Application ID of Parent if this is a segment","format":"int64"},"properties":{"$ref":"#/components/schemas/NFDProperties"},"reservedFor":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"saleType":{"type":"string","description":"Sale type of NFD","enum":["auction","buyItNow"]},"sellAmount":{"type":"integer","description":"amount NFD is being sold for (microAlgos)","format":"int64"},"seller":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"sigNameAddress":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"state":{"type":"string","description":"State of NFD","enum":["available","minting","reserved","forSale","owned","expired"]},"tags":{"type":"array","items":{"type":"string"},"description":"Tags assigned to this NFD"},"timeChanged":{"type":"string","format":"date-time"},"timeCreated":{"type":"string","format":"date-time"},"timeExpires":{"type":"string","format":"date-time"},"timePurchased":{"type":"string","format":"date-time"},"unverifiedCa":{"type":"object","description":"Unverified (non-algo) Crypto addresses for this NFD","additionalProperties":{"type":"array","items":{"type":"string"}}},"unverifiedCaAlgo":{"type":"array","items":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"description":"Unverified Algorand addresses for this NFD"}},"description":"NFD contains all known information about an NFD record","required":["name"]},"NFDProperties":{"type":"object","properties":{"internal":{"type":"object","description":"Internal properties","additionalProperties":{"type":"string"}},"userDefined":{"type":"object","description":"User properties","additionalProperties":{"type":"string"}},"verified":{"type":"object","description":"Verified properties","additionalProperties":{"type":"string"}}},"description":"NFDProperties contains the expanded metadata stored within an NFD contracts' global-state"},"Error":{"type":"object","properties":{"fault":{"type":"boolean","description":"Is the error a server-side fault?"},"id":{"type":"string","description":"ID is a unique identifier for this particular occurrence of the problem."},"message":{"type":"string","description":"Message is a human-readable explanation specific to this occurrence of the problem."},"name":{"type":"string","description":"Name is the name of this class of errors."},"temporary":{"type":"boolean","description":"Is the error temporary?"},"timeout":{"type":"boolean","description":"Is the error a timeout?"}},"required":["name","id","message","temporary","timeout","fault"]},"RateLimited":{"type":"object","properties":{"limit":{"type":"integer","format":"int64"},"secsRemaining":{"type":"integer","format":"int64"}},"required":["limit","secsRemaining"]}}}}
```

## The NfdV2SearchRecords object

```json
{"openapi":"3.0.3","info":{"title":"NFD Management Service","version":"1.0"},"components":{"schemas":{"NfdV2SearchRecords":{"type":"object","properties":{"cache-control":{"type":"string","description":"Cache-Control header"},"etag":{"type":"string","description":"ETag"},"match-check":{"type":"string","description":"Not returned, used in tagging for response to indicate if-none-match etag matched"},"nfds":{"$ref":"#/components/schemas/NfdRecordCollection"},"total":{"type":"integer","description":"total number of results, with data containing paged amount based on offset/limit","format":"int64"}},"description":"Collection of NFD browse results","required":["total","nfds"]},"NfdRecordCollection":{"type":"array","items":{"$ref":"#/components/schemas/NfdRecord"}},"NfdRecord":{"type":"object","properties":{"appID":{"type":"integer","description":"NFD Application ID","format":"int64"},"asaID":{"type":"integer","description":"NFD ASA ID","format":"int64"},"avatarOutdated":{"type":"boolean","description":"Whether the verified Avatar set in this NFD is newer (arc19) then is set into the NFD. This will only be present on direct NFD fetch and if true"},"caAlgo":{"type":"array","items":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"description":"Verified Algorand addresses for this NFD"},"cache-control":{"type":"string","description":"Cache-Control header"},"category":{"type":"string","description":"Category of NFD","enum":["curated","premium","common"]},"currentAsOfBlock":{"type":"integer","description":"Round this data was last fetched from","format":"int64"},"depositAccount":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"etag":{"type":"string","description":"ETag"},"expired":{"type":"boolean"},"match-check":{"type":"string","description":"Not returned, used in tagging for response to indicate if-none-match etag matched"},"metaTags":{"type":"array","items":{"type":"string"},"description":"Tags set by the system for tracking/analytics"},"name":{"type":"string"},"nfdAccount":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"owner":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"parentAppID":{"type":"integer","description":"NFD Application ID of Parent if this is a segment","format":"int64"},"properties":{"$ref":"#/components/schemas/NFDProperties"},"reservedFor":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"saleType":{"type":"string","description":"Sale type of NFD","enum":["auction","buyItNow"]},"sellAmount":{"type":"integer","description":"amount NFD is being sold for (microAlgos)","format":"int64"},"seller":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"sigNameAddress":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"state":{"type":"string","description":"State of NFD","enum":["available","minting","reserved","forSale","owned","expired"]},"tags":{"type":"array","items":{"type":"string"},"description":"Tags assigned to this NFD"},"timeChanged":{"type":"string","format":"date-time"},"timeCreated":{"type":"string","format":"date-time"},"timeExpires":{"type":"string","format":"date-time"},"timePurchased":{"type":"string","format":"date-time"},"unverifiedCa":{"type":"object","description":"Unverified (non-algo) Crypto addresses for this NFD","additionalProperties":{"type":"array","items":{"type":"string"}}},"unverifiedCaAlgo":{"type":"array","items":{"type":"string","description":"An Algorand Account address","pattern":"^[A-Z2-7]{57}[AEIMQUY4]$"},"description":"Unverified Algorand addresses for this NFD"}},"description":"NFD contains all known information about an NFD record","required":["name"]},"NFDProperties":{"type":"object","properties":{"internal":{"type":"object","description":"Internal properties","additionalProperties":{"type":"string"}},"userDefined":{"type":"object","description":"User properties","additionalProperties":{"type":"string"}},"verified":{"type":"object","description":"Verified properties","additionalProperties":{"type":"string"}}},"description":"NFDProperties contains the expanded metadata stored within an NFD contracts' global-state"}}}}
```


# Interactive NFD lookup

## 'Looking up' an NFD interactively (debounce)

If you want to provide interactive search features for an NFD.  You can use the NFD 'search' endpoint using the prefix query parameter.

The endpoint is the /nfd/v2/search endpoint.  It takes many query parameters but only certain types should be specified at one time.

ie: prefix, but not owner or reservedFor. &#x20;

{% openapi src="/files/LUX7Ji30E1eiEMcVZkUr" path="/nfd/v2/search" method="get" %}
[openapi3.yaml](https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2FW7CFlA4K7snWCT3liMou%2Fopenapi3.yaml?alt=media\&token=0490decf-f3b6-4911-88c6-ed5a87e7872d)
{% endopenapi %}

### Example

<details>

<summary>Get first 4 matching names by prefix (forced 'thumbnail' view)</summary>

```json
http https://api.nf.domains/nfd/v2/search?prefix=patric&view=thumbnail&limit=4

{
    "nfds": [
        {
            "appID": 1184583458,
            "depositAccount": "MQ2QJHZSZ6A7ZXPFE2EPIWLYUMRRDO3DQBEO6NIQ2B5A5OJ4VMWOOI2AX4",
            "name": "patrick.voi.algo",
            "owner": "MQ2QJHZSZ6A7ZXPFE2EPIWLYUMRRDO3DQBEO6NIQ2B5A5OJ4VMWOOI2AX4",
            "parentAppID": 984682143,
            "properties": {},
            "state": "owned"
        },
        {
            "appID": 1016975892,
            "depositAccount": "SAC732VSDG3J7ZVDOIV7I5Y3REVCYB2EPMNU6PRLJY67L37YOARYSD2PDM",
            "name": "patricia.eth.algo",
            "owner": "SAC732VSDG3J7ZVDOIV7I5Y3REVCYB2EPMNU6PRLJY67L37YOARYSD2PDM",
            "parentAppID": 1016578024,
            "properties": {},
            "state": "owned"
        },
        {
            "appID": 1016975648,
            "depositAccount": "SAC732VSDG3J7ZVDOIV7I5Y3REVCYB2EPMNU6PRLJY67L37YOARYSD2PDM",
            "name": "patricia.btc.algo",
            "owner": "SAC732VSDG3J7ZVDOIV7I5Y3REVCYB2EPMNU6PRLJY67L37YOARYSD2PDM",
            "parentAppID": 1016575127,
            "properties": {},
            "state": "owned"
        },
        {
            "appID": 1016602998,
            "depositAccount": "MQ2QJHZSZ6A7ZXPFE2EPIWLYUMRRDO3DQBEO6NIQ2B5A5OJ4VMWOOI2AX4",
            "name": "patrick.btc.algo",
            "owner": "MQ2QJHZSZ6A7ZXPFE2EPIWLYUMRRDO3DQBEO6NIQ2B5A5OJ4VMWOOI2AX4",
            "parentAppID": 1016575127,
            "properties": {},
            "state": "owned"
        }
    ],
    "total": 10
}
```

</details>

The thumbnail view will return verified and userDefined avatars for names.  This way, the UI can show avatars with choices as well, as seen here in this NFDomains search UI example:

![](https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2FgZ59UiE1pgHCiUOzuYyr%2Fimage.png?alt=media\&token=e1e7d2cd-a011-4dc1-8c8c-513f86caf922)

Note the verified.**avatarasaid** property in this example (same exists for banner, **bannerasaid** when fetching with other views).  For integrations that manage their own ASA caching, this may make it easier to reference already cached versions of the NFT image if the ASA id is used instead of IPFS CID references.


# Discord / Telegram bots

If you have a project using Discord or Telegram for verification features (ie: possession of specific NFTs / or permission tokens) then NFDs are an excellent way to let users get access without having to do anything other than provide their NFD name !

Users can verify their Discord and Telegram accounts via NFDs.  If users perform this verification, their ID will be stored as a 'verified' property in their NFD and provide a trivial way of *sign on w/ proven on-chain metadata*.&#x20;

#### Verifying a Discord handle

{% embed url="<https://youtu.be/XxjbQMeIrLY?t=457>" %}

Once a user has verified their Discord ID, their Discord 'snowflake' ID will be set in properties.verified.discord in the 'full' view of an NFD fetch.

To use NFDs for verification, a bot simply needs to use the users' snowflake ID and find all NFDs that have that ID as a verified discord handle.  You will be able to know the Algorand accounts are all theirs, no webpage has to be popped, nor transactions to sign.

The bot should query the API (See [NFDomains REST API Reference](/reference/nfdomains-rest-api-reference)), the /nfd/v2/search?vproperty=discord\&vvalue={snowflakeid} endpoint specifically (with the 'full' view), for the id of the user.

{% openapi src="/files/LUX7Ji30E1eiEMcVZkUr" path="/nfd/v2/search" method="get" %}
[openapi3.yaml](https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2FW7CFlA4K7snWCT3liMou%2Fopenapi3.yaml?alt=media\&token=0490decf-f3b6-4911-88c6-ed5a87e7872d)
{% endopenapi %}

If NFD(s) are returned for that discord ID, then all the 'related' accounts of those NFDs can be examined.&#x20;

These are:

* Owner - the owner of the account is guaranteed and can't be impersonated.
* nfdAccount - this is the account address of the NFD itself.  NFDs can contain assets and this will come into play more in the future.
* caAlgo\[\*] - the list of accounts in the caAlgo array are the accounts the user has explicitly linked to their NFD and all are accounts the user can sign for. &#x20;

All of these accounts can be searched by the bot to check for ownership of membership assets (like owning a particular NFT or other membership token).  If found in any of these accounts, then the discord user (which owns that NFD) can be assigned specific roles which require proof of ownership.

Because users can either sell their NFDs, or the assets which gave them access, it is suggested that bots store the discord id and periodically reassert the above search/account checks.  If the user either sells their asset or perhaps links additional accounts that grant additional rights, the bot should remove or add roles as appropriate.


# Linking an Application to an NFD

{% hint style="info" %}
This requires NFDs at contract 2.11 or higher
{% endhint %}

Contracts can now verify their account address with an NFD.  There is no direct API for this as the contract that wants to verify its address must make the actual call via an inner transaction to the NFD.

The verification process is a 2-step process.

* First, the OWNER of the NFD needs to update the user-defined property (u.cav.algo.a) with the raw 32-byte PK of the contract's account address (the Algorand GetApplicationAddress SDK for eg).  The NFD API can be used to get the transaction for this, or you can update it directly using a contract call.
* Second, once u.cav.algo.a is set, you next need to call the NFD Registry contract (see [Registry Application IDs](/reference/on-chain-reference/registry-application-ids)) to 'verify' the address in the NFD **FROM THE CONTRACT BEING VERIFIED.**\
  The NFD can verify its own vault contract address if desired.  In this case, the Owner of the NFD is allowed to verify the vault address.
* The Reti code that does this is probably the best example.
* To **set** the address to be verified. See this section of the Reti UI (the linkPoolToNfd helper function):
  * <https://github.com/algorandfoundation/reti/blob/main/ui/src/api/contracts.ts#L1067>
  * Notice it pays the MBR, updates the u.cav.algo.a field, then calls the Reti Staking Pool contract's '**linkToNfd**' ABI method.
  * The **linkToNfd** method Tealscript code is here: <https://github.com/algorandfoundation/reti/blob/main/contracts/contracts/stakingPool.algo.ts#L920>


# Using Vaults (2.x+)

In this guide we will provide an example of how to use the NFDomains API to send assets to and from an NFD's vault, as a reference for adding Vault support in your own application.

## Overview

A vault is an Algorand account controlled by an NFD's smart contract that can automatically opt-in to assets it receives.

The vault account is the 'nfdAccount' property in all NFDs.  It is the contract account of the NFD itself.  As each NFD is a distinct contract instance, each NFD has its own account - its vault.

Sending TO a vault (or account) using the API will check if the receiver is already opted-in and just provide a transfer transaction instead.  If the receivers vault is not opted-in and is unlocked (or you're the owner of the vault), then the API returns transactions to have the vault opt-in.  The transactions will pay the MBR (.1 per asset) of the assets being sent, and then `vault_optin` calls to the vault to opt-in each of the assets, followed by asset transfers to the vault.

Sending FROM a vault follows a similar process, with the distinction that the `receiverType` must be designated as either a standard `'account'` or another `'nfdVault'`.

Vaults are locked by default, meaning only the NFD's owner can send assets to it. When the owner unlocks the vault, anyone can send assets to it. The owner can lock or unlock the vault at any time.

## API

{% openapi src="/files/qti34SRFix74YjY9eJRa" path="/nfd/vault/sendFrom/{name}" method="post" expanded="false" %}
[nfdv2.yaml](https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2FwTi52L8cvytTuEXepzcX%2Fnfdv2.yaml?alt=media\&token=91a76476-4c1b-4df1-b734-d6984f189e11)
{% endopenapi %}

{% openapi src="/files/qti34SRFix74YjY9eJRa" path="/nfd/vault/sendTo/{name}" method="post" expanded="false" %}
[nfdv2.yaml](https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2FwTi52L8cvytTuEXepzcX%2Fnfdv2.yaml?alt=media\&token=91a76476-4c1b-4df1-b734-d6984f189e11)
{% endopenapi %}

## TypeScript Example

### Setup

In this example we will create two very basic React components that will prompt the user to sign transactions for sending an asset to and from an NFD's vault.

We will use Axios for handling HTTP requests to fetch the transactions, and the `@txnlab/use-wallet` library to sign and send the transactions. Make sure these libraries are installed in your project:

```bash
npm install axios @txnlab/use-wallet
```

**Note:** `@txnlab/use-wallet` is not required, but it is used in this example to simplify the process of signing and sending transactions. See <https://github.com/TxnLab/use-wallet> for full setup instructions.

First we define functions for making requests to the NFD Vaults API using Axios. The `sendToVault` and `sendFromVault` functions will make POST requests to the `/nfd/vault/sendTo/{name}` and `/nfd/vault/sendFrom/{name}` endpoints, respectively.

```ts
// src/api.ts

import axios from 'axios'

const API_BASE_URL = 'https://api.nf.domains'

interface SendToVaultRequestBody {
  /* Base amount (in base units of specified asset - so decimals must be considered) of asset to
  send. If multiple assets specified, amount is ignored and ALL of each are sent */
  amount: number
  /* Algorand ASA IDs to transfer (and opt-in inside vault if necessary) - use asset 0 to send
  ALGO. Specifying multiple assets means ALL of each are sent and amount is ignored. 13 is max
  assets that can be specified if they're being sent (2 for MBR payments, 2 for opt-in txns (8+4
  asset opt-ins), 12 asset transfers). If opt-in only then 64 is maximum (1 MBR per 8 assets, 8
  assets per txn * 8 txns) */
  assets: number[]
  /* Optional note to include in asset send transaction */
  note?: string
  /* Whether to only opt-in to the asset, instead of including asset transfer txn */
  optInOnly: boolean
  /* Sender of transaction, an Algorand account */
  sender: string
}

export function sendToVault(name: string, data: SendToVaultRequestBody) {
  return axios<string>({
    url: `${API_BASE_URL}/nfd/vault/sendTo/${name}`,
    method: 'post',
    headers: { 'Content-Type': 'application/json' },
    data
  })
}

interface SendFromVaultRequestBody {
  amount: number
  /* Algorand ASA IDs to transfer FROM vault - use asset 0 to send ALGO. Specifying multiple assets
  means ALL of each are sent and amount is ignored. If receiver is a vault and needs to opt-in,
  then need MBR/opt-in pairs (5 pairs - 8 opt-ins each - 40 assets), then 6 send calls of 7 assets
  w/ 5 at end for total of 40. If receiver is already opted-in, then 112 (7 per txn, 16 tnxs) is
  max. */
  assets: number[]
  /* Optional note to include in asset send transaction */
  note?: string
  /* Algorand account or NFD Name (if vault receiver) the asset(s) should be sent to */
  receiver: string
  /* Specifies that the receiver account is something the caller can sign for. If specified, then
  opt-in transactions it signs may be included */
  receiverCanSign?: boolean
  /* Account or NFD Vault the asset should be sent to (if allowed) */
  receiverType?: 'account' | 'nfdVault'
  /* Sender of transaction, must be NFD owner */
  sender: string
}

export function sendFromVault(name: string, data: SendFromVaultRequestBody) {
  return axios<string>({
    url: `${API_BASE_URL}/nfd/vault/sendFrom/${name}`,
    method: 'post',
    headers: { 'Content-Type': 'application/json' },
    data
  })
}
```

### Send To Vault

Using the `sendToVault` function, we create a component that prompts the user to sign and send transactions for sending an asset to an NFD's vault.

* If the sender is not the owner, the receiving vault must be **unlocked** to auto opt-in and receive assets
* If the vault is locked, only the NFD owner can send assets to it
* The NFD must be upgraded to smart contract version 2.6 or higher to receive vault assets

```tsx
// src/sendToVault.tsx

import React from 'react'
import {
  useWallet,
  encodeNFDTransactionsArray,
  TransactionsArray
} from '@txnlab/use-wallet'
import { sendToVault } from './api'

export default function SendToVaultDemo() {
  const wallet = useWallet()
  const { activeAddress } = wallet

  async function signAndSendTransactions() {
    const NFD_NAME = 'doug.algo'
    const ASSET_ID = 212389838
    const AMOUNT = 1

    try {
      if (!activeAddress) {
        throw new Error('No account connected')
      }

      const response = await sendToVault(NFD_NAME, {
        sender: activeAddress,
        assets: [ASSET_ID],
        amount: AMOUNT,
        optInOnly: false
      })

      if (typeof response.data !== 'string') {
        throw new Error('Failed to fetch transactions')
      }

      const transactionsArray = JSON.parse(response.data) as TransactionsArray
      const signedTransactions = await wallet.signTransactions(
        encodeNFDTransactionsArray(transactionsArray)
      )

      const { id } = await wallet.sendTransactions(signedTransactions)

      console.log(
        `Successfully sent asset ${ASSET_ID} to ${NFD_NAME}'s vault!`,
        `Transaction ID: ${id}`
      )
    } catch (error) {
      console.error(`Send to vault failed`, error)
    }
  }

  if (!activeAddress) {
    return <p>Connect an account first.</p>
  }

  return (
    <div>
      <button type="button" onClick={signAndSendTransactions}>
        Sign and send transactions
      </button>
    </div>
  )
}
```

### Send From Vault

Using the `sendFromVault` function, we create a component that prompts the user to sign and send transactions for sending an asset from the vault of an NFD they own.

* The transactions must be signed by the NFD's `owner` account
* The `receiver` can be either an Algorand account or another NFD's vault (same rules described [above](#send-to-vault) apply to a receiving vault)
* The NFD must be upgraded to smart contract version 2.6 or higher

```tsx
// src/sendFromVault.tsx

import React from 'react'
import {
  useWallet,
  encodeNFDTransactionsArray,
  TransactionsArray
} from '@txnlab/use-wallet'
import { sendFromVault } from './api'

export default function SendFromVaultDemo() {
  const wallet = useWallet()
  const { activeAddress } = wallet // must match the NFD's `owner` account

  async function signAndSendTransactions() {
    const NFD_NAME = 'doug.algo'
    const ASSET_ID = 212389838
    const AMOUNT = 1
    const RECEIVER = 'receiver-address' // replace with valid Algorand account

    try {
      if (!activeAddress) {
        throw new Error('No account connected')
      }

      const response = await sendFromVault(NFD_NAME, {
        assets: [ASSET_ID],
        amount: AMOUNT,
        receiver: RECEIVER,
        receiverType: 'account'
      })

      if (typeof response.data !== 'string') {
        throw new Error('Failed to fetch transactions')
      }

      const transactionsArray = JSON.parse(response.data) as TransactionsArray
      const signedTransactions = await wallet.signTransactions(
        encodeNFDTransactionsArray(transactionsArray)
      )

      const { id } = await wallet.sendTransactions(signedTransactions)

      console.log(
        `Successfully sent asset ${ASSET_ID} from ${NFD_NAME}'s vault!`,
        `Transaction ID: ${id}`
      )
    } catch (error) {
      console.error(`Send from vault failed`, error)
    }
  }

  if (!activeAddress) {
    return <p>Connect an account first.</p>
  }

  return (
    <div>
      <button type="button" onClick={signAndSendTransactions}>
        Sign and send transactions
      </button>
    </div>
  )
}
```

## Sequence diagram of calls

<div data-full-width="true"><figure><img src="https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2FXmTFfoQfDJ1kWzrYisoP%2Fvaults.png?alt=media&amp;token=697656bf-1e05-470c-b1c9-0b3c6b690f6e" alt=""><figcaption></figcaption></figure></div>

## Resources

* [Vaults Documentation](https://docs.nf.domains/guides/manage-nfds/vaults)
* [NFDomains API Documentation](https://api-docs.nf.domains/)
* [Mainnet API Swagger Docs](https://editor.swagger.io/?url=https://api.nf.domains/info/openapi3.yaml)


# Expirations  / MBR liquidation

In order for someone to become the new owner of an NFD once it has expired, all metadata must first be cleared.  This is similar to listing an NFD for sale, where all metadata must first be cleared.

An exception with expired NFDs, is that ANYONE can clear the metadata of an NFD if its expired.  Anything deleting the fields of an NFD via the new `deleteFields` ABI call, will receive the MBR held by the box storage being freed.  The same applies to calling the registry to `unlinkNfdAddress` method - returning the box storage MBR if no data remains once unlinking.

This allows a type of 'liquidation' feature where on-chain NFD data can be cleared once the NFD expires by providing an incentive for anyone to take advantage of.

If an NFD isn't cleared, then the UI adds deleteField calls prior to the renew so that the new owner is able to claim the expired NFD.


# API Changelog

The features or responses in the API may change regularly. This will not be exhaustive or include every change, but as there are notable changes made they will be listed here.

Versions referenced are part of /info/version endpoint.

### 2025/04/17

* v2/search with vproperty query parameter WITHOUT a vvalue will find NFDs where that verified property has ANY value.  ie: v2/search?owner=xxx\&vproperty=twitter would find all nfds with the verified twiter field set to anything.

### 2024/10/11

* added expiresDesc sort option to v2/search

### 2024/10/08

* Added 'expiresBefore' query parameter to v2/search endpoint.  Allows returning filtered list based on NFDs expiring at or before the specified timestamp.

### 2024/09/25

* Removed rejectNft option from purchase endpoint.  The NFT is always rejected now starting w/ V3. &#x20;

### 2024/09/24

* Removed reLockAt argument to vault opt-in lock.  Supported in 2.x but nothing used it and it wasn't exposed in the UI, so removed in 3.x and API.

### 2024/09

* A new **state** value of 'expired' will be set for NFDs which have expired.  They should be treated like they are for sale.  The depositAccount property will not be returned for NFDs in this state.  Wallets should not send to an NFD in this state (the missing depositAccount property is the clear sign - just like when for sale)
* NFD v3 contains changes to many endpoints - although mostly those used by the app only as part of minting or pricing.&#x20;
* The /nfd/vault/sendFrom/{name} and /nfd/vault/sendTo/{name} endpoints have a new amountStr property so that JS clients can pass integers larger than the Number type allows (bigint).  Passing the amounts as a string instead of integer is only way to support broken implementations like this.
* Minting an NFD is now through the the /nfd/mint endpoint and /nfd/kickoff has been removed.  Because of AVM limitations the linkOnMint option can't be used yet.

### 2024/01/03

* The *brief* view for NFDs will now return the internal ver(sion) and vaultOptInLocked state to help determine vault availability in NFD prior to making vault contract calls.

### 2023/06

* Added new /nfd/lookup endpoint for reverse lookups.  For almost all reverse-lookup scenarios this is what should be used.  The address endpoints (original and v2/) will have more aggressive rate-limits imposed to discourage their use. &#x20;

{% openapi src="/files/LUX7Ji30E1eiEMcVZkUr" path="/nfd/lookup" method="get" %}
[openapi3.yaml](https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2FW7CFlA4K7snWCT3liMou%2Fopenapi3.yaml?alt=media\&token=0490decf-f3b6-4911-88c6-ed5a87e7872d)
{% endopenapi %}

### 2023/04 - 2023/09&#x20;

#### 2.x(+) Contract updates

* All contracts older than 2.x will be forced to be upgraded to 2.x to be edited through the API/UI. This is to provide a clean migration path from 1.x to 2.x contract storage. Upgrading the contract migrates registration of the name and linked addresses to box storage in the registry from LSIGs as well as all user-defined/verified fields from state to boxes. The system will still handle reading V1 and V2 NFDs.
* All user-defined and verified properties are now stored using Boxes in the NFD contract.  The transactions returned from the API will automatically include payments to the registry contract account or the NFD contract account (its vault) to pay fo the box storage.  Box storage is basically 'pay as you go' per box and per byte.

#### New API endpoints for Vault operations

* Added **/nfd/vault/lock/{name}** for locking or unlocking the vault of an NFD. The NFD vault defaults to locked.
* Added **/nfd/vault/sendTo/{name}** for sending ALGO or an ASA **to** an NFD vault, sending .1 for the MBR if necessary, having the vault opt-in, and transfer the asset. If the vault is already opted-in, then it will just be a simple payment/asset transfer. Sending to your own vault is always allowed. Other users can only send to a vault if it is unlocked by the owner.
* Added **/nfd/vault/sendFrom/{name}** for sending ALGO or an ASA **from** an NFDs vault to another account, or NFD vault. Only the NFD owner can send from the vault.

### 2023/02/07

* Added /nfd/consensus/metrics endpoint with ranked list of proposers up to 74% threshold for basic nakamoto coefficient (by proposing account)

### 2023/02/03

* Add optional requireNFD parameter to /nfd/consensus/leaders to specify whether to require the proposing account to be linked to an NFD.

### 2023/01/25

* Added a consensus leaderboard endpoint at /nfd/consensus/leaders utilizing data from new internal consensus tracking service.

### 2023/01/17

* Added segment totals to /nfd/totals endpoint return.

### 2023/01/16

* As part of Segments roll-out, all the features updated/added that were previously only on betanet/testnet will be live (All of the changes since [#2022-09](#2022-09 "mention") changes)
* Modified GET **/nfd/{name}** which is now **/nfd/{nameOrID}**. You can fetch an NFD now by its full name, or by its application ID.
* Modified **/nfd/purchase/{name}** API to have new parameter, **rejectNFT.** Setting this 'opts out' of receiving the NFD's NFT upon claiming. This is mainly for large projects, who may mint a significant number of segments in a central account and not wanting the extra MBR requirements on the owning account.

### 2023/01/13

* Added new **participation** return to /nfd/badges endpoint if an NFD's related account is participating in consensus (an 'online' account). "online":"true" is included in the participation object if so.

### 2022/12/xx

* Added **/nfd/v2/donations/list** endpoint which simply returns list of NFDs (brief view) that are considered donation targets.
* Added **/nfd/v2/donations/leaders/{name}** endpoint which accepts an NFD name as donation target to get donors for instead of a single address.
* Added **segmentLocked** parameter as additional available filter on the **/nfd/v2/search** endpoint. Allows getting only locked (not particularly useful), or unlocked NFDs.
* Added **segmentRoot** parameter as additional available filter on the **/nfd/v2/search** endpoint. Allows getting just roots, or just segments.
* Added /**nfd/segment/leaders** endpoint which returns the top 50 root NFDs, ordered by number of minted segments.

### 2022/11/04

* **Added /nfd/v2/address** - This is a new version of the /nfd/address reverse-lookup endpoint, but which returns the results as a map. Each address \[if found] is the key, with an array of any NFDs referenced by that address. The limit parameter is no longer a total limit, but a limit PER address.
* **Deprecated /nfd/address** - This endpoint will remain for a significant period of time but its awkwardness encourages its deprecation.
* Example /nfd/v2/address use and results:

{% code overflow="wrap" %}

```
https://api.nf.domains/nfd/v2/address?address=D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA&address=2MQZFP7LNZNQDMJO5F3SZDFVEEHOFH6G3NXSLASIJIB7OLDPVLSCX67QZ4&address=T3UL3YBJZILL2WGJ5HG3EAVAR6H4ETVZ4RKFGDFBWAFZSG7PEXSQRN2NGY&limit=1
```

{% endcode %}

```json
{
    "2MQZFP7LNZNQDMJO5F3SZDFVEEHOFH6G3NXSLASIJIB7OLDPVLSCX67QZ4": [
        {
            "appID": 764594795,
            "caAlgo": [
                "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA",
                "2MQZFP7LNZNQDMJO5F3SZDFVEEHOFH6G3NXSLASIJIB7OLDPVLSCX67QZ4",
                "AW3ZJNEVJKKICLA6JQ6ME64J2BOJORTYLJ7EOOAVF4ROY2QLBCZWLZOHFM"
            ],
            "depositAccount": "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA",
            "name": "goanna.algo",
            "owner": "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA",
            "properties": {},
            "state": "owned",
            "timeChanged": "2022-09-21T06:19:11Z"
        }
    ],
    "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA": [
        {
            "appID": 764594795,
            "caAlgo": [
                "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA",
                "2MQZFP7LNZNQDMJO5F3SZDFVEEHOFH6G3NXSLASIJIB7OLDPVLSCX67QZ4",
                "AW3ZJNEVJKKICLA6JQ6ME64J2BOJORTYLJ7EOOAVF4ROY2QLBCZWLZOHFM"
            ],
            "depositAccount": "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA",
            "name": "goanna.algo",
            "owner": "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA",
            "properties": {},
            "state": "owned",
            "timeChanged": "2022-09-21T06:19:11Z"
        }
    ],
    "T3UL3YBJZILL2WGJ5HG3EAVAR6H4ETVZ4RKFGDFBWAFZSG7PEXSQRN2NGY": [
        {
            "appID": 764582734,
            "caAlgo": [
                "T3UL3YBJZILL2WGJ5HG3EAVAR6H4ETVZ4RKFGDFBWAFZSG7PEXSQRN2NGY",
                "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA",
                "DGOANM6JL4VNSBJW737T24V4WVQINFWELRE3OKHQQFZ2JFMVKUF52D4AY4"
            ],
            "depositAccount": "T3UL3YBJZILL2WGJ5HG3EAVAR6H4ETVZ4RKFGDFBWAFZSG7PEXSQRN2NGY",
            "name": "artgoanna.algo",
            "owner": "D5J7H7PIYKLY2U6A5OFUAC7GQHTHSXXNX65DSD3CJYPBV2MVK6NTNW44CA",
            "properties": {},
            "state": "owned",
            "timeChanged": "2022-07-15T10:58:49Z"
        }
    ]
}

```

### 2022/11/04

* Added **/nfd/segment/price/{name}?buyer={address}** - This endpoint will provide a price breakdown (if possible) of minting the specified named segment.
* Updated **/nfd/suggest/{name}** to support optional **buyer={address}** parameter. Will return an error if the specified buyer can't mint any of the suggested names, such as a segment.

### 2022/10/23

* **Added (*****Changed)***\*\* \*\* a parameter in the **/nfd/analytics** endpoint
  * Added **includeOwner** parameter. Specifies whether to add a **currentOwner** property to each event with the *current owner of the NFD* referenced by that event. Should only be used rarely. Added to help a UI feature.
  * This was originally includeState - but changed to includeOwner.

### 2022/09/28

* Added new top-level property in API returns called **unverifiedCa**. (**unverified** **C**rypto **A**ddress)
  * Returns as an object with members for userDefined.ca.XXX properties.\
    ie: u.ca.btc on-chain is exposed in the api as unverifiedCa.btc: \["xxxx"]
  * The addresses will be returned as an array for future-proofing. Initially there will be only one address the user can enter for other chains, but editing of lists could be added later.
  * Example 'brief' view return for patrick.algo w/ dummy BTC / ETH values

```json
{
    "appID": 763843612,
    "asaID": 763843618,
    "caAlgo": [
        "MQ2QJHZSZ6A7ZXPFE2EPIWLYUMRRDO3DQBEO6NIQ2B5A5OJ4VMWOOI2AX4"
    ],
    "category": "premium",
    "depositAccount": "MQ2QJHZSZ6A7ZXPFE2EPIWLYUMRRDO3DQBEO6NIQ2B5A5OJ4VMWOOI2AX4",
    "metaTags": [
        "7_letters"
    ],
    "name": "patrick.algo",
    "nfdAccount": "LTWWWD52TRSUIM2RIPNFYEX4YL2GM2YQBRTYEGT54UPJM4RATVW7UWSWSY",
    "owner": "MQ2QJHZSZ6A7ZXPFE2EPIWLYUMRRDO3DQBEO6NIQ2B5A5OJ4VMWOOI2AX4",
    "properties": {
        "verified": {
            "avatar": "ipfs://bafkreifu5fbvnxfptgj6nisk4m2lc557dgs7soumr3nxxirrrv7zp4qivm",
            "avatarasaid": "847757707"
        }
    },
    "saleType": "buyItNow",
    "state": "owned",
    "unverifiedCa": {
        "btc": ["xxxxxxxxxxxxxxxx"],
        "eth": ["xxxxxxxxxxxxxxxx"]
    }
}
```

### 2022/09

* "**search**" endpoint at **/nfd?\[query params]** is now deprecated. It will continue to be supported for some time.
  * The v1 endpoint will no longer allow combining owner and reservedFor for a dual-search - and it **will also cap results to 240 \[total]**. Previously it would return ALL NFDs owned by X for eg.
* **Added** a new 'v2' search endpoint at **/nfd/v2/search?\[query params]**
  * Added **offset** parameter. The v2 endpoint supports offset / limit params like some of the other endpoints for paging through the results, but will also return a 'total' value like the analytics endpoint.
  * **removed** **requireAddresses** param.
  * **added optional state** parameter - can specify: forSale, owned, or reserved to filter results.

### 2022/09/06

* "c842b5c:main \[2022-09-06T18:18:39Z]
* Removed the 'state' parameter from **/nfd/analytics** (wasn't ever used)
* Updated the options in the '**state**' parameter in **/nfd/browse** to include **reserved**
* Reserved NFDs will no longer be filtered from results returned by the browse endpoint. This was originally done to prevent confusion in the marketplace, showing names users couldn't do anything with if it wasn't reserved for them. Additional data is always shown for all names now so it makes sense to show them again. At least one integrator was also using this endpoint for tracking changes over time and reserved nfds being excluded was creating gaps in their caches.
* A new **invalidCA** error code can be returned from the PATCH/PUT (updatePartial / updateAll) calls to update an NFD. This is returned if user-defined caalgo addresses that have been specified aren't valid Algorand addresses.

### 2022/09/01

* c52a8cd:main \[2022-09-01T03:56:08Z]
* The **caAlgo** property could have had an undefined ordering if more than 3 linked addresses were defined.
* The **asaID** property will now be returned in all 'brief' view returns of NFDs.

### 2022/08/25

#### Changed allowed pattern for userDefined fields

* ca.\[a-z]{1,5} is now allowed as a field name pattern for user defined fields. This will be used for specifying other token deposit addresses. ie: ca.btc, ca.eth...

### 2022/08/12

* "appVersion": "c54f842:main \[2022-08-12T17:49:39Z]"

#### Added POST /nfd/escrowOffer/{name} endpoint

* Make escrowed bid for a new 'floor' price of an ongoing auction. Higher bidder refunds you, if price drops to your escrow, you win auction with it being reserved for you.
* See API spec for body requirements. Effectively same as normal purchase call.

#### Enhanced /nfd/activity endpoint

* Added **timeAsc** as a new sort option.

#### NFDAnalyticEvent has new event type

* Added event type: **escrowedOffer**

#### **Enhanced /nfd/auction endpoint**

* **newEndTime**, and **newFloorPrice** will be added to auction return data if an escrowed floor has been set.

### 2022/07/27

#### Enhanced '/nfd/browse' endpoint

* "appVersion": "bd23fab:main \[2022-07-27T21:39:06Z]"
* Added **changedAfter** as new query param on browse endpoint allowing filtering on NFDs that changed after that timestamp.
* Added **timeChangedDesc** as a new sort option.

#### Added avatarasaid / avatarOutdated properties to NFD returns

* "appVersion": "1bc1198:main \[2022-07-27T19:58:47Z]"
* "appVersion": "2340c69:main \[2022-07-28T01:30:07Z]" (small fix for prefix endpoiint not including the avatarasaid)
* **NFD** returns will now contain properties.verified.avatarasaid in **thumbnail**, **brief**, and **full** views.
  * The avatarasaid property (on-chain as well) contains the ASA ID of the NFT the owner set as their avatar. Some integrators may cache based on asset id, so this is an easier way for them to display thumbnails than the IPFS link in the avatar property.
* **GET** calls on an NFD (/nfd/silvio.algo) in **brief** or **full** view will now return an optional property titled '**avatarOutdated**'. If true, this means the PFP set into this NFD has a newer image on-chain that the owner can switch to. Currently this means their PFP Avatar is an ARC19 NFT and an update has bee made since they set its image as their avatar.

### 2022/07/26 - excludeNFDAsSeller param on analytics

* Added query parameter: **excludeNFDAsSeller** (bool) to the **/nfd/analytics** endpoint:
  * "Whether to exclude events where NFDomains is the seller. If set to true, and filtering on 'sold' event for eg, returned items will will be secondary sales only.


# Status Page

<https://status.nf.domains>


# Community

For discussion with the team, and other users, please join us on the NFDomains Discord.

[<img src="https://248579615-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMrFth5XKC1eo3nu9xVre%2Fuploads%2F8ry6ASE6lns25nyYflEO%2FDiscord-Logo%2BWordmark-Color.svg?alt=media&amp;token=c8490424-b6af-4cbb-bf97-d55bc97a9cc2" alt="" data-size="line">](https://discord.com/invite/7XcuMTfeZP)

Once there, please check out the **#nfd-api** channel for development discussions.


