# Overview

## Overview

Clusters is a universal namespace. All the blockchains, all your wallets, one name.&#x20;

Clusters is different than existing services. It's **one universal name** that encompasses all the blockchains. It's different than ENS' .eth, SNS' .sol, Avvy's .avax, SpaceID's .bnb and .matic, or UnstoppableDomains using arbitrary conflicting TLDs. It embraces the multichain world.

A clusters name is suffixed with a trailing slash: **/**&#x20;

So if I'm foobar, my clusters name is **foobar/**

All my wallets are then suffixed individually under this universal name, just like a website URL. My ethereum wallet could be **foobar/eth**, my solana wallet is **foobar/sol**, my bitcoin wallet is **foobar/ordinals**. I can also have multiple wallets on the same chain, for example **foobar/defi** and **foobar/nfts** can both be ethereum wallets.&#x20;

<figure><img src="/files/WHvJAAgNpcJDNdIuhX8f" alt=""><figcaption><p>A sample cluster containing two wallets: an EVM wallet and a solana wallet</p></figcaption></figure>

Clusters helps users create a **universal profile**.&#x20;

* Say a project wants to airdrop a Solana coin to all Pudgy Penguins holders. Before Clusters, there's no easy way to do this, you'd have to go manually collect addresses yourself and likely only get a fraction of the data you need. With Clusters, you can easily query which SOL addresses are linked to Pudgy Penguin EVM addresses.
* Say a user is trying to bridge Ethereum to Solana. They have to manually go open up their SOL wallet every time, copy paste the SOL address, and hope they didn't misclick on a block explorer spoofed transaction. Wasted time and energy. With Clusters, the bridge app can autopopulate which SOL address is in the same cluster as the EVM address they're bridging from. Smoother UX, fewer scams, and higher conversion rates for the app.
* Say a hot new blockchain app pops up, like Farcaster. Instead of yet another first-come first-serve squatter race where the app owner has to manually claw back names and face social backlash, cluster names can be easily integrated as first-class prioritization without pandering to one ecosystem over another.


# Concepts

{% content-ref url="/pages/Jrv1EGR5qvkMijvziAV3" %}
[Cluster Name](/introduction/concepts/cluster-name)
{% endcontent-ref %}

{% content-ref url="/pages/cE1GUEeWfLqc6NwCulQu" %}
[Wallet Name](/introduction/concepts/wallet-name)
{% endcontent-ref %}


# Cluster Name

<figure><img src="/files/MK4i8K4L78QTmifPQqK1" alt="" width="563"><figcaption></figcaption></figure>

A cluster is a bundle of addresses where each address has its own unique name. For example, my cluster might be *foobar/* and I'd have three wallets each named *foobar/cold*, *foobar/memecoins*, and *foobar/gaming*, respectively. The part before the / slash is called the **cluster name**, the part after the / slash is called the **wallet name**.&#x20;

A cluster name can be owned by a single individual - called a **personal cluster** name. Or a cluster name can be owned by a community such as an a blockchain, an NFT community, or gaming ecosystem - called a **community cluster** name.&#x20;

Personal Cluster Examples:

* fooba&#x72;**/**
* ry&#x65;**/**
* vitali&#x6B;**/**

Community Cluster Examples:

* treasure/
* pudgy/
* layerzero/

Personal clusters and community clusters act the same aesthetically and display across integrations. They're both bundles of addresses with wallet names. The difference is that community clusters have more advanced management tooling to manage thousands of community wallet names at the same time.

#### Requirements

* **Uniqueness:** Unlike .eth and .sol fragmentation, names are globally unique across all chain ecosystems. If you try to register a name that already exists, it will fail.
* **Length:** Names are stored as a bytes32 string. So the max length is 32 characters. As we role out unicode support such as emoji's, the max length will vary&#x20;
* **Allowed Characters:** Supported characters include `a-z`, `0-9`, underscores (`_`), and dashes (`-`)
* **Case Sensitivity:** Cluster names are case insensitive. So `Foobar/` and `foobar/` will be treated the same.


# Wallet Name

A cluster is a bundle of addresses where each address has its own unique name. For example, my cluster might be *foobar/* and I'd have three wallets each named *foobar/cold*, *foobar/memecoins*, and *foobar/gaming*, respectively. The part before the / slash is called the **cluster name**, the part after the / slash is called the **wallet name**.&#x20;

#### Requirements

* **Uniqueness:** There cannot be 2 wallet names that are the same under a namespace. So you cannot have an EVM address called `foobar/defi` and a solana address that is also called `foobar/defi`
* **Length:** There are currently no limit to how long a wallet name should be. But it's encouraged to keep it under 10 characters.
* **Allowed Characters:** Supported characters include `a-z`, `0-9`, underscores (`_`), and dashes (`-`)
* **Case Sensitivity:** Cluster names are case insensitive. So `foobar/defi` and `foobar/Defi` will be treated the same.

**Data structure -** A wallet consists of:

* **Cluster Name:** What cluster name that wallet is attaached to
* **Wallet name:** The shorthand name you give your wallet address
* **Address:** The full length address of the wallet. It's then converted and stored in bytes32 format.
* **Verification:** Verified wallets are unique to the entire ecosystem. You can't have a wallet verified on 2 clusters at a time. This represents priority when querying names.
  * For example: If 0x123 is on foobar/cold and 0x123 is also on rye/hot but verified, rye/hot will have priority when resolving a cluster via an address.


# Features

{% content-ref url="/pages/DZXqsKpyFmrBXnT2KbML" %}
[Multichain](/introduction/features/multichain)
{% endcontent-ref %}

{% content-ref url="/pages/rQRQdxv3Dy6RgkLf2z5V" %}
[Wallet Bundles](/introduction/features/wallet-bundles)
{% endcontent-ref %}

{% content-ref url="/pages/mLBzmSgWseWocXSz4iPK" %}
[Antisquatting](/introduction/features/antisquatting)
{% endcontent-ref %}

{% content-ref url="/pages/KXrgeDYw24vGERdFJHi1" %}
[Communities](/getting-started/api/v0.1-deprecated/communities)
{% endcontent-ref %}

{% content-ref url="/pages/CH1UPcnEAnetTa0vghYn" %}
[Wallet Generation](/introduction/features/wallet-generation)
{% endcontent-ref %}


# Communities

Clusters is the best way to issue usernames to your community. A community like *treasure/* can issue thousands or even hundreds of thousands or free and paid usernames like *treasure/karel*, *treasure/yuri*, *treasure/foobar*. There are three key advantages:

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

* **Brand Awareness -** cluster names are visible across leading block explorers like Etherscan, wallets like Metamask, and bridges like LayerZero
* **Clean Aesthetics** - it looks way cleaner to have *treasure/karel* than karel.treasure.eth.
* **Make Money -** communities can combine a free or a paid approach and earn from both community name signups and upgrades to personal clusters
* **Fastest Launch** - no need to develop or deploy your own smart contracts, we take care of it all with a white-label solution

**Community Cluster**

<div align="center"><figure><img src="/files/E2C5X6B0bW44IX4wNvQ8" alt="" width="563"><figcaption></figcaption></figure></div>

**How do I set it up?**

We provide a flexible settings panel where you can choose community images, names, payment currency, pricing, and more. We provide a simple default interface that users can register on our site but expect to work with partners to set up a seamless experience on their community website.

<div align="center"><figure><img src="/files/8X5oTFGrqx29fyaeoHLH" alt="" width="563"><figcaption></figcaption></figure></div>

**What's the technical architecture?**

Clusters takes an appchain approach for both decentralization and scalability. Cluster names are decentralized digital assets, and full cluster configuration is replicated onto a data availability layer (currently Arweave) so that full protocol state can be permissionlessly indexed and replicated by anyone. However most integrators and community partners interact solely with our REST/JavaScript APIs for simplicity. Community name registration can be done either offchain via API (for free names) or onchain via smart contract call (for paid names).


# Multichain

Powered by LayerZero v2, a bridge with best-in-class track record, a $15 million security bounty, and configurable modular security, Clusters is natively multichain. Using chain abstraction, users can initiate any action from any chain and have it reflected in unified global state.

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


# Wallet Bundles

Users can group multiple wallets under one universal name in the most intuitive format yet.&#x20;

* foobar/defi
* foobar/nfts
* foobar/solana
* foobar/ordinals

This combination of rivalrous onchain top-level namespace combined with flexible offchain secondary namespace makes a cluster both a unique digital asset and a highly useful universal profile.


# Antisquatting

First and foremost, a namespace must be **efficient**. What does efficiency mean? It means that a high proportion of names are owned by people who are actively using them. Ecosystems that are too value extractive will suffer a weak community, but a lesser understood counterpoint is that ecosystems which do not properly price the most valuable assets will end up with high inefficiency, captured by squatters who do nothing to further social awareness and usage of the protocol. This short-term hype is outweighed by long-term frustration of potential future users who will go elsewhere.

So Clusters introduces demand-based capped recurring pricing, as proposed by Vitalik in <https://web.archive.org/web/20231102073510/https://vitalik.ca/general/2022/09/09/ens.html>. This mitigates malicious squatting while also providing simple longterm digital property rights guarantees for the average user.&#x20;

How does it work practically?

* 99% of users with an uncontested name like boldleonidas/ will pay a cheap minimum price
* For competitive names like defi/, users can bid on names owned by others which will increase the recurring fee. This introduces an explicit demand-based cost for squatting and squatters will be forced to sell into bids.
* If a prankster decides to grief an uncontested user like foobar/ by bidding on their name, the user can accept the bid and make money, while the prankster loses lots of money.


# Wallet Generation

## Overview

We provide a 1-click solution to onboard users from a variety of different chains. These wallet's private keys can later be exported to use outside of the Clusters ecosystem.&#x20;

| Bitcoin | Ton      | Aptos  | Litecoin |
| ------- | -------- | ------ | -------- |
| EVM     | Cosmos   | Ripple | Algorand |
| Solana  | Dogecoin | Tron   |          |

## Security

### Authentication

Signing into your cluster starts with verifying wallet ownership using off-chain authentication via signature signing. All users must sign an off-chain signature to prove they own their cluster.

**Passphrase:** Users may choose to opt in for an additional layer of security by adding a passphrase. We recommend users enable this feature when storing significant amount of funds on generated wallets.&#x20;

{% hint style="info" %}
Clusters does not store this passphrase. It is impossible to recover if lost and will result in forfeiting the ability to recover private keys of wallets generated on the users account.
{% endhint %}

* Passphrases are used as the encryption key for private key shards.
* Users will need to enter their passphrase in order to export their wallet's private keys.

### Private Key Storage

Key's are split up into 3 different shares using the [Shamir's secret sharing](https://en.wikipedia.org/wiki/Shamir%27s_secret_sharing). It is split between user and cloud encrypted storage. In order to reveal the private key, 2 out of the 3 shares need to be present. Full keys are never sent over the air or stored in Clusters infrastructure.

1. **User device share —** Stored on the users device via local storage. This share will be stored encrypted using the passphrase if opted in.
2. **Recovery share** **—** This share is encrypted at rest by default and stored by Clusters. This share is encrypted using the users passphrase if they opt in for this additional security later.
3. **Backup share —** Encrypted at rest and stored by Clusters. It's used, in conjunction with the recovery share, to generate an additional compatible user device share if it's lost or moving to another device.


# Selective Wallet Sharing

## Setup selective sharing for your app

{% stepper %}
{% step %}

### Create an internal webhook URL

In order for users to selectively share their wallets with your application, a webhook will need to be created to process `POST` requests coming from the user.

When wallets are shared with your application, you'll get the following `POST` payload:

```typescript
{
  "wallets": string[];
  "signer": string;
  "signature": string;
}
```

{% hint style="warning" %}
All signatures will be signed by `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266`
{% endhint %}

To check the validity of the webhook requests, run the following code:

```typescript
const address = await recoverMessageAddress({
    message: JSON.stringify(selectedWallets),
    signature: attestation.signature as `0x${string}`
});
```

The `address` should match the `signer` .
{% endstep %}

{% step %}

### Retrieve a custom share URL for your app

Go to <https://clusters.xyz/share> where you will be prompted to enter the webhook URL that you just created.

{% hint style="info" %}
Your webhook URL may look something like this: `https://api.myapp.com/webhook/share`
{% endhint %}

Once you enter a URL, a unique share link can be created that you can send to your users.

<figure><img src="/files/aEerabQAmXyqIWlfaJPg" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

### Send users to the URL

They will be prompted to select which wallets they want to share. When they click "Share" the data will be sent to your webhook URL.&#x20;

Users can verify that they are sharing data to the right place but double checking the blue "Sharing with" label as seen below.

<figure><img src="/files/5oOLIH7z4W4nGU2GBYQ6" alt=""><figcaption></figcaption></figure>
{% endstep %}
{% endstepper %}


# Javascript

Learn how to setup our framework agnostic javascript client

{% stepper %}
{% step %}

### Install the SDK

Using npm:

```bash
npm install @clustersxyz/sdk viem
```

Or yarn:

```bash
yarn add @clustersxyz/sdk viem
```

{% endstep %}

{% step %}

### Get an optional API Key

You can get an API Key by visiting <https://clusters.xyz/developer>
{% endstep %}

{% step %}

### Create your Clusters client

**Import**

```typescript
import { Clusters } from "@clustersxyz/sdk"

// 3 different examples
const clusters = new Clusters();

// with testnet enabled
const clusters = new Clusters({ isTestnet: true }) 

// with api key
const clusters = new Clusters({ apiKey: "abc123" });
```

{% endstep %}
{% endstepper %}

***

## Helper functions & variables

<table data-header-hidden><thead><tr><th width="250"></th><th></th></tr></thead><tbody><tr><td><code>validNameRegex</code></td><td>The regex for a valid cluster name</td></tr><tr><td><code>getImageUrl(name)</code></td><td>Get's the profile pfp url of a specific cluster name</td></tr><tr><td><code>getProfileUrl(name)</code></td><td>Get's the profile url of a cluster name</td></tr><tr><td><code>normalizeName(name)</code></td><td>Will clean up the name for onchain submission</td></tr><tr><td><code>isNameValid(name)</code></td><td>Checks if the name is a valid name and will be accepted on chain</td></tr></tbody></table>


# Authentication

{% hint style="info" %}
This is only required for making changes to your Cluster. Querying does not require authentication.
{% endhint %}

1. Get signing message
2. Sign the message with an EVM or Solana wallet
3. Get authentication key with the signature of the signed message
4. Using authenticate key with endpoints that require wallet authentication

***

**Get Signing Message**

Get the signing message used for wallet signature authentication

**Example**

```javascript
const data = await clusters.getAuthMessage()
```

**Response**

> The message to sign and the corresponding signing date

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

```json
{
  "message": "clusters.xyz verification\n\nBefore interacting with certain functionality, we require a wallet signature for verification.\n\n2024-05-14T19:08:27.985Z",
  "signingDate": "2024-05-14T19:08:27.985Z"
}
```

{% endtab %}
{% endtabs %}

***

**Get Authentication Token**

Authenticate the ownership of a specific wallet

| Name          | Description                                      |
| ------------- | ------------------------------------------------ |
| `signature`   | Signed message signature                         |
| `signingDate` | Date given by signing message response           |
| `type`        | `evm` or `solana` (wallet type used for signing) |
| `wallet`      | Wallet that signed the message                   |

**Example**

<pre class="language-javascript"><code class="lang-javascript">const data = await clusters.getAuthToken(
<strong>    '0x0000000000000000000000000000000000000001',
</strong><strong>    '2025-03-26T14:56:07.601Z',
</strong>    'evm',
    '0x5cff9c1362a71247da33887be2a44ac36a8724bb',
);
</code></pre>

**Response**

> The auth key used for authenticated responses

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

```json
{
  "token": "eyJzaWduYXR1cmUiOiJhYmMxMjMiLCJzaWduaW5nRGF0ZSI6IjIwMjQtMDUtMTRUMTk6MDg6MjcuOTg1WiIsInR5cGUiOiJldm0iLCJ3YWxsZXQiOiIweDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifQ=="
}
```

{% endtab %}
{% endtabs %}

***

**Validate Authentication Token**

Authenticate if a authentication key is valid or not

<table><thead><tr><th width="380.63671875">Name</th><th>Description</th></tr></thead><tbody><tr><td> <code>authToken</code></td><td>Authentication token</td></tr></tbody></table>

**Example**

```javascript
const check = await clusters.validateAuthToken(authToken)
```

**Response**

> The auth key used for authenticated responses

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

```json
{
  "signature": "abc123",
  "signingDate": "2024-05-14T19:08:27.985Z",
  "type": "evm",
  "wallet": "0x0000000000000000000000000000000000000000",
  "isValid": false
}
```

{% endtab %}
{% endtabs %}


# Clusters

### Create a Cluster

Create a cluster

<table><thead><tr><th width="385.23046875">Name</th><th>Description</th></tr></thead><tbody><tr><td> <code>authToken</code></td><td><a href="/pages/6AGkfCSOcnx2jBGZ0ljQ">Authentication from wallet</a></td></tr></tbody></table>

**Example**

```javascript
const data = await clusters.createCluster(authToken);
```

**Response**

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

```json
{
    "id": "0x000049d953f88ab10f1b2778275028894602c41bbe6ea42bf46695d4edc5b6d6",
}
```

{% endtab %}
{% endtabs %}

***

### Cluster by ID

Get the cluster from an id

| Name | Description   |
| ---- | ------------- |
| `id` | ID of cluster |

**Example**

```javascript
const cluster = await clusters.getClusterById(id);
```

**Response**

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

```json
{
  "id": "0xa8d12b92b91fe0db3651ff2d45c1f47b1bb343054e9cd1e556c73f2330269224",
  "createdBy": "0x5cff9c1362a71247da33887be2a44ac36a8724bb",
  "createdAt": "2024-02-14 21:39:05+00",
  "wallets": [
    {
      "address": "0x5cff9c1362a71247da33887be2a44ac36a8724bb",
      "name": "main",
      "isVerified": true,
      "isPrivate": false,
      "isBackedUp": false,
      "updatedAt": "2024-02-09 15:21:22+00",
      "updatedBy": "0x5cff9c1362a71247da33887be2a44ac36a8724bb",
      "createdAt": "2024-02-09 15:21:22+00"
    }
  ],
  "isTestnet": false
}
```

{% endtab %}
{% endtabs %}

***

### Cluster by Name

Get the cluster from a name

| Name   | Description  |
| ------ | ------------ |
| `name` | Cluster name |

**Example**

```javascript
const cluster = await clusters.getClusterByName(name);
```

**Response**

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

```json
{
  "id": "0xa8d12b92b91fe0db3651ff2d45c1f47b1bb343054e9cd1e556c73f2330269224",
  "createdBy": "0x5cff9c1362a71247da33887be2a44ac36a8724bb",
  "createdAt": "2024-02-14 21:39:05+00",
  "wallets": [
    {
      "address": "0x5cff9c1362a71247da33887be2a44ac36a8724bb",
      "name": "main",
      "isVerified": true,
      "isPrivate": false,
      "isBackedUp": false,
      "updatedAt": "2024-02-09 15:21:22+00",
      "updatedBy": "0x5cff9c1362a71247da33887be2a44ac36a8724bb",
      "createdAt": "2024-02-09 15:21:22+00"
    }
  ],
  "isTestnet": false
}
```

{% endtab %}
{% endtabs %}

***

### Cluster ID by address

Get the cluster from an id

| Name      | Description                   |
| --------- | ----------------------------- |
| `address` | Verified address of a cluster |

**Example**

```javascript
const data = await clusters.getClusterIdByAddress(address);
```

**Response**

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

```json
{
  "clusterId": "0xa8d12b92b91fe0db3651ff2d45c1f47b1bb343054e9cd1e556c73f2330269224"
}
```

{% endtab %}
{% endtabs %}

***

### Add wallets

Add wallets to the cluster the authenticated wallet is in

<table><thead><tr><th width="375.26171875">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>wallets</code></td><td>{ address: string, name: string, isPrivate: boolean }[]</td></tr><tr><td><code>authToken</code></td><td><a href="/pages/6AGkfCSOcnx2jBGZ0ljQ">Authentication from wallet</a></td></tr></tbody></table>

**Example**

```javascript
await clusters.addWallets(
    [{ address: '0x0000000000000000000000000000000000000011', name: 'new', isPrivate: true }],
    authToken,
 );
```

**Response**

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

```json
{
    "success": true,
}
```

{% endtab %}
{% endtabs %}

***

### Generate wallet

Generate a wallet in a cluster that the authenticated wallet is in

<table><thead><tr><th width="385.23046875">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>wallets</code></td><td>{ address: string, name: string, isPrivate: boolean }[]</td></tr><tr><td><code>authToken</code></td><td><a href="/pages/6AGkfCSOcnx2jBGZ0ljQ">Authentication from wallet</a></td></tr></tbody></table>

**Example**

```javascript
await clusters.generateWallet(
    [{ address: '0x0000000000000000000000000000000000000011', name: 'test', isPrivate: false }],
    authToken,
  );
```

**Response**

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

```json
{
  type: "evm",
  address: "0x9D212340B4E5F3a38B171b2971D678B95083d928",
  name: "test",
  share: "0xdb87682d04e71c99bf8964e51cf5ec4843f2c588946bf749324819df0ba9ee1fb66e67bc21b440817310724abd1fb41676f6e2b30923819cf647bcb94a097c2b148257c58f38280682419af6c1fdab21cb94827bab4c0c4fa1d6ce153635b591a8b5c706f2db8307d08bc54022d381599a661c11dc5ab39fd0fbc167aac81ed48b211e98192cb0b6344232a4c577a7754339be",
}
```

{% endtab %}
{% endtabs %}

***

### Update wallets

Update wallets in a cluster that the authenticated wallet is in

<table><thead><tr><th width="385.23046875">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>wallets</code></td><td>{ address: string, name: string }[]</td></tr><tr><td><code>authToken</code></td><td><a href="/pages/6AGkfCSOcnx2jBGZ0ljQ">Authentication from wallet</a></td></tr></tbody></table>

**Example**

```javascript
await clusters.updateWalletNames(
    [{ address: '0x0000000000000000000000000000000000000011', name: 'new2' }],
    authToken,
  );
```

**Response**

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

```json
{
    "success": true,
}
```

{% endtab %}
{% endtabs %}

***

### Remove wallets

Remove an array of addresses from the cluster of the authenticated wallet

<table><thead><tr><th width="385.23046875">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>wallets</code></td><td>string[]</td></tr><tr><td><code>authToken</code></td><td><a href="/pages/6AGkfCSOcnx2jBGZ0ljQ">Authentication from wallet</a></td></tr></tbody></table>

**Example**

```javascript
await clusters.removeWallets(['0x0000000000000000000000000000000000000011'], authToken)
```

**Response**

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

```json
{
    "success": true,
}
```

{% endtab %}
{% endtabs %}

***

### Verify wallet

Verify the authenticated wallet in a specific cluster

<table><thead><tr><th width="385.23046875">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>clusterId</code></td><td>The cluster id you want to verify the wallet in</td></tr><tr><td><code>authToken</code></td><td><a href="/pages/6AGkfCSOcnx2jBGZ0ljQ">Authentication from wallet</a></td></tr></tbody></table>

**Example**

```javascript
await clusters.verifyWallet(clusterId, authToken);
```

**Response**

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

```json
{
    "success": true,
}
```

{% endtab %}
{% endtabs %}


# Address → Cluster Name

### Get name

Get the cluster and wallet name based on an address

| Name      | Description           |
| --------- | --------------------- |
| `address` | EVM or Solana address |

**Example**

```javascript
const name = await clusters.getName(address);
```

**Response**

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

```json
{
    "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
    "type": "evm",
    "clusterName": "cluster",
    "walletName": "main",
    "isVerified": true
}
```

{% endtab %}

{% tab title="404" %}

```json
{
  "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
  "type": "evm",
  "clusterName": null,
  "walletName": null
}
```

{% endtab %}
{% endtabs %}

***

### Bulk get names

Get the cluster and wallet name based on multiple addresses

| Name        | Description     |
| ----------- | --------------- |
| `addresses` | Array addresses |

**Example**

```javascript
const names = await clusters.getNames(['address1', 'address2'])
```

**Response**

> Any addresses not found will have `clusterName: null`

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

```json
[
  {
    "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
    "type": "evm",
    "clusterName": "clusters",
    "walletName": "main",
    "isVerified": true
  },
  {
    "address": "0xccdead94e8cf17de32044d9701c4f5668ad0bef9",
    "type": "evm",
    "clusterName": "layerzero",
    "walletName": "main",
    "isVerified": true
  }
]
```

{% endtab %}

{% tab title="500" %}

```json
[]
```

{% endtab %}
{% endtabs %}

***


# Cluster Name → Address

### Bulk get addresses

Get the wallets associated to a cluster name. You can pass just the cluster name or also include the address name.

| Name    | Description                                           |
| ------- | ----------------------------------------------------- |
| `names` | Array of cluster names (with or without address name) |

**Example**

```javascript
await clusters.getAddresses(['clusters', 'clusters/main']);
```

**Response**

> Any names in the array that are not found will be omitted from the response.

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

```json
[
  {
    "name": "clusters",
    "type": "evm",
    "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
    "clusterName": "clusters",
    "walletName": "main",
    "isVerified": true
  },
  {
    "name": "clusters/main",
    "type": "evm",
    "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
    "clusterName": "clusters",
    "walletName": "main",
    "isVerified": true
  }
]
```

{% endtab %}

{% tab title="500" %}

```json
[]
```

{% endtab %}
{% endtabs %}


# Registration

### getNameAvailability(names)

Check the availability of multiple cluster names

```javascript
const names = ["foobar", "namethatdoesntexist"];
const cluster = await clusters.getNameAvailability(names);
```

**Response**

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

```json
[
  {
    "name": "foobar",
    "isAvailable": false
  },
  {
    "name": "namethatdoesntexist",
    "isAvailable": true
  }
]
```

{% endtab %}
{% endtabs %}

***

### getRegistrationTransaction(names, sender, network)

Get the transaction data for registering a cluster so that a user can sign it

<table><thead><tr><th width="200">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>network</code></td><td><pre><code>"1"
"10"
"56"
"137"
"8453"
"81457"
"17000"
"42161"
"43114"
"11155111"
"solana"
</code></pre></td></tr><tr><td><code>sender</code></td><td>The sender's address</td></tr><tr><td><code>names</code></td><td><code>{ name: string; amountWei?: string }[]</code><br>bidAmountWei <em>(optional)</em> is always in ETH and defaults to 0.01 ETH</td></tr><tr><td><code>referralClusterId</code><br><em>(optional)</em></td><td>The cluster id that is referring this registration</td></tr></tbody></table>

{% tabs %}
{% tab title="Ethereum" %}
**Example**

```javascript
const names = [
    { "name": "namethatdoesntexist1" },
    { "name": "namethatdoesntexist2" }
]
const sender = "0x0000000000000000000000000000000000000001";
const cluster = await clusters.getRegistrationTransactionEvm(names, sender, "1");
```

**Response**

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

```json
{
  "type": "evm",
  "gasToken": {
    "symbol": "ETH",
    "decimals": 18
  },
  "transactionData": {
    "to": "0x00000000000e1a99dddd5610111884278bdbda1d",
    "data": "0x40bee835000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000026e616d6574686174646f65736e746578697374310000000000000000000000006e616d6574686174646f65736e74657869737432000000000000000000000000",
    "value": "20000000000000000"
  },
  "registrationFee": "20000000000000000",
  "bridgeFee": "0",
  "names": [
    {
      "name": "namethatdoesntexist1",
      "amountWei": "10000000000000000",
      "amount": "10000000000000000"
    },
    {
      "name": "namethatdoesntexist2",
      "amountWei": "10000000000000000",
      "amount": "10000000000000000"
    }
  ]
}
```

{% endtab %}

{% tab title="500" %}
**Names unavailable**

```json
{
  "status": 500,
  "message": "1 or more names are unavailable"
}
```

**Bridge Failure**

```json
{
  "status": 500,
  "message": "lz quote failed. most likely caused by wallets having insufficient balances"
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="Solana" %}
**Example**

```javascript
const names = [
    { "name": "namethatdoesntexist1" },
    { "name": "namethatdoesntexist2" }
]
const sender = "1nc1nerator11111111111111111111111111111111";
const cluster = await clusters.getRegistrationTransactionSolana(names, sender);
```

**Response**

{% hint style="warning" %}
Notice when using solana, `transactionData` is an array of transactions instead of a single object on other networks. This is because only one name is supported per transaction at the moment when using solana.
{% endhint %}

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

```json
{
  "type": "solana",
  "gasToken": {
    "symbol": "SOL"
  },
  "transactionData": [
    "gAEAAgQAM5ByjTQRYHm9yRG//wDb1E0uzcz3nKbhADjhAAAAAC/u4TnLEmhvVrNvLTdpdpVdJLegH99q7vYkM2iDi+hFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFSlNamSkhBk0k6HFg2jh8fDW13bySu4HkH6hAQQVEjZdvwyMMJHbJTNyHX/nuAnbanfNbTqWDof5avLEj3/dwAgICAAEMAgAAAFR9jwgAAAAAAwEAqgF7InR5cGUiOiJiaWQiLCJuYW1lIjoibmFtZXRoYXRkb2VzbnRleGlzdDEiLCJ3ZWkiOjEwMDAwMDAwMDAwMDAwMDAwLCJ0aW1lc3RhbXAiOjE3NDMwMDgwOTcsInNpZ25hdHVyZSI6ImViM2NhNjBkMDE5OWQwNWVkMzVkNGJjNGFhNTI1Yzk4MmVlYzI1M2I4MjIyMzE0MjBhNzNlZGQ1NzAzOWVmYmMifQA=",
    "gAEAAgQAM5ByjTQRYHm9yRG//wDb1E0uzcz3nKbhADjhAAAAAC/u4TnLEmhvVrNvLTdpdpVdJLegH99q7vYkM2iDi+hFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFSlNamSkhBk0k6HFg2jh8fDW13bySu4HkH6hAQQVEjZdvwyMMJHbJTNyHX/nuAnbanfNbTqWDof5avLEj3/dwAgICAAEMAgAAAFR9jwgAAAAAAwEAqgF7InR5cGUiOiJiaWQiLCJuYW1lIjoibmFtZXRoYXRkb2VzbnRleGlzdDIiLCJ3ZWkiOjEwMDAwMDAwMDAwMDAwMDAwLCJ0aW1lc3RhbXAiOjE3NDMwMDgwOTcsInNpZ25hdHVyZSI6ImViM2NhNjBkMDE5OWQwNWVkMzVkNGJjNGFhNTI1Yzk4MmVlYzI1M2I4MjIyMzE0MjBhNzNlZGQ1NzAzOWVmYmMifQA="
  ],
  "registrationFee": "287242920",
  "bridgeFee": "0",
  "names": [
    {
      "name": "namethatdoesntexist1",
      "amountWei": "10000000000000000",
      "amount": "143621460"
    },
    {
      "name": "namethatdoesntexist2",
      "amountWei": "10000000000000000",
      "amount": "143621460"
    }
  ]
}
```

{% endtab %}

{% tab title="500" %}
**Names unavailable**

```json
{
  "status": 500,
  "error": "names_unavailable",
  "message": "1 or more names are unavailable"
}
```

**Bridge Failure**

```json
{
  "status": 500,
  "error": "lz_quote_fail",
  "message": "lz quote failed. most likely caused by wallets having insufficient balances"
}
```

{% endtab %}
{% endtabs %}
{% endtab %}
{% endtabs %}

***

### getTransactionStatus(tx)

Get the status of a specific transaction hash.

{% hint style="info" %}
For now, if you registered more than one name in a transaction, it'll only give you the status of the first name.
{% endhint %}

**Example**

```javascript
const cluster = await clusters.getTransactionStatus("0xffea3b29c64016772b7d3194b3c3899a62191e654990b6e3393b410667102284");
```

**Response**

```typescript
type status = 'not_found' | 'pending' | 'invalid' | 'finalized'
```

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

```json
{
  "tx": "0xffea3b29c64016772b7d3194b3c3899a62191e654990b6e3393b410667102284",
  "status": "finalized"
}
```

{% endtab %}
{% endtabs %}


# Communities

## Check name availability

Check the availability of a community cluster name

```javascript
const isAvailable = await clusters.getCommunityNameAvailability("pengu/myname");
```

**Response**

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

```json
{
  "name": "pengu/myname",
  "isAvailable": false
}
```

{% endtab %}
{% endtabs %}

## Register a community name

Check the availability of one or more clusters

| Name                         | Description                                                                                                                                               |
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `communityName`              | The name of the community you are registering under                                                                                                       |
| `name`                       | The name you want to register                                                                                                                             |
| `autheToken`                 | Authentication key                                                                                                                                        |
| `walletAddress` *(optional)* | If you are the admin of the community, you can register on behalf of someone. If this is left out the name will be registered to the authenticated wallet |

**Example**

```javascript
const name = await clusters.registerCommunityCluster(
   "pengu", 
   "myname",
   AUTH_TOKEN
);
```

**Response**

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

```json
{
  "clusterName": "pengu/myname",
  "owner": "0x123"
}
```

{% endtab %}
{% endtabs %}


# Event Indexing

### Events

Get a list of historical events that happen on the Clusters application that can be replayed to get the full state.

| Name                         | Description                                   |
| ---------------------------- | --------------------------------------------- |
| `nextPage` *(optional)*      | The next page attribute to retrieve more data |
| `limit` *(optional)*         | How many rows to return.  Defaults to `500`   |
| `from` *(optional)*          | The user who initiated the event              |
| `orderBy` *(optional)*       | `oldest` or `newest` Defaults to `oldest`     |
| `fromTimestamp` *(optional)* | Timestamp in ms to start at                   |

**Note:** If you use the `nextPage` query param, `fromTimestamp` will be ignored.

**Example**

```javascript
const events = await clusters.getEvents({ limit: 1, nextPage: 'sAPGZWgLwk' })
```

**Response**

> List of events

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

```json
{
  "items": [
    {
      "id": "SqEhcVxrs2",
      "type": "ADD_WALLET",
      "from": "0x6ed7d526b020780f694f3c10dfb25e1b134d3215",
      "data": {
        "name": "main",
        "address": "0x6ed7d526b020780f694f3c10dfb25e1b134d3215",
        "cluster_id": "0x414d66466808501ce4eb380bcc67eddba8b3379363dafb635e5ccc5f90871fb5",
        "is_verified": true,
        "is_backed_up": false
      },
      "isTestnet": false,
      "timestamp": 1706797486000
    },
    {
      "id": "aSBcH9uPdG",
      "type": "REGISTER_NAME",
      "from": "0x822b5a721b4bcf8e627f0dc287c57f187d0b17dc",
      "data": {
        "name": "torje",
        "wei_amount": "10000000000000000"
      },
      "isTestnet": false,
      "timestamp": 1706799767000
    },
    {
      "id": "96HQxn4l5q",
      "type": "REGISTER_NAME",
      "from": "0xd64477d0f5fe7e6b8486a336fe694ef7789ab912",
      "data": {
        "name": "misconix",
        "wei_amount": "10000000000000000"
      },
      "isTestnet": false,
      "timestamp": 1706799779000
    }
  ],
  "nextPage": "1iW2vjkBqM"
}
```

{% endtab %}

{% tab title="500" %}

```json
null
```

{% endtab %}
{% endtabs %}


# API

## API Endpoint

```
https://api.clusters.xyz
```

#### API KEY

{% hint style="info" %}
You can get an API Key by visiting <https://clusters.xyz/developer/api-keys>
{% endhint %}

If you have an API KEY, you can add it to the request headers like so:

```javascript
fetch(URL, { headers: { "X-API-KEY": "abc123" } }) 
```


# v1

## API Endpoint

```
https://api.clusters.xyz/v1
```

#### API KEY

{% hint style="info" %}
You can get an API Key by visiting <https://clusters.xyz/developer>
{% endhint %}

If you have an API KEY, you can add it to the request headers like so:

```javascript
fetch(URL, { headers: { "X-API-KEY": "abc123" } }) 
```

***

## Authentication

{% hint style="info" %}
Currently, this is only required for [Managing Wallets](/getting-started/api/v0.1-deprecated/managing-wallets). If you do not need to manage wallets via the API, you can ignore this.
{% endhint %}

1. Get signing message
2. Sign the message with an EVM or Solana wallet
3. Get authentication key with the signature of the signed message
4. Using authenticate key with endpoints that require wallet authentication

***

**Get Signing Message**

<mark style="color:green;">`GET`</mark> `/v1/auth/message`

Get the signing message used for wallet signature authentication

**Example**

```bash
curl -X GET 'https://api.clusters.xyz/v1/auth/message'
```

**Response**

> The message to sign and the corresponding signing date

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

```json
{
  "message": "clusters.xyz verification\n\nBefore interacting with certain functionality, we require a wallet signature for verification.\n\n2024-05-14T19:08:27.985Z",
  "signingDate": "2024-05-14T19:08:27.985Z"
}
```

{% endtab %}
{% endtabs %}

***

**Get Authentication Key**

<mark style="color:green;">`POST`</mark> `/v1/auth/token`

Authenticate the ownership of a specific wallet

| Name          | Description                                      |
| ------------- | ------------------------------------------------ |
| `signature`   | Signed message signature                         |
| `signingDate` | Date given by signing message response           |
| `type`        | `evm` or `solana` (wallet type used for signing) |
| `wallet`      | Wallet that signed the message                   |

**Example**

```bash
curl --request POST \
  --url https://api.clusters.xyz/v1/auth/token \
  --header 'Content-Type: application/json' \
  --data '{
  "signature": "abc123",
  "signingDate": "2024-05-14T19:08:27.985Z",
  "type": "evm",
  "wallet": "0x0000000000000000000000000000000000000000"
}'
```

**Response**

> The auth key used for authenticated responses

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

```json
{
  "authenticationKey": "eyJzaWduYXR1cmUiOiJhYmMxMjMiLCJzaWduaW5nRGF0ZSI6IjIwMjQtMDUtMTRUMTk6MDg6MjcuOTg1WiIsInR5cGUiOiJldm0iLCJ3YWxsZXQiOiIweDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifQ=="
}
```

{% endtab %}
{% endtabs %}

***

**Validate Authentication Key**

<mark style="color:green;">`POST`</mark> `/v1/auth/validate`

Authenticate if a authentication key is valid or not

| Name                | Description        |
| ------------------- | ------------------ |
| HEADER `X-AUTH-KEY` | Authentication key |

**Example**

```bash
curl --request GET
--url https://api.clusters.xyz/v1/auth/validate
--header 'X-AUTH-KEY: eyJzaWduYXR1cmUiOiJhYmMxMjMiLCJzaWduaW5nRGF0ZSI6IjIwMjQtMDUtMTRUMTk6MDg6MjcuOTg1WiIsInR5cGUiOiJldm0iLCJ3YWxsZXQiOiIweDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifQ=='
```

**Response**

> The auth key used for authenticated responses

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

```json
{
  "signature": "abc123",
  "signingDate": "2024-05-14T19:08:27.985Z",
  "type": "evm",
  "wallet": "0x0000000000000000000000000000000000000000",
  "isValid": false
}
```

{% endtab %}
{% endtabs %}


# Authentication

{% hint style="info" %}
This is only required for making changes to your Cluster. Querying does not require authentication.
{% endhint %}

1. Get signing message
2. Sign the message with an EVM or Solana wallet
3. Get authentication key with the signature of the signed message
4. Using authenticate key with endpoints that require wallet authentication

***

**Get Signing Message**

<mark style="color:green;">`GET`</mark> `/v1/auth/message`

Get the signing message used for wallet signature authentication

**Example**

```bash
curl -X GET 'https://api.clusters.xyz/v1/auth/message'
```

**Response**

> The message to sign and the corresponding signing date

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

```json
{
  "message": "clusters.xyz verification\n\nBefore interacting with certain functionality, we require a wallet signature for verification.\n\n2024-05-14T19:08:27.985Z",
  "signingDate": "2024-05-14T19:08:27.985Z"
}
```

{% endtab %}
{% endtabs %}

***

**Get Authentication Key**

<mark style="color:green;">`POST`</mark> `/v1/auth/token`

Authenticate the ownership of a specific wallet

| Name          | Description                                      |
| ------------- | ------------------------------------------------ |
| `signature`   | Signed message signature                         |
| `signingDate` | Date given by signing message response           |
| `type`        | `evm` or `solana` (wallet type used for signing) |
| `wallet`      | Wallet that signed the message                   |

**Example**

```bash
curl --request POST \
  --url https://api.clusters.xyz/v1/auth/token \
  --header 'Content-Type: application/json' \
  --data '{
  "signature": "abc123",
    "signingDate": "2024-05-14T19:08:27.985Z",
    "type": "evm",
    "wallet": "0x0000000000000000000000000000000000000000"
  }'
```

**Response**

> The auth key used for authenticated responses

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

```json
{
  "token": "RmUyNi4yKjEqZjkyOGU3OWNjMmY5NDlkODZmM2I4OWQzNWFjOWIwODQxYjVkMTlmMTY3MzNjYjRjYjdjNmE4MWU3MTE5MzAwNSpkb0hLZ1FURGN1WWhhY2VFeHZxdWh3KnZQdkNhSm02QVk5dEJwZFdVUnNuTTBNVk9vZGNaa1Z4dE5ObENiWjNKWUcxdllXR1J0TkVpdFo4b2ZZNVJ6aW5Qd005OFhkTUtWS2ZscmhtRHJzTnN5cEdaa1E4YWZZMnVTaXpSUjhQdUp3ZFpmNFF3YXZaWGdweDdSbHVfbk1hN3c0WlQ2bktvVGtDSVVJVmVhb3VLUSoxNzQ2ODExMDYyMTU5KjViMzY4YWY3ZTcwZmU3YTI2Mjc1OGRkZmQyMDVlYzU0NzkxYjcwMzZjNzc3YWFlMDJkMmRhYWI3NzMzMzYwOTIqSVhXZnNnU3Z6c0RHdEpYemFqMGw1YzIzdVgyNzV4M3RBSG1jWXZxZ2dUc34y"
}
```

{% endtab %}
{% endtabs %}

***

**Validate Authentication Token**

<mark style="color:green;">`GET`</mark> `/v1/auth/validate`

Authenticate if a authentication key is valid or not

<table><thead><tr><th width="380.63671875">Name</th><th>Description</th></tr></thead><tbody><tr><td>HEADER <code>Authorization: Bearer {AUTHKEY}</code></td><td>Authentication key</td></tr></tbody></table>

**Example**

```bash
curl --request GET
--url https://api.clusters.xyz/v1/auth/validate
--header 'Authorization: Bearer eyJzaWduYXR1cmUiOiJhYmMxMjMiLCJzaWduaW5nRGF0ZSI6IjIwMjQtMDUtMTRUMTk6MDg6MjcuOTg1WiIsInR5cGUiOiJldm0iLCJ3YWxsZXQiOiIweDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifQ=='
```

**Response**

> The auth key used for authenticated responses

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

```json
{
  "signature": "abc123",
  "signingDate": "2024-05-14T19:08:27.985Z",
  "type": "evm",
  "wallet": "0x0000000000000000000000000000000000000000",
  "isValid": false
}
```

{% endtab %}
{% endtabs %}


# Clusters

### Create a Cluster

<mark style="color:green;">`POST`</mark> `/v1/clusters`

Create a cluster

<table><thead><tr><th width="385.23046875">Name</th><th>Description</th></tr></thead><tbody><tr><td>HEADER <code>Authorization: Bearer {AUTHKEY}</code></td><td><a href="/pages/6AGkfCSOcnx2jBGZ0ljQ">Authentication from wallet</a></td></tr></tbody></table>

**Example**

```bash
curl --request POST \
  --url https://api.clusters.xyz/v1/clusters \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer AUTHKEY'
```

**Response**

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

```json
{
    "id": "0x000049d953f88ab10f1b2778275028894602c41bbe6ea42bf46695d4edc5b6d6",
}
```

{% endtab %}
{% endtabs %}

***

### Cluster by ID

<mark style="color:green;">`GET`</mark> `/v1/clusters/id/:id`

Get the cluster from an id

| Name | Description   |
| ---- | ------------- |
| `id` | ID of cluster |

**Example**

```bash
curl --request GET \
  --url https://api.clusters.xyz/v1/clusters/id/0xa8d12b92b91fe0db3651ff2d45c1f47b1bb343054e9cd1e556c73f2330269224
```

**Response**

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

```json
{
  "id": "0xa8d12b92b91fe0db3651ff2d45c1f47b1bb343054e9cd1e556c73f2330269224",
  "createdBy": "0x5cff9c1362a71247da33887be2a44ac36a8724bb",
  "createdAt": "2024-02-14 21:39:05+00",
  "wallets": [
    {
      "address": "0x5cff9c1362a71247da33887be2a44ac36a8724bb",
      "name": "main",
      "isVerified": true,
      "isPrivate": false,
      "isBackedUp": false,
      "updatedAt": "2024-02-09 15:21:22+00",
      "updatedBy": "0x5cff9c1362a71247da33887be2a44ac36a8724bb",
      "createdAt": "2024-02-09 15:21:22+00"
    }
  ],
  "isTestnet": false
}
```

{% endtab %}
{% endtabs %}

***

### Cluster by Name

<mark style="color:green;">`GET`</mark> `/v1/clusters/name/:name`

Get the cluster from a name

| Name   | Description  |
| ------ | ------------ |
| `name` | Cluster name |

**Example**

```bash
curl --request GET \
  --url https://api.clusters.xyz/v1/clusters/name/clusters
```

**Response**

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

```json
{
  "id": "0xa8d12b92b91fe0db3651ff2d45c1f47b1bb343054e9cd1e556c73f2330269224",
  "createdBy": "0x5cff9c1362a71247da33887be2a44ac36a8724bb",
  "createdAt": "2024-02-14 21:39:05+00",
  "wallets": [
    {
      "address": "0x5cff9c1362a71247da33887be2a44ac36a8724bb",
      "name": "main",
      "isVerified": true,
      "isPrivate": false,
      "isBackedUp": false,
      "updatedAt": "2024-02-09 15:21:22+00",
      "updatedBy": "0x5cff9c1362a71247da33887be2a44ac36a8724bb",
      "createdAt": "2024-02-09 15:21:22+00"
    }
  ],
  "isTestnet": false
}
```

{% endtab %}
{% endtabs %}

***

### Cluster ID by address

<mark style="color:green;">`GET`</mark> `/v1/clusters/address/:address`

Get the cluster from an id

| Name      | Description                   |
| --------- | ----------------------------- |
| `address` | Verified address of a cluster |

**Example**

```bash
curl --request GET \
  --url https://api.clusters.xyz/v1/clusters/address/0x5cff9c1362a71247da33887be2a44ac36a8724bb
```

**Response**

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

```json
{
  "clusterId": "0xa8d12b92b91fe0db3651ff2d45c1f47b1bb343054e9cd1e556c73f2330269224"
}
```

{% endtab %}
{% endtabs %}

***

### Add wallets

<mark style="color:green;">`POST`</mark> `/v1/clusters/wallets`

Add wallets to the cluster the authenticated wallet is in

<table><thead><tr><th width="375.26171875">Name</th><th>Description</th></tr></thead><tbody><tr><td>HEADER <code>Authorization: Bearer {AUTHKEY}</code></td><td><a href="/pages/6AGkfCSOcnx2jBGZ0ljQ">Authentication from wallet</a></td></tr><tr><td><code>body</code></td><td>{ address: string, name: string, isPrivate: boolean }[]</td></tr></tbody></table>

**Example**

```bash
curl --request POST \
  --url 'https://api.clusters.xyz/v1/clusters/wallets' \
  --header 'Authorization: Bearer AUTHKEY' \
  --header 'Content-Type: application/json' \
  --data '[{
  "address": "0x0000000000000000000000000000000000000011",
  "name": "new",
  "isPrivate": false
}]'
```

**Response**

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

```json
{
    "success": true,
}
```

{% endtab %}
{% endtabs %}

***

## Generate wallet

<mark style="color:green;">`POST`</mark> `/v1/clusters/generate/wallet`

Generate a wallet in a cluster that the authenticated wallet is in

<table><thead><tr><th width="385.23046875">Name</th><th>Description</th></tr></thead><tbody><tr><td>HEADER <code>Authorization: Bearer {AUTHKEY}</code></td><td><a href="/pages/6AGkfCSOcnx2jBGZ0ljQ">Authentication from wallet</a></td></tr><tr><td><code>body</code></td><td>{ address: string, name: string, isPrivate: boolean }[]</td></tr></tbody></table>

**Example**

```javascript
curl --request POST \
  --url 'https://api.clusters.xyz/v1/clusters/generate/wallet' \
  --header 'Authorization: Bearer AUTHKEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "type": "solana",
  "name": "test",
  "isPrivate": false
}'
```

**Response**

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

```json
{
  type: "evm",
  address: "0x9D212340B4E5F3a38B171b2971D678B95083d928",
  name: "test",
  share: "0xdb87682d04e71c99bf8964e51cf5ec4843f2c588946bf749324819df0ba9ee1fb66e67bc21b440817310724abd1fb41676f6e2b30923819cf647bcb94a097c2b148257c58f38280682419af6c1fdab21cb94827bab4c0c4fa1d6ce153635b591a8b5c706f2db8307d08bc54022d381599a661c11dc5ab39fd0fbc167aac81ed48b211e98192cb0b6344232a4c577a7754339be",
}
```

{% endtab %}
{% endtabs %}

***

### Update wallets

<mark style="color:green;">`PUT`</mark> `/v1/clusters/wallets/names`

Update wallets in a cluster that the authenticated wallet is in

<table><thead><tr><th width="385.23046875">Name</th><th>Description</th></tr></thead><tbody><tr><td>HEADER <code>Authorization: Bearer {AUTHKEY}</code></td><td><a href="/pages/6AGkfCSOcnx2jBGZ0ljQ">Authentication from wallet</a></td></tr><tr><td><code>body</code></td><td>{ address: string, name: string }[]</td></tr></tbody></table>

**Example**

```bash
curl --request PUT \
  --url 'https://api.clusters.xyz/v1/clusters/wallets/names' \
  --header 'Authorization: Bearer AUTHKEY' \
  --header 'Content-Type: application/json' \
  --data '[{
  "address": "0x0000000000000000000000000000000000000011",
  "name": "new"
}]'
```

**Response**

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

```json
{
    "success": true,
}
```

{% endtab %}
{% endtabs %}

## Remove wallets

<mark style="color:green;">`DELETE`</mark> `/v1/clusters/wallets`

Remove an array of addresses from the cluster of the authenticated wallet

<table><thead><tr><th width="385.23046875">Name</th><th>Description</th></tr></thead><tbody><tr><td>HEADER <code>Authorization: Bearer {AUTHKEY}</code></td><td><a href="/pages/6AGkfCSOcnx2jBGZ0ljQ">Authentication from wallet</a></td></tr><tr><td><code>body</code></td><td>string[]</td></tr></tbody></table>

**Example**

```bash
curl --request DELETE \
  --url 'https://api.clusters.xyz/v1/clusters/wallets' \
  --header 'Authorization: Bearer AUTHKEY' \
  --header 'Content-Type: application/json' \
  --data '["0x0000000000000000000000000000000000000011"]
}]'
```

**Response**

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

```json
{
    "success": true,
}
```

{% endtab %}
{% endtabs %}

## Verify wallet

<mark style="color:green;">`POST`</mark> `/v1/clusters/verify/:clusterId`

Verify the authenticated wallet in a specific cluster

<table><thead><tr><th width="385.23046875">Name</th><th>Description</th></tr></thead><tbody><tr><td>HEADER <code>Authorization: Bearer {AUTHKEY}</code></td><td><a href="/pages/6AGkfCSOcnx2jBGZ0ljQ">Authentication from wallet</a></td></tr><tr><td><code>clusterId</code></td><td>The cluster id you want to verify the wallet in</td></tr></tbody></table>

**Example**

```bash
curl --request POST \
  --url https://api.clusters.xyz/v1/clusters/verify/0x0 \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer AUTHKEY' \
}'
```

**Response**

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

```json
{
    "success": true,
}
```

{% endtab %}
{% endtabs %}


# Address → Cluster Name

{% hint style="info" %}
To enable testnet querying on Sepolia, add `?testnet=true` to the end of the URL
{% endhint %}

## Get name

<mark style="color:green;">`GET`</mark> `/v1/names/address/:address`

Get the cluster and wallet name based on an address

| Name      | Description           |
| --------- | --------------------- |
| `address` | EVM or Solana address |

**Example**

```http
https://api.clusters.xyz/v1/names/address/0x5755d1dcea21caa687339c305d143e6e78f96adf
```

**Response**

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

```json
{
    "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
    "type": "evm",
    "clusterName": "cluster",
    "walletName": "main",
    "isVerified": true
}
```

{% endtab %}

{% tab title="404" %}

```json
{
  "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
  "type": "evm",
  "clusterName": null,
  "walletName": null
}
```

{% endtab %}
{% endtabs %}

***

## Get names by owner

<mark style="color:green;">`GET`</mark> `/v1/names/owner/address/:address`

Get all clusters and wallet names from a single wallet address

|           |                       |
| --------- | --------------------- |
| `address` | EVM or Solana address |

**Example**

```http
https://api.clusters.xyz/v1/names/owner/address/0x5755d1dcea21caa687339c305d143e6e78f96adf
```

**Response**

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

```json
[
    {
        "name": "cypherpunks/satoshi",
        "owner": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
        "totalWeiAmount": "0",
        "createdAt": "2025-04-28 18:10:05.805+00",
        "updatedAt": "2025-04-28 18:10:05.805+00",
        "updatedBy": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
        "isTestnet": false,
        "clusterId": "0x...",
        "expiresAt": null
    },
    {
        "name": "mclovin",
        "owner": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
        "totalWeiAmount": "0",
        "createdAt": "2025-04-28 18:10:05.805+00",
        "updatedAt": "2025-04-28 18:10:05.805+00",
        "updatedBy": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
        "isTestnet": false,
        "clusterId": "0x...",
        "expiresAt": null
    }
]
```

{% endtab %}
{% endtabs %}

***

## Bulk get names

<mark style="color:green;">`POST`</mark> `/v1/names/address`

Get the cluster and wallet name based on multiple addresses

| Name        | Description                          |
| ----------- | ------------------------------------ |
| POST `body` | Array of EVM and or Solana addresses |

**Example**

```bash
curl -X POST \
  'http://api.clusters.xyz/v1/names/address' \
  --data-raw '[
  "0x5755d1dcea21caa687339c305d143e6e78f96adf",
  "0xccdead94e8cf17de32044d9701c4f5668ad0bef9"
]'
```

**Response**

> Any addresses not found will have `clusterName: null`

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

```json
[
  {
    "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
    "type": "evm",
    "clusterName": "clusters",
    "walletName": "main",
    "isVerified": true
  },
  {
    "address": "0xccdead94e8cf17de32044d9701c4f5668ad0bef9",
    "type": "evm",
    "clusterName": "layerzero",
    "walletName": "main",
    "isVerified": true
  }
]
```

{% endtab %}

{% tab title="500" %}

```json
[]
```

{% endtab %}
{% endtabs %}

***


# Cluster Name → Address

{% hint style="info" %}
To enable testnet querying on Sepolia, add `?testnet=true` to the end of the URL
{% endhint %}

## Bulk get addresses

<mark style="color:green;">`POST`</mark> `/v1/names`

Get the wallets associated to a cluster name. You can pass just the cluster name or also include the address name.

| Name        | Description                                           |
| ----------- | ----------------------------------------------------- |
| POST `body` | Array of cluster names (with or without address name) |

**Example**

```bash
curl  -X POST \
  'https://api.clusters.xyz/v1/names' \
  --data-raw '[
  { name: "clusters" },
  { name: "clusters/main" },
  { name: "layerzero" },
  { name: "layerzero/main" }
]'
```

**Response**

> Any names in the array that are not found will be omitted from the response.

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

```json
[
  {
    "name": "clusters",
    "type": "evm",
    "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
    "clusterName": "clusters",
    "walletName": "main",
    "isVerified": true
  },
  {
    "name": "clusters/main",
    "type": "evm",
    "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
    "clusterName": "clusters",
    "walletName": "main",
    "isVerified": true
  },
  {
    "name": "layerzero",
    "type": "evm",
    "address": "0xccdead94e8cf17de32044d9701c4f5668ad0bef9",
    "clusterName": "layerzero",
    "walletName": "main",
    "isVerified": true
  },
  {
    "name": "layerzero",
    "type": "evm",
    "address": "0xccdead94e8cf17de32044d9701c4f5668ad0bef9",
    "clusterName": "layerzero",
    "walletName": "main",
    "isVerified": true
  },
]
```

{% endtab %}

{% tab title="500" %}

```json
[]
```

{% endtab %}
{% endtabs %}


# Registration

## Check availability

Check the availability of one or more clusters

<mark style="color:green;">`POST`</mark> `/v1/names/register/check`

| Name   | Description                             |
| ------ | --------------------------------------- |
| `BODY` | `string[]` - an array of names to check |

**Example**

```bash
curl --request POST \
  --url https://api.clusters.xyz/v1/names/register/check \
  --header 'Content-Type: application/json' \
  --data '["foobar", "namethatdoesntexist"]'
```

**Response**

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

```json
[
  {
    "name": "foobar",
    "isAvailable": false
  },
  {
    "name": "namethatdoesntexist",
    "isAvailable": true
  }
]
```

{% endtab %}
{% endtabs %}

***

## Registration data

<mark style="color:green;">`POST`</mark> `/v1/names/register/evm`

<mark style="color:green;">`POST`</mark> `/v1/names/register/solana`

Get the transaction data for registering a cluster so that a user can sign it

<table><thead><tr><th width="200">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>network</code></td><td><pre><code>"1"
"10"
"56"
"137"
"8453"
"81457"
"17000"
"42161"
"43114"
"11155111"
"solana"
</code></pre></td></tr><tr><td><code>sender</code></td><td>The sender's address</td></tr><tr><td><code>names</code></td><td><code>{ name: string; amountWei?: string }[]</code><br>bidAmountWei <em>(optional)</em> is always in ETH and defaults to 0.01 ETH</td></tr><tr><td><code>referralClusterId</code><br><em>(optional)</em></td><td>The cluster id that is referring this registration</td></tr></tbody></table>

{% tabs %}
{% tab title="Ethereum" %}
**Example**

```bash
curl --request POST \
  --url https://api.clusters.xyz/v1/names/register/evm \
  --header 'Content-Type: application/json' \
  --data '{
  "network": "1",
  "sender": "0x0000000000000000000000000000000000000001",
  "names": [
    { "name": "namethatdoesntexist1" },
    { "name": "namethatdoesntexist2" }
  ]
}'
```

**Response**

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

```json
{
  "type": "evm",
  "gasToken": {
    "symbol": "ETH",
    "decimals": 18
  },
  "transactionData": {
    "to": "0x00000000000e1a99dddd5610111884278bdbda1d",
    "data": "0x40bee835000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000026e616d6574686174646f65736e746578697374310000000000000000000000006e616d6574686174646f65736e74657869737432000000000000000000000000",
    "value": "20000000000000000"
  },
  "registrationFee": "20000000000000000",
  "bridgeFee": "0",
  "names": [
    {
      "name": "namethatdoesntexist1",
      "amountWei": "10000000000000000",
      "amount": "10000000000000000"
    },
    {
      "name": "namethatdoesntexist2",
      "amountWei": "10000000000000000",
      "amount": "10000000000000000"
    }
  ]
}
```

{% endtab %}

{% tab title="400/500" %}
**Names unavailable**

```json
{
  "status": 400,
  "message": "1 or more names are unavailable"
}
```

**Bridge Failure**

```json
{
  "status": 400,
  "message": "lz quote failed. most likely caused by wallets having insufficient balances"
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="Solana" %}
**Example**

```bash
curl --request POST \
  --url https://api.clusters.xyz/v1/names/register/solana \
  --header 'Content-Type: application/json' \
  --data '{
  "network": "solana",
  "sender": "1nc1nerator11111111111111111111111111111111",
  "names": [
    { "name": "namethatdoesntexist1" },
    { "name": "namethatdoesntexist2" }
  ]
}'
```

**Response**

{% hint style="warning" %}
Notice when using solana, `transactionData` is an array of transactions instead of a single object on other networks. This is because only one name is supported per transaction at the moment when using solana.
{% endhint %}

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

```json
{
  "type": "solana",
  "gasToken": {
    "symbol": "SOL"
  },
  "transactionData": [
    "gAEAAgQAM5ByjTQRYHm9yRG//wDb1E0uzcz3nKbhADjhAAAAAC/u4TnLEmhvVrNvLTdpdpVdJLegH99q7vYkM2iDi+hFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFSlNamSkhBk0k6HFg2jh8fDW13bySu4HkH6hAQQVEjZdvwyMMJHbJTNyHX/nuAnbanfNbTqWDof5avLEj3/dwAgICAAEMAgAAAFR9jwgAAAAAAwEAqgF7InR5cGUiOiJiaWQiLCJuYW1lIjoibmFtZXRoYXRkb2VzbnRleGlzdDEiLCJ3ZWkiOjEwMDAwMDAwMDAwMDAwMDAwLCJ0aW1lc3RhbXAiOjE3NDMwMDgwOTcsInNpZ25hdHVyZSI6ImViM2NhNjBkMDE5OWQwNWVkMzVkNGJjNGFhNTI1Yzk4MmVlYzI1M2I4MjIyMzE0MjBhNzNlZGQ1NzAzOWVmYmMifQA=",
    "gAEAAgQAM5ByjTQRYHm9yRG//wDb1E0uzcz3nKbhADjhAAAAAC/u4TnLEmhvVrNvLTdpdpVdJLegH99q7vYkM2iDi+hFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFSlNamSkhBk0k6HFg2jh8fDW13bySu4HkH6hAQQVEjZdvwyMMJHbJTNyHX/nuAnbanfNbTqWDof5avLEj3/dwAgICAAEMAgAAAFR9jwgAAAAAAwEAqgF7InR5cGUiOiJiaWQiLCJuYW1lIjoibmFtZXRoYXRkb2VzbnRleGlzdDIiLCJ3ZWkiOjEwMDAwMDAwMDAwMDAwMDAwLCJ0aW1lc3RhbXAiOjE3NDMwMDgwOTcsInNpZ25hdHVyZSI6ImViM2NhNjBkMDE5OWQwNWVkMzVkNGJjNGFhNTI1Yzk4MmVlYzI1M2I4MjIyMzE0MjBhNzNlZGQ1NzAzOWVmYmMifQA="
  ],
  "registrationFee": "287242920",
  "bridgeFee": "0",
  "names": [
    {
      "name": "namethatdoesntexist1",
      "amountWei": "10000000000000000",
      "amount": "143621460"
    },
    {
      "name": "namethatdoesntexist2",
      "amountWei": "10000000000000000",
      "amount": "143621460"
    }
  ]
}
```

{% endtab %}

{% tab title="500" %}
**Names unavailable**

```json
{
  "status": 500,
  "error": "names_unavailable",
  "message": "1 or more names are unavailable"
}
```

**Bridge Failure**

```json
{
  "status": 500,
  "error": "lz_quote_fail",
  "message": "lz quote failed. most likely caused by wallets having insufficient balances"
}
```

{% endtab %}
{% endtabs %}

**Using transaction data**

```javascript
for (const transaction of data.transactionData) {
   const deserializedMessage = web3.VersionedMessage.deserialize(
      Buffer.from(transaction, 'base64')
   );
   const newTransaction = new web3.VersionedTransaction(deserializedMessage);
   
   const signedTransaction = await $Wallet.solanaProvider.signTransaction(newTransaction);
   await connection.sendTransaction(signedTransaction);
}
```

{% endtab %}
{% endtabs %}

***

## Trasaction Status

Checks the status of a transaction

<mark style="color:green;">`GET`</mark> `/v1/names/register/tx/:tx`

{% hint style="info" %}
For now, if you registered more than one name in a transaction, it'll only give you the status of the first name. To enable testnet querying on Sepolia, add `?testnet=true` to the end of the URL
{% endhint %}

**Example**

```bash
curl https://api.clusters.xyz/v1/names/register/tx/0xffea3b29c64016772b7d3194b3c3899a62191e654990b6e3393b410667102284
```

**Response**

```typescript
type status = 'not_found' | 'bridging' | 'invalid' | 'finalized'
```

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

```json
{
  "tx": "0xffea3b29c64016772b7d3194b3c3899a62191e654990b6e3393b410667102284",
  "status": "finalized"
}
```

{% endtab %}
{% endtabs %}


# Communities

## Check availability

Check the availability of a community cluster name

<mark style="color:green;">`GET`</mark> `/v1/names/community/:communityName/check/:name`

**Example**

```bash
curl https://api.clusters.xyz/v1/names/community/pengu/check/myname
```

**Response**

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

```json
{
  "name": "pengu/myname",
  "isAvailable": false
}
```

{% endtab %}
{% endtabs %}

***

## Register a community name

Check the availability of one or more clusters

<mark style="color:green;">`POST`</mark> `/v1/names/community/:communityName/register`

| Name                                     | Description                                                                                                                                               |
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| HEADER `Authorization: Bearer {AUTHKEY}` | Authentication key                                                                                                                                        |
| `name`                                   | The name you want to register                                                                                                                             |
| `walletAddress` *(optional)*             | If you are the admin of the community, you can register on behalf of someone. If this is left out the name will be registered to the authenticated wallet |

**Example**

```bash
curl --request POST \
  --url https://api.clusters.xyz/v1/names/community/pengu/register \
  --header 'Authorization: Bearer AUTHKEY'
  --header 'Content-Type: application/json' \
  --data '{ "name": "myname" }'
```

**Response**

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

```json
{
  "clusterName": "pengu/myname",
  "owner": "0x123"
}
```

{% endtab %}
{% endtabs %}


# Event Indexing

## Events

<mark style="color:green;">`GET`</mark> `/v1/events`

Get a list of historical events that happen on the Clusters application that can be replayed to get the full state.

| Name                         | Description                                   |
| ---------------------------- | --------------------------------------------- |
| `nextPage` *(optional)*      | The next page attribute to retrieve more data |
| `limit` *(optional)*         | How many rows to return.  Defaults to `500`   |
| `from` *(optional)*          | The user who initiated the event              |
| `orderBy` *(optional)*       | `oldest` or `newest` Defaults to `oldest`     |
| `fromTimestamp` *(optional)* | Timestamp in ms to start at                   |

**Note:** If you use the `nextPage` query param, `fromTimestamp` will be ignored.

**Example**

```http
https://api.clusters.xyz/v1/events
```

**Response**

> List of events

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

```json
{
  "items": [
    {
      "id": "SqEhcVxrs2",
      "type": "ADD_WALLET",
      "from": "0x6ed7d526b020780f694f3c10dfb25e1b134d3215",
      "data": {
        "name": "main",
        "address": "0x6ed7d526b020780f694f3c10dfb25e1b134d3215",
        "cluster_id": "0x414d66466808501ce4eb380bcc67eddba8b3379363dafb635e5ccc5f90871fb5",
        "is_verified": true,
        "is_backed_up": false
      },
      "isTestnet": false,
      "timestamp": 1706797486000
    },
    {
      "id": "aSBcH9uPdG",
      "type": "REGISTER_NAME",
      "from": "0x822b5a721b4bcf8e627f0dc287c57f187d0b17dc",
      "data": {
        "name": "torje",
        "wei_amount": "10000000000000000"
      },
      "isTestnet": false,
      "timestamp": 1706799767000
    },
    {
      "id": "96HQxn4l5q",
      "type": "REGISTER_NAME",
      "from": "0xd64477d0f5fe7e6b8486a336fe694ef7789ab912",
      "data": {
        "name": "misconix",
        "wei_amount": "10000000000000000"
      },
      "isTestnet": false,
      "timestamp": 1706799779000
    }
  ],
  "nextPage": "1iW2vjkBqM"
}
```

{% endtab %}

{% tab title="500" %}

```json
null
```

{% endtab %}
{% endtabs %}


# v0.1 (Deprecated)

## API Endpoint

```
https://api.clusters.xyz
```

#### API KEY

{% hint style="info" %}
You can get an API Key by visiting <https://clusters.xyz/developer/api-keys>
{% endhint %}

If you have an API KEY, you can add it to the request headers like so:

```javascript
fetch(URL, { headers: { "X-API-KEY": "abc123" } }) 
```

***

## Authentication

{% hint style="info" %}
Currently, this is only required for [Managing Wallets](/getting-started/api/v0.1-deprecated/managing-wallets). If you do not need to manage wallets via the API, you can ignore this.
{% endhint %}

1. Get signing message
2. Sign the message with an EVM or Solana wallet
3. Get authentication key with the signature of the signed message
4. Using authenticate key with endpoints that require wallet authentication

***

**Get Signing Message**

<mark style="color:green;">`GET`</mark> `/v0.1/auth/message`

Get the signing message used for wallet signature authentication

**Example**

```bash
curl -X GET 'https://api.clusters.xyz/v0.1/auth/message'
```

**Response**

> The message to sign and the corresponding signing date

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

```json
{
  "message": "clusters.xyz verification\n\nBefore interacting with certain functionality, we require a wallet signature for verification.\n\n2024-05-14T19:08:27.985Z",
  "signingDate": "2024-05-14T19:08:27.985Z"
}
```

{% endtab %}
{% endtabs %}

***

**Get Authentication Key**

<mark style="color:green;">`POST`</mark> `/v0.1/auth/token`

Authenticate the ownership of a specific wallet

| Name          | Description                                      |
| ------------- | ------------------------------------------------ |
| `signature`   | Signed message signature                         |
| `signingDate` | Date given by signing message response           |
| `type`        | `evm` or `solana` (wallet type used for signing) |
| `wallet`      | Wallet that signed the message                   |

**Example**

```bash
curl --request POST \
  --url https://api.clusters.xyz/v0.1/auth/token \
  --header 'Content-Type: application/json' \
  --data '{
  "signature": "abc123",
  "signingDate": "2024-05-14T19:08:27.985Z",
  "type": "evm",
  "wallet": "0x0000000000000000000000000000000000000000"
}'
```

**Response**

> The auth key used for authenticated responses

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

```json
{
  "authenticationKey": "eyJzaWduYXR1cmUiOiJhYmMxMjMiLCJzaWduaW5nRGF0ZSI6IjIwMjQtMDUtMTRUMTk6MDg6MjcuOTg1WiIsInR5cGUiOiJldm0iLCJ3YWxsZXQiOiIweDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifQ=="
}
```

{% endtab %}
{% endtabs %}

***

**Validate Authentication Key**

<mark style="color:green;">`POST`</mark> `/v0.1/auth/validate`

Authenticate if a authentication key is valid or not

| Name                | Description        |
| ------------------- | ------------------ |
| HEADER `X-AUTH-KEY` | Authentication key |

**Example**

```bash
curl --request GET
--url https://api.clusters.xyz/v0.1/auth/validate
--header 'X-AUTH-KEY: eyJzaWduYXR1cmUiOiJhYmMxMjMiLCJzaWduaW5nRGF0ZSI6IjIwMjQtMDUtMTRUMTk6MDg6MjcuOTg1WiIsInR5cGUiOiJldm0iLCJ3YWxsZXQiOiIweDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifQ=='
```

**Response**

> The auth key used for authenticated responses

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

```json
{
  "signature": "abc123",
  "signingDate": "2024-05-14T19:08:27.985Z",
  "type": "evm",
  "wallet": "0x0000000000000000000000000000000000000000",
  "isValid": false
}
```

{% endtab %}
{% endtabs %}

***

{% content-ref url="/pages/4GHriRFjYt1bHz1AV6E4" %}
[Cluster → Address](/getting-started/api/v0.1-deprecated/cluster-address)
{% endcontent-ref %}

{% content-ref url="/pages/F1wmdghh29o2w1igZfQJ" %}
[Address → Cluster](/getting-started/api/v0.1-deprecated/address-cluster)
{% endcontent-ref %}

{% content-ref url="/pages/HYcYnu7eHLFrI0670GqQ" %}
[Cluster → Metadata](/getting-started/api/v0.1-deprecated/cluster-metadata)
{% endcontent-ref %}

{% content-ref url="/pages/lcbdSKQgWPL1O5lrMGW1" %}
[Registration](/getting-started/api/v0.1-deprecated/registration)
{% endcontent-ref %}

{% content-ref url="/pages/mrEMw3isVEhVodF5B0Qd" %}
[Event Indexing](/getting-started/api/v0.1-deprecated/event-indexing)
{% endcontent-ref %}

{% content-ref url="/pages/9EgLI0jhcXnwcBHmxR6D" %}
[Managing Wallets](/getting-started/api/v0.1-deprecated/managing-wallets)
{% endcontent-ref %}


# Address → Cluster

{% hint style="info" %}
To enable testnet querying on Sepolia, add `?testnet=true` to the end of the URL
{% endhint %}

## Get name

<mark style="color:green;">`GET`</mark> `/v0.1/name/:address`

Get the cluster and wallet name based on an address

| Name      | Description           |
| --------- | --------------------- |
| `address` | EVM or Solana address |

**Example**

```http
https://api.clusters.xyz/v0.1/name/0x5755d1dcea21caa687339c305d143e6e78f96adf
```

**Response**

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

```json
clusters/main
```

{% endtab %}

{% tab title="404" %}

```json
null
```

{% endtab %}
{% endtabs %}

***

## Bulk get names

<mark style="color:green;">`POST`</mark> `/v0.1/name/addresses`

Get the cluster and wallet name based on multiple addresses

| Name        | Description                          |
| ----------- | ------------------------------------ |
| POST `body` | Array of EVM and or Solana addresses |

**Example**

```bash
curl -X POST \
  'http://api.clusters.xyz/v0.1/name/addresses' \
  --data-raw '[
  "0x5755d1dcea21caa687339c305d143e6e78f96adf",
  "0xccdead94e8cf17de32044d9701c4f5668ad0bef9"
]'
```

**Response**

> Any addresses not found will have `name: null`

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

```json
[
  {
    "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
    "name": "clusters/main"
  },
  {
    "address": "0xccdead94e8cf17de32044d9701c4f5668ad0bef9",
    "name": "layerzero/main"
  }
]
```

{% endtab %}

{% tab title="500" %}

```json
[]
```

{% endtab %}
{% endtabs %}

***


# Cluster → Address

{% hint style="info" %}
To enable testnet querying on Sepolia, add `?testnet=true` to the end of the URL
{% endhint %}

## Get address

<mark style="color:green;">`GET`</mark> `/v0.1/address/:name`

<mark style="color:green;">`GET`</mark> `/v0.1/address/:name/:addressName`

Get the wallet associated to a cluster name. You can pass just the cluster name or also include the address name as shown in the 2 examples.

| Name                       | Description                 |
| -------------------------- | --------------------------- |
| `name`                     | Name of the cluster         |
| `addressName` *(optional)* | Address name of the cluster |

**Example**

```http
https://api.clusters.xyz/v0.1/address/clusters
https://api.clusters.xyz/v0.1/address/clusters/main
```

**Response**

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

```json
{
  "type": "evm",
  "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
  "name": "clusters/main",
  "isVerified": true
}
```

{% endtab %}

{% tab title="404" %}

```json
null
```

{% endtab %}
{% endtabs %}

***

## Bulk get addresses

<mark style="color:green;">`POST`</mark> `/v0.1/address/names`

Get the wallets associated to a cluster name. You can pass just the cluster name or also include the address name.

| Name        | Description                                           |
| ----------- | ----------------------------------------------------- |
| POST `body` | Array of cluster names (with or without address name) |

**Example**

```bash
curl  -X POST \
  'https://api.clusters.xyz/v0.1/address/names' \
  --data-raw '[
  "clusters/",
  "clusters/main",
  "layerzero/",
  "layerzero/main"
]'
```

**Response**

> Any names in the array that are not found will be omitted from the response.

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

```json
[
  {
    "name": "clusters/",
    "type": "evm",
    "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
    "isVerified": true
  },
  {
    "name": "clusters/main",
    "type": "evm",
    "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
    "isVerified": true
  },
  {
    "name": "layerzero/",
    "type": "evm",
    "address": "0xccdead94e8cf17de32044d9701c4f5668ad0bef9",
    "isVerified": true
  },
  {
    "name": "layerzero/main",
    "type": "evm",
    "address": "0xccdead94e8cf17de32044d9701c4f5668ad0bef9",
    "isVerified": true
  }
]
```

{% endtab %}

{% tab title="500" %}

```json
[]
```

{% endtab %}
{% endtabs %}


# Cluster → Metadata

{% hint style="info" %}
To enable testnet querying on Sepolia, add `?testnet=true` to the end of the URL
{% endhint %}

## Get cluster

<mark style="color:green;">`GET`</mark> `/v0.1/cluster/:name`

Get the cluster from a cluster name

| Name   | Description         |
| ------ | ------------------- |
| `name` | Name of the cluster |

**Example**

```http
https://api.clusters.xyz/v0.1/cluster/:name
```

**Response**

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

```json
{
  "name": "clusters/",
  "profileUrl": "https://clusters.xyz/clusters",
  "imageUrl": "https://cdn.clusters.xyz/profile/clusters",
  "hasCustomImage": false,
  "wallets": [
    {
      "type": "evm",
      "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
      "name": "clusters/main",
      "isVerified": true
    }
  ]
}
```

{% endtab %}

{% tab title="404" %}

```json
null
```

{% endtab %}
{% endtabs %}

***

## Bulk get clusters

<mark style="color:green;">`POST`</mark> `/v0.1/cluster/names`

Get the cluster from a cluster name

| Name        | Description            |
| ----------- | ---------------------- |
| POST `body` | Array of cluster names |

**Example**

```bash
curl  -X POST \
  'https://api.clusters.xyz/v0.1/cluster/names' \
  --data-raw '[
  "clusters/",
  "layerzero/"
]'
```

**Response**

> Any names in the array that are not found will be omitted from the response.

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

```json
[
  {
    "name": "clusters/",
    "profileUrl": "https://clusters.xyz/clusters",
    "imageUrl": "https://cdn.clusters.xyz/profile/clusters",
    "hasCustomImage": false,
    "wallets": [
      {
        "name": "clusters/main",
        "type": "evm",
        "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
        "isVerified": true
      }
    ]
  },
  {
    "name": "layerzero/",
    "profileUrl": "https://clusters.xyz/layerzero",
    "imageUrl": "https://cdn.clusters.xyz/profile/layerzero",
    "hasCustomImage": true,
    "wallets": [
      {
        "name": "layerzero/main",
        "type": "evm",
        "address": "0xccdead94e8cf17de32044d9701c4f5668ad0bef9",
        "isVerified": true
      }
    ]
  }
]
```

{% endtab %}

{% tab title="500" %}

```json
[]
```

{% endtab %}
{% endtabs %}


# Registration

## Check availability

Check the availability of one or more clusters

<mark style="color:green;">`GET`</mark> `/v0.1/register/check/:name`

<mark style="color:green;">`POST`</mark> `/v0.1/register/check`

| Name   | Description                             |
| ------ | --------------------------------------- |
| `BODY` | `string[]` - an array of names to check |

**Example**

```bash
# Single check
curl https://api.clusters.xyz/v0.1/register/check/namethatdoesntexist

# Bulk Check
curl --request POST \
  --url https://api.clusters.xyz/v0.1/register/check \
  --header 'Content-Type: application/json' \
  --data '["foobar", "namethatdoesntexist"]'
```

**Response**

{% tabs %}
{% tab title="Single Check" %}

```json
{
  "name": "namethatdoesntexist",
  "isAvailable": true
}
```

{% endtab %}

{% tab title="Bulk Check" %}

```
[
  {
    "name": "foobar",
    "isAvailable": false
  },
  {
    "name": "namethatdoesntexist",
    "isAvailable": true
  }
]
```

{% endtab %}
{% endtabs %}

***

## Register Transaction Data

<mark style="color:green;">`POST`</mark> `/v0.1/register`

Get the transaction data for registering a cluster so that a user can sign it

<table><thead><tr><th width="200">Name</th><th>Description</th></tr></thead><tbody><tr><td><code>network</code></td><td><pre><code>"1"
"10"
"56"
"137"
"8453"
"81457"
"17000"
"42161"
"43114"
"11155111"
"solana"
</code></pre></td></tr><tr><td><code>sender</code></td><td>The sender's address (EVM or Solana)</td></tr><tr><td><code>names</code></td><td><code>{ name: string; bidAmountWei?: string }[]</code><br>bidAmountWei <em>(optional)</em> is always in ETH</td></tr><tr><td><code>referralAddress</code><br><em>(optional)</em></td><td>An address that would get credit for this registration (EVM or Solana)</td></tr></tbody></table>

{% tabs %}
{% tab title="Ethereum - Mainnet" %}
**Example**

```bash
curl --request POST \
  --url https://api.clusters.xyz/v0.1/register \
  --header 'Content-Type: application/json' \
  --data '{
  "network": "1",
  "sender": "0x0000000000000000000000000000000000000001",
  "names": [
    { "name": "namethatdoesntexist1" },
    { "name": "namethatdoesntexist2" }
  ]
}'
```

**Response**

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

```json
{
  "gasToken": {
    "symbol": "ETH",
    "decimals": 18
  },
  "transactionData": {
    "to": "0x00000000000e1a99dddd5610111884278bdbda1d",
    "data": "0x40bee835000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000002386f26fc10000000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000000000000000026e616d6574686174646f65736e746578697374310000000000000000000000006e616d6574686174646f65736e74657869737432000000000000000000000000",
    "value": "20000000000000000"
  },
  "registrationFee": "20000000000000000",
  "bridgeFee": "0",
  "names": [
    {
      "name": "namethatdoesntexist1",
      "amount": "10000000000000000"
    },
    {
      "name": "namethatdoesntexist2",
      "amount": "10000000000000000"
    }
  ]
}
```

{% endtab %}

{% tab title="500" %}
**Names unavailable**

```json
{
  "status": 500,
  "error": "names_unavailable",
  "message": "1 or more names are unavailable"
}
```

**Bridge Failure**

```json
{
  "status": 500,
  "error": "lz_quote_fail",
  "message": "lz quote failed. most likely caused by wallets having insufficient balances"
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="Ethereum - Bridging " %}
{% hint style="info" %}
**Note**: Make sure the sender has enough funds to cover the transaction or the endpoint will result in an error from the bridge.
{% endhint %}

**Example**

```bash
curl --request POST \
  --url https://api.clusters.xyz/v0.1/register \
  --header 'Content-Type: application/json' \
  --data '{
  "network": "56",
  "sender": "0x0000000000000000000000000000000000000001",
  "names": [
    { "name": "namethatdoesntexist1" },
    { "name": "namethatdoesntexist2" }
  ]
}'
```

**Response**

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

```json
{
  "gasToken": {
    "symbol": "BNB",
    "decimals": 18
  },
  "transactionData": {
    "to": "0x00000000000e1a99dddd5610111884278bdbda1d",
    "data": "0xef76678a00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000010440bee835000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000e00b7847192c0000000000000000000000000000000000000000000000000000e00b7847192c0000000000000000000000000000000000000000000000000000000000000000026e616d6574686174646f65736e746578697374310000000000000000000000006e616d6574686174646f65736e746578697374320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000260003010021010000000000000000000000000000fde8000000000000000001c016f08e3258000000000000000000000000000000000000000000000000000000",
    "value": "1011176496282622517"
  },
  "registrationFee": "126126012000000000",
  "bridgeFee": "885050484282622517",
  "names": [
    {
      "name": "namethatdoesntexist1",
      "amount": "63063006000000000"
    },
    {
      "name": "namethatdoesntexist2",
      "amount": "63063006000000000"
    }
  ]
}
```

{% endtab %}

{% tab title="500" %}
**Names unavailable**

```json
{
  "status": 500,
  "error": "names_unavailable",
  "message": "1 or more names are unavailable"
}
```

**Bridge Failure**

```json
{
  "status": 500,
  "error": "lz_quote_fail",
  "message": "lz quote failed. most likely caused by wallets having insufficient balances"
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="Solana" %}
**Example**

```bash
curl --request POST \
  --url https://api.clusters.xyz/v0.1/register \
  --header 'Content-Type: application/json' \
  --data '{
  "network": "solana",
  "sender": "dead111111111111111111111111111111111111111",
  "names": [
    { "name": "namethatdoesntexist1" },
    { "name": "namethatdoesntexist2" }
  ]
}'
```

**Response**

{% hint style="warning" %}
Notice when using solana, `transactionData` is an array of transactions instead of a single object on other networks. This is because only one name is supported per transaction at the moment when using solana.
{% endhint %}

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

```json
{
  "gasToken": {
    "symbol": "SOL",
    "decimals": "lamports"
  },
  "transactionData": [
    { ...Transaction },
    { ...Transaction }
  ],
  "registrationFee": "443581060",
  "bridgeFee": "0",
  "names": [
    {
      "name": "namethatdoesntexist1",
      "amount": "221790530"
    },
    {
      "name": "namethatdoesntexist2",
      "amount": "221790530"
    }
  ]
}
```

{% endtab %}

{% tab title="500" %}
**Names unavailable**

```json
{
  "status": 500,
  "error": "names_unavailable",
  "message": "1 or more names are unavailable"
}
```

**Bridge Failure**

```json
{
  "status": 500,
  "error": "lz_quote_fail",
  "message": "lz quote failed. most likely caused by wallets having insufficient balances"
}
```

{% endtab %}
{% endtabs %}
{% endtab %}
{% endtabs %}

***

## Trasaction Status

Checks the status of a transaction

<mark style="color:green;">`GET`</mark> `/v0.1/register/tx/:tx`

{% hint style="info" %}
For now, if you registered more than one name in a transaction, it'll only give you the status of the first name. To enable testnet querying on Sepolia, add `?testnet=true` to the end of the URL
{% endhint %}

**Example**

```bash
curl https://api.clusters.xyz/v0.1/register/tx/0xffea3b29c64016772b7d3194b3c3899a62191e654990b6e3393b410667102284
```

**Response**

```typescript
type status = 'not_found' | 'pending' | 'bridging' | 'invalid' | 'lost_bid' | 'finalized'
```

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

```json
{
  "tx": "0xffea3b29c64016772b7d3194b3c3899a62191e654990b6e3393b410667102284",
  "status": "finalized"
}
```

{% endtab %}
{% endtabs %}


# Event Indexing

Events are updated every minute

## Events

<mark style="color:green;">`GET`</mark> `/v0.1/events`

Get a list of historical events that happen on the Clusters application that can be replayed to get the full state.

| Name                         | Description                                  |
| ---------------------------- | -------------------------------------------- |
| `fromTimestamp` *(optional)* | The unix timestamp to start your filter from |
| `nextPage` *(optional)*      | Specify the next page in the response        |
| `sortBy` *(optional)*        | `oldest` or `newest` Defaults to `oldest`    |
| `limit` *(optional)*         | How many rows to return.  Defaults to `1000` |

**Note:** If you use the `nextPage` query param, `fromTimestamp` will be ignored.

**Example**

```http
https://api.clusters.xyz/v0.1/events
```

**Response**

> List of events

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

```json
{
  "nextPage": "jXZ3XA7TgI",
  "items": [
    {
      "eventType": "register",
      "clusterId": 3,
      "bytes32Address": "0x000000000000000000000000822b5a721b4bcf8e627f0dc287c57f187d0b17dc",
      "address": "0x822b5a721b4bcf8e627f0dc287c57f187d0b17dc",
      "addressType": "evm",
      "data": {
        "name": "torje",
        "weiAmount": 10000000000000000
      },
      "timestamp": 1706799767
    },
    {
      "eventType": "register",
      "clusterId": 4,
      "bytes32Address": "0x000000000000000000000000d64477d0f5fe7e6b8486a336fe694ef7789ab912",
      "address": "0xd64477d0f5fe7e6b8486a336fe694ef7789ab912",
      "addressType": "evm",
      "data": {
        "name": "misconix",
        "weiAmount": 10000000000000000
      },
      "timestamp": 1706799779
    },
    {
      "eventType": "updateWallet",
      "clusterId": 2,
      "bytes32Address": "0x000000000000000000000000000000006533ecb269f92a09d8541e84e485332f",
      "address": "0x000000006533ecb269f92a09d8541e84e485332f",
      "addressType": "evm",
      "data": {
        "name": "main",
        "isVerified": 1
      },
      "timestamp": 1706799790
    },
  ]
}
```

{% endtab %}

{% tab title="500" %}

```json
null
```

{% endtab %}
{% endtabs %}


# Managing Wallets

## Add, update, or delete wallets

{% hint style="info" %}
This endpoint is in beta and is likely to change. Reach out if you'd like to use this endpoint.
{% endhint %}

<mark style="color:green;">`POST`</mark> `/v0.1/cluster/:name/update/wallets`

Update wallets in your cluster

<table><thead><tr><th width="249">Name</th><th>Description</th></tr></thead><tbody><tr><td>HEADER <code>X-AUTH-KEY</code></td><td><a href="/pages/GvLe79HZ0PXI5WTSVCdV#authentication">Authentication key from wallet</a></td></tr><tr><td><code>add</code></td><td><code>{ name: string; address: string }[]</code></td></tr><tr><td><code>update</code></td><td><code>{ name: string; address: string }[]</code></td></tr><tr><td><code>remove</code></td><td><code>string[]</code> (array of addresses)</td></tr></tbody></table>

**Example**

```bash
curl --request POST \
  --url https://api.clusters.xyz/v0.1/cluster/layerzero/update/wallets \
  --header 'Content-Type: application/json' \
  --header 'X-AUTH-KEY: eyJzaWduYXR1cmUiOiJhYmMxMjMiLCJzaWduaW5nRGF0ZSI6IjIwMjQtMDUtMTRUMTk6MDg6MjcuOTg1WiIsInR5cGUiOiJldm0iLCJ3YWxsZXQiOiIweDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifQ=' \
  --data '{
  "add": [{
    "name": "hot",
    "address": "0x0000000000000000000000000000000000000000"
  }],
  "update": [],
  "remove": []
}'
```

**Response**

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

```json
201
```

{% endtab %}

{% tab title="500" %}

```json
500
```

{% endtab %}
{% endtabs %}


# Communities

{% hint style="info" %}
Currently only available on Sepolia and Holesky as we roll out the feature
{% endhint %}

## Check availability

Check the availability of a single community cluster

<mark style="color:green;">`GET`</mark> `/check/availability/:clusterName/:walletName`

**Example**

{% code title="Single check" %}

```bash
curl https://api.clusters.xyz/v0.1/community/check/availability/mycommunity/foobar?testnet=true
```

{% endcode %}

**Response**

{% tabs %}
{% tab title="Single Check" %}

```json
{
  "name": "mycommunity/foobar",
  "isAvailable": true
}
```

{% endtab %}
{% endtabs %}

***

## Check requirements

Check if the community requirements for a specific wallet has been met for registration

<mark style="color:green;">`GET`</mark> `/check/requirements/:clusterName?walletAddress=:walletName`

**Example**

```bash
curl https://api.clusters.xyz/v0.1/community/check/requirements/mycommunity?walletAddress=0x123?testnet=true
```

**Response**

```json
{
  "clusterName": "mycommunity",
  "walletAddress": "0x123",
  "isRequirementsMet": true
}
```

***

## Registration

<mark style="color:green;">`POST`</mark> `/v0.1/community/register`

Either registers a free community clusters OR returns transaction data for registrations that require payment.

<table><thead><tr><th width="226">Name</th><th>Description</th></tr></thead><tbody><tr><td>HEADER <em>(bearer)</em> <code>Authorization</code></td><td><a href="/pages/GvLe79HZ0PXI5WTSVCdV#authentication">Authentication key from wallet</a></td></tr><tr><td><code>clusterName</code></td><td>The cluster name of the community being registered</td></tr><tr><td><code>walletName</code></td><td>The wallet name being registered under the community</td></tr><tr><td><code>chainId</code><br><em>(optional)</em></td><td>If the price of a community cluster is USDC, a chainId should be passed so that the correct contracts are used when returning transaction data. It will default to mainnet.</td></tr><tr><td><code>walletAddress</code><br><em>(optional)</em></td><td>If the registration sign up's are restricted to community owners, a wallet address needs to be included to know who to register the name to.</td></tr></tbody></table>

{% tabs %}
{% tab title="Free" %}
**Example**

```bash
curl --request POST \
  --url 'https://api.clusters.xyz/v0.1/community/register?testnet=true' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer abc123' \
  --data '{
  "clusterName": "mycommunity",
  "walletName": "foobar"
}'
```

**Response**

```json
{ 
   "clusterName": "mycommunity",
   "walletName": "foobar"
}
```

{% endtab %}

{% tab title="EVM payment" %}
**Example**

```bash
curl --request POST \
  --url 'https://api.clusters.xyz/v0.1/community/register?testnet=true' \
  --header 'Content-Type: application/json' \
  --header 'X-AUTH-KEY: abc123' \
  --data '{
  "clusterName": "mycommunity",
  "walletName": "foobar",
  "chainId": "11155111"
}'
```

**Response**

```typescript
{
  "clusterName": string;
  "walletName": string;
  "transactionInfo": {
    "type": "evm",
    "transaction": {
      "to": string;
      "data": `0x${string}`;
      "value": string;
    }
  }
}
```

**Usage**

```typescript
import { createWalletClient, custom } from 'viem'
import { sepolia } from 'viem/chains'

const url = 'https://api.clusters.xyz/v0.1/community/register?testnet=true';
const getRegister = await fetch(url, { 
  method: 'POST',
  headers: {
    'Content-type': 'application/json',
    'X-AUTH-KEY': 'abc123'
  },
  data: JSON.stringify({ clusterName, walletName })
})
const register = await getRegister.json();

const walletClient = createWalletClient({
  chain: sepolia,
  transport: custom(provider),
});

const { to, data, value } = register.transactionInfo.transaction;
const tx = await walletClient.sendTransaction({
  account,
  to,
  data,
  value
});
```

{% endtab %}

{% tab title="Solana Payment" %}
**Example**

```bash
curl --request POST \
  --url 'https://api.clusters.xyz/v0.1/community/register?testnet=true' \
  --header 'Content-Type: application/json' \
  --header 'X-AUTH-KEY: abc123' \
  --data '{
  "clusterName": "mycommunity",
  "walletName": "foobar",
  "chainId": "11155111"
}'
```

**Response**

```typescript
{
  "clusterName": string;
  "walletName": string;
  "transactionInfo": {
    "type": "solana",
    "transaction": {
      "data": string; // base64 string of serialized transactions
    }
  }
}
```

**Usage**

```typescript
import * as web3 from '@solana/web3.js';

const message = web3.VersionedMessage.deserialize(
  Buffer.from(register.transactionInfo.transaction, 'base64'),
);
const transaction = new web3.VersionedTransaction(message);
const { signature } = await provider.signAndSendTransaction(
  transaction,
);
```

{% endtab %}

{% tab title="Error handling" %}
**Testnet only -** We are in beta only phase of community clusters. You'll receive this error if testnet is not defined

```json
{ error: "testnet_only" }
```

**Community not found -** If the specific cluster name is not a community

```
{ error: "not_found" }
```

**Not a verified user -** If sign ups are restricted to community owners and the auth key is not from an owner

```
{ error: "not_verified" }
```

**No wallet address found -** If sign ups are restricted to community owners and no wallet address was included in the body of the request

```
{ error: "no_walletAddress_found"}
```

**Registration has not started yet -** If sign ups for this community have not started yet

```
{ error: "not_active" }
```

**Name is not available -** If the name is taken

```
{ error: "not_available" }
```

**Requirements have not been met -** If the user registering the name has not met the requirements

```
{ error: "requirements_not_met" }
```

**Invalid chain id -** If the chain submitted in the request is invalid

```
{ error: "invalid_chain_id" }
```

{% endtab %}
{% endtabs %}

***

## Transaction Status

Checks the status of a transaction

<mark style="color:green;">`GET`</mark> `/v0.1/community/tx/:tx`

**Example**

```bash
curl https://api.clusters.xyz/v0.1/community/tx/0x123?testnet=true
```

**Response**

```typescript
type status = 'not_found' | 'pending' | 'bridging' | 'invalid' | 'lost_bid' | 'finalized'
```

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

```json
{
  "tx": "0x123",
  "status": "not_found"
}
```

{% endtab %}
{% endtabs %}


# Data Availability

{% hint style="info" %}
We'll be rolling out our data availability in conjunction with our Community Clusters launch!
{% endhint %}

<figure><img src="/files/qRcxcp2v5wuOPLWAkC6e" alt=""><figcaption><p>Architecture of DA</p></figcaption></figure>

Clusters are composed of a series of events that happen on and off chain. These events include:

* On chain personal cluster registration & extensions
* On & off chain community cluster registration & renewals
* Wallet management

### Data Hosting

[**Arweave**](https://www.arweave.org/) is where our data lives as a permanent data storage layer that is available for user consumption across \~7.2k nodes across the world.

### Data ingestion

Based on event throughput, data is pushed to the Arweave network every 1-3 hours. ([Sample tx](https://viewblock.io/arweave/tx/Jx4i7lZag5S7xRBNhyGcccdcE6Ikau5gHIpdgf5pNoc))

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

```json
[
  [1,"updateWallet",2224,"0x000000000000000000000000f4cb0399fd1b3c370a297fdc21090a28a1366608","0xf4cb0399fd1b3c370a297fdc21090a28a1366608","evm","main",1,1706810479],
  [1,"register",2288,"0x0000000000000000000000002d185421f556ea40bd67f5512cca15f2a793ba55","0x2d185421f556ea40bd67f5512cca15f2a793ba55","evm","peapol26",10000000000000000,1706810483]
]
```

{% endtab %}

{% tab title="Parsed" %}

```json
[
  {
    "eventType": "updateWallet",
    "clusterId": 2224,
    "bytes32Address": "0x000000000000000000000000f4cb0399fd1b3c370a297fdc21090a28a1366608",
    "address": "0xf4cb0399fd1b3c370a297fdc21090a28a1366608",
    "addressType": "evm",
    "data": {
      "name": "main",
      "weiAmount": 1,
    },
    "timestamp": 1706810479
  },
  {
    "eventType": "register",
    "clusterId": 2288,
    "bytes32Address": "0x0000000000000000000000002d185421f556ea40bd67f5512cca15f2a793ba55",
    "address": "0x2d185421f556ea40bd67f5512cca15f2a793ba55",
    "addressType": "evm",
    "data": {
      "name": "peapol26",
      "weiAmount": 10000000000000000,
    },
    "timestamp": 1706810483
  }
]
```

{% endtab %}
{% endtabs %}

Once the data has successfully submitted, these transactions are picked up and added to a manifest.

```json
[
 "uWaE1nsM_hyZzARTc-br_5BXB6cfdWDcglJHd-b5pdg",
 "HIxCu-qBwEwnUiEeQso89X2ZqeqxPxSX_penY_B1UCY",
]
```

### Querying data

Querying data consists of:

1. Retrieving the last known transaction from our manifest uploader wallet *(this will include all the transactions that have been pushed to the data availability layer)*.&#x20;
2. In chronological order, retrieving each transaction in the manifest to keep a current record of all events

This data can be queried in different ways:

1. Make calls directly to an arweave node using the above steps
2. Use our SDK that consists of both a centralized and decentralized option to return pre-parsed DA layer events
   * `getEvents()` — Retrieve a centralized live version of the data availabiltiy events
   * `getEventsDA()` — Communicate directly with an arweave node to retrieve and parse data availability events
3. Use our RESTful API interface to retrieve a centralized version of the data availabiltiy events


# Smart Contracts

The protocol follows a hub-and-spoke model for global state propagation. Users can initiate any action from any initiator chain, then data is routed to the hub via LayerZero v2 bridge messaging and replicated out via data availability.

## Mainnet Smart Contracts

<table><thead><tr><th width="163">Chain</th><th width="442">Address</th><th>Type</th></tr></thead><tbody><tr><td>Ethereum</td><td>0x00000000000E1A99dDDd5610111884278BDBda1D</td><td>Hub</td></tr><tr><td>Arbitrum</td><td>0x00000000000E1A99dDDd5610111884278BDBda1D</td><td>Initiator</td></tr><tr><td>Base</td><td>0x00000000000E1A99dDDd5610111884278BDBda1D</td><td>Initiator</td></tr><tr><td>Optimism</td><td>0x00000000000E1A99dDDd5610111884278BDBda1D</td><td>Initiator</td></tr><tr><td>Avalanche</td><td>0x00000000000E1A99dDDd5610111884278BDBda1D</td><td>Initiator</td></tr><tr><td>Polygon</td><td>0x00000000000E1A99dDDd5610111884278BDBda1D</td><td>Initiator</td></tr><tr><td>Binance Chain</td><td>0x00000000000E1A99dDDd5610111884278BDBda1D</td><td>Initiator</td></tr><tr><td>Blast</td><td>0x00000000000E1A99dDDd5610111884278BDBda1D</td><td>Initiator</td></tr></tbody></table>

## Testnet Smart Contracts

<table><thead><tr><th width="164">Chain</th><th width="430">Address</th><th>Type</th></tr></thead><tbody><tr><td>Sepolia</td><td>0x00000000000E1A99dDDd5610111884278BDBda1D</td><td>Hub</td></tr><tr><td>Holesky</td><td>0x00000000000E1A99dDDd5610111884278BDBda1D</td><td>Initiator</td></tr></tbody></table>


# Address Types

## Address Type Support

<table><thead><tr><th width="310">VM</th><th>Chains</th></tr></thead><tbody><tr><td>Ethereum Virtual Machine (EVM)</td><td>Ethereum, Arbitrum, Base, Optimism, Avalanche, Polygon, Binance Chain, Blast, Fantom, Gnosis, Polygon zkEVM, Moonbeam, Moonriver, Sanko, Ronin, Hychain, Mantle, ZetaChain, Canto, Arbitrum One, Arbitrum Nova, Celo, Linea,  Taiko, Scroll, Zora, Sepolia Testnet, Holesky Testnet, Berachain Testnet</td></tr><tr><td>Solana Virtual Machine (SVM)</td><td>Solana</td></tr><tr><td>Bitcoin</td><td></td></tr><tr><td>Cosmos</td><td></td></tr><tr><td>NEAR</td><td></td></tr><tr><td>Dogecoin</td><td></td></tr><tr><td>Litecoin</td><td></td></tr><tr><td>Aptos</td><td></td></tr><tr><td>Ripple</td><td></td></tr><tr><td>Tron</td><td></td></tr><tr><td>Hedera</td><td></td></tr><tr><td>Stacks</td><td></td></tr><tr><td>Algorand</td><td></td></tr><tr><td>Filecoin</td><td></td></tr><tr><td>Arweave</td><td></td></tr></tbody></table>


# Protocol Mechanics (OLD)

Clusters Beta launched in Feb 2024 focused on initial name distribution. Clusters Prod launch is targeted for Q2 2024 focused on security, cost, and protocol decentralization.

## Beta Period (OVER)

Weekly auctions ending at Friday 3pm ET, highest bid for each name locks it in and will be eligible to receive/create it on prod launch. Bids in the last 10 minute of any auction will extend the auction for that specific name by 10 more minutes. Losing bids will be refunded, though any bridge/gas fee will not be.

Boosted bids to incentivize certain activity. Let’s walk through an example. Suppose I bid 0.1 eth on foobar/. I am the first bidder, I link my twitter pfp, and I refer five friends who bid 0.5 eth in total.:

* Core bid: 0.1
* First bidder: +1x the bid amount, or +0.1
* Link twitter pfp: +0.1x the bid amount, or +0.01
* Referral bids: 1x their bids

Rollout and beta testing of javascript SDK and REST API. Third-party integrations can happen during the beta period without waiting for prod launch.

## Prod Launch

First-come first-served name registration. Demand-based recurring pricing goes live. Users can receive/mint their beta names as onchain digital objects.


# Using Clusters for ETH->SOL Airdrops

### Overview

Crosschain airdrops (like airdropping a Solana SPL-20 coin to holders of an Ethereum or EVM-based NFT) are tricky because Solana addresses are different than Ethereum addresses. Users have to link, or cluster, their authenticated EVM and SOL addresses together to ensure the airdrop goes to the right place.

For example, with the Pudgy Penguin $PENGU airdrop, users held Ethereum NFTs and were eligible to claim a Solana SPL-20. They linked their wallets together, with an option for full privacy, using Clusters. Clusters powered over 1.3 million unique claim events and over $1 billion of value.

Using Clusters is better than building your own bespoke internal system because:

* Faster time to ship, we've already built this infra and run several top airdrops through it, like $PENGU and $ANIME
* Users can preregister ahead of time, reducing first-hour load on launch servers and improving both uptime & throughput
* Easy compatibility with delegate.xyz claims, which are critical for smart contract wallets like Gnosis Safe and 4337 wallets that cannot make an EIP-712 signature
* Battle-tested across literal billions of dollars worth of value already
* Reusable composable state, so your community becomes legible and can make even more money from third-party airdrops (or future first-party airdrops)

### How Clusters Works

Clusters are bundles of wallets, for example here is a cluster of one ETH address, one SOL address, and one BTC address:

```
[
    0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045, 
    HWEoBxYs7ssKuudEjzjmpfJVX7Dvi7wescFsVx2L5yoY, 
    1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa
]
```

A verified wallet can only be apart of one wallet at a time.

Any verified wallet in a bundle can publicly or privately add wallets to a bundle.

Clusters uses a bundling framework. Where a wallet creates a “bundle” they then can publicly or privately add wallets to the bundle.

1. **Authentication**

Clusters protocol authentication works via offline wallets based signatures via EVM or Solana wallets.

```tsx
// 1. Get signing message
POST /v1/auth/message

// 2. Get auth token from signed item
POST /v1/auth/token
body={ signature, signginDate, type, wallet }
```

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

2. **Creating a bundle**

`POST /v1/bundle`

The connected wallet will create a bundle.

*(Alternatively, a user can skip this step and go straight to adding wallets to a bundle. If no bundle exists, it will create one beforehand)*

<figure><img src="/files/X02FLTl2SnumanfTXA2U" alt=""><figcaption><p>The initial created bundle</p></figcaption></figure>

The initial created bundle

3. **Adding wallets privately OR publicly**

`POST /v1/bundle/wallets`

The authenticated wallet can add wallets of any type to the bundle they are in. These wallets can be public to everyone OR can be completely private and hidden to the outside world.

\**When privately adding wallets, no events will be emitted to our Data Availability Layer, completely shielding the identity of the wallet and their associated bundle connections.*

<figure><img src="/files/a1ZTzbolNCAUefMKnbR8" alt=""><figcaption><p>A sample address bundle</p></figcaption></figure>

4. **Claiming airdrops**

`GET /v1/airdrop/claim/doodles`

Any wallet that is apart of a bundle can authenticate themselves and privately call the associated airdrop endpoint. The clusters backend will determine what wallets are eligible by traversing all bundles they are associated with.

Return objects can be associated merkle roots, transaction call data, etc.

In the below example, solana wallet Gg1..fwz tries to claim an airdrop. The system determines it’s associated with EVM wallet 0x000…123. If this EVM wallet is eligible for a claim, we can process it accordingly.

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

5. **Post claim**

* Register the user a new community name `doodles/foobar`
* Assign the above bundle to the community name (`POST /bundles/assign`)


# Convert hex address to clusters name

Before you begin, install & setup the cluster imports at the top of your code.

### Install the SDK

```bash
npm install @clustersxyz/sdk viem
```

### Setup your imports

```javascript
import { Clusters } from "@clustersxyz/sdk";
const clusters = new Clusters();
```

***

### **A single address**

This single address call is great for getting the name of a user's connected wallet or the name of an address on a profile page.

<figure><img src="/files/4T3lDJR26bipUzFIr72B" alt=""><figcaption></figcaption></figure>

```javascript
// This will return { clusterName: "clusters", walletName: "main" }
const { clusterName, walletName } = await clusters.getName('0x5755d1dcea21caa687339c305d143e6e78f96adf');
```

2. **Profile Information**

You can use the following helper functions to help display the info you need. We offer 2 functions:

* A synchronous function to get the profile image url of a cluster name&#x20;
* A function to navigate the user to their official clusters profile page on clusters.xyz

You can use the following helper functions to help display the info you need

```javascript
import { getImageUrl, getProfileUrl } from "@clustersxyz/sdk";

// https://cdn.clusters.xyz/profile/clusters
const profileImage = getImageUrl(clusterName);

// https://clusters.xyz/clusters
const profileUrl = getProfileUrl(clusterName);
```

Clusters are a multi-wallet username. Sometimes you will want to display the other wallets a cluster has. By passing in the cluster name, you'll be able to retrieve an array of wallets along with their type.

When people create a cluster, they can add multiple addresses to it. If you want to be able to display this information, you can call the following function.

```javascript
const cluster = await clusters.getClusterByName('clusters');

/*
{
  "id": "0xa8d12b92b91fe0db3651ff2d45c1f47b1bb343054e9cd1e556c73f2330269224",
  "createdBy": "0x5cff9c1362a71247da33887be2a44ac36a8724bb",
  "createdAt": "2024-02-14 21:39:05+00",
  "wallets": [
    {
      "address": "0x5cff9c1362a71247da33887be2a44ac36a8724bb",
      "name": "main",
      "isVerified": true,
      "isPrivate": false,
      "isBackedUp": false,
      "updatedAt": "2024-02-09 15:21:22+00",
      "updatedBy": "0x5cff9c1362a71247da33887be2a44ac36a8724bb",
      "createdAt": "2024-02-09 15:21:22+00"
    }
  ],
  "isTestnet": false
}
*/
```

***

### **Multiple addresses in one call**

This is useful if you have 2 or more addresses on a page that you want to retrieve a cluster name for. You can get all the information you need in one call.

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

1. **Resolve multiple names**

```javascript
import { Clusters } from "@clustersxyz/sdk";
const clusters = new Clusters();

const clusterName = await clusters.getNames([
    '0x5755d1dcea21caa687339c305d143e6e78f96adf', 
    '0xccdead94e8cf17de32044d9701c4f5668ad0bef9'
]);

/*
[
  {
    "address": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
    "name": "clusters/main"
  },
  {
    "address": "0xccdead94e8cf17de32044d9701c4f5668ad0bef9",
    "name": "layerzero/main"
  }
]
*/
```


# Registering a name


# Ethereum Networks

We currently support the following networks for name registration

<table><thead><tr><th width="138">ID</th><th>Name</th></tr></thead><tbody><tr><td>1</td><td>Mainnet</td></tr><tr><td>10</td><td>Optimism</td></tr><tr><td>56</td><td>BNB Smart Chain</td></tr><tr><td>137</td><td>Polygon</td></tr><tr><td>8453</td><td>Base</td></tr><tr><td>42161</td><td>Arbitrum One</td></tr><tr><td>43114</td><td>Avalanche</td></tr><tr><td>11155111</td><td>Sepolia</td></tr><tr><td>17000</td><td>Holesky (testnet)</td></tr></tbody></table>

1. **Let the user decide what name they want**

<figure><img src="/files/0RvG1cLL8QtO3CQGhE23" alt=""><figcaption></figcaption></figure>

You'll want to provide some sort of input box that allows a user to see if the name they want is available.

2. **Check if the name is valid**

There are some restrictions for registering a name. In short, the name must fit in 32 bytes, must not have a space, or a /.

```javascript
import { isNameValid } from "@clustersxyz/sdk"

const validName = "clusters";
console.log(isNameValid(validName)); // true

const invalidName = "c/c/c/c";
console.log(isNameValid(invalidName)); // false
```

3. **Once you know the name is valid, make sure the name is not taken**

```javascript
import { isNameValid } from "@clustersxyz/sdk"

const name = "clusters";
const nameAvailability = await clusters.getNameAvailability([name]);
console.log(nameAvailability[0].isAvailable) // false
```

4. **Get the raw transaction data for the on-chain transaction**

This call will get you the on-chain transaction data to register the names you want at the minimum registration fee of 0.01 ETH. If you are on a chain, such as Polygon, that has a different gas token than ETH, you'll receive back the converted value in MATIC.

{% hint style="info" %}
If you want to impose a specific registration amount, you can do so in the names array. Check out the documentation on [getting the transaction data](/getting-started/javascript/registration#getregistrationtransaction-names-sender-network) to learn more.
{% endhint %}

```javascript
const names = [{ name: 'mynewname' }];
const signerAddress = '0x..123';
const chainId = '1';
const data = await clusters.getRegistrationTransactionEvm(names, signerAddress, chainId);
```

5. **You can now display the registration fee, bridge fee (if any), and what gas token the user will be paying with**

```typescript
import { formatUnits } from 'viem';

const registrationFee = formatUnits(BigInt(data.registrationFee), data.gasToken.decimals);
const bridgeFee = formatUnits(BigInt(data.bridgeFee), data.gasToken.decimals);

// Total Registration fee: 0.01 ETH
console.log(`Total Registration fee: ${registrationFee} ${data.gasToken.symbol}`);

// Bridge fee: 0 ETH
console.log(`Bridge fee: ${bridgeFee} ${data.gasToken.symbol}`);
```

6. **Submit the transaction**

```javascript
// https://viem.sh/docs/actions/wallet/sendTransaction

const hash = await walletClient.sendTransaction({
  data: data.tranasctionData.data, 
  account: signerAddress,
  to: data.tranasctionData.to,
  value: BigInt(data.transactionData.value)
})
```

7. **Track the status of the transaction**

Once the transaction is submitted, you can check when the registration has been processed. This is especially useful when bridging. An alternative method is pinging the `getNameAvailability()` function to see when the name becomes taken.

```javascript
const getStatus = await clusters.getTransactionStatus(hash);
console.log(getStatus.status) // finalized
```


# Solana

1. **Let the user decide what name they want**

<figure><img src="/files/0RvG1cLL8QtO3CQGhE23" alt=""><figcaption></figcaption></figure>

You'll want to provide some sort of input box that allows a user to see if the name they want is available.

2. **Check if the name is valid**

There are some restrictions for registering a name. In short, the name must fit in 32 bytes, must not have a space, or a /.

```javascript
import { isNameValid } from "@clustersxyz/sdk"

const validName = "clusters";
console.log(isNameValid(validName)); // true

const invalidName = "c/c/c/c";
console.log(isNameValid(invalidName)); // false
```

3. **Once you know the name is valid, make sure the name is not taken**

```javascript
import { isNameValid } from "@clustersxyz/sdk"

const name = "clusters";
const nameAvailability = await clusters.getNameAvailability([name]);
console.log(nameAvailability[0].isAvailable) // false
```

4. **Get the raw transaction data for the on-chain transaction**

This call will get you the on-chain transaction data to register the names you want at the minimum registration fee of 0.01 ETH (converted to lamports).

{% hint style="info" %}
If you want to impose a specific registration amount, you can do so in the names array. Input these amounts in Wei and they'll be converted to SOL. Check out the documentation on [getting the transaction data](/getting-started/javascript/registration#getregistrationtransaction-names-sender-network) to learn more.
{% endhint %}

```javascript
const names = [{ name: 'mynewname' }];
const signerAddress = 'A59..iHqw';
const data = await clusters.getRegistrationTransactionSolana(names, signerAddress);
```

5. **You can now display the registration fee**

```typescript
import { LAMPORTS_PER_SOL } from '@solana/web3.js';

const registrationFee = Number(data.registrationFee) / LAMPORTS_PER_SOL;

// Total Registration fee: 0.2248 SOL
console.log(`Total Registration fee: ${registrationFee} ${data.gasToken.symbol}`);
```

6. **Submit the transaction**

```javascript
// Example of a solana provider
// https://docs.phantom.app/solana/detecting-the-provider
const solanaProvider = window.phantom?.solana;

for (const transaction of data.transactionData) {
   const deserializedMessage = web3.VersionedMessage.deserialize(
      Buffer.from(transaction, 'base64')
   );
   const newTransaction = new web3.VersionedTransaction(deserializedMessage);
   
   const signedTransaction = await $Wallet.solanaProvider.signTransaction(newTransaction);
   const signature = await connection.sendTransaction(signedTransaction);
}
```

7. **Track the status of the transaction**

Once the transaction is submitted, you can check when the registration has been processed. An alternative method is pinging the `getNameAvailability()` function to see when the name becomes taken.

```javascript
const getStatus = await clusters.getTransactionStatus(signature);
console.log(getStatus.status) // finalized
```


# Whitelabel Communities Registration Flow

How to implement communities registration on your application with Clusters API v1

For projects that want to deploy a registration page quickly, we provide a simple settings panel where you configure some basic settings and be on your way. [More information on that here.](https://docs.clusters.xyz/introduction/features/communities)&#x20;

For projects that want to implement the community name registration directly into their app, this guide's for you!

***

## What's the Difference?

If you deploy a community name registration page through our settings panel, we'll host the page where you redirect users to when they need/want to claim a community name. In this flow, users register community names by signing a message.

If you implement the community name registration into your hosted app, the flow requires the Cluster owner's Authentication Key to claim a name on behalf of the user.

This simply means the owner's wallet needs to generate a secret key and manage it in your application.

The below guide walks through a NextJS app that enables users to register a name in a Clusters community, using the [Clusters API v1 - Communities](https://docs.clusters.xyz/getting-started/api/v1/registration/communities). The guide uses direct HTTP calls only, making it easy to extend or port to other stacks.

We will reference the [claim-community-cluster-demo](https://github.com/ayv8er/claim-community-cluster-demo) repo.

***

## Prerequisites

1. Register a Cluster through our GUI at <https://clusters.xyz/register>, or programmatically through [API v1 - Registration](https://docs.clusters.xyz/getting-started/api/v1/registration).
2. Enabling the communities feature on your Clusters requires manual activation. Please contact us to have it enabled for you.
3. Get the Authentication Key of the owner (the wallet used to register the Cluster).

{% hint style="info" %}
Test Clusters can be registered on <https://testnet.clusters.xyz/register>. When testing, add the `testnet=true` query param to each endpoint.
{% endhint %}

***

## Authenticate Owner

The owner's Authentication Key is required to register a community name on behalf of a user's wallet address. You only need to generate this secret key once, and can re-use it. This token is used to register a name on behalf of a user's wallet address.

{% hint style="danger" %}
**NOTE:** The **secret** Authentication key should only be used server-side to register a name on behalf of the user. **Never expose this token client-side.**
{% endhint %}

To generate one quickly, a function called `getAuthKey` has been included in the `useAuthKey` hook in the [claim-community-cluster-demo](https://github.com/ayv8er/claim-community-cluster-demo) repo for your convenience.

The following endpoints are in [API v1 - Authentication](https://docs.clusters.xyz/getting-started/api/v1/authentication):

#### Get Signing Message

1. `GET /v1/auth/message`
2. Returns a `message` and a `signingDate`&#x20;
3. Sign the `message` with the owner's wallet

```bash
curl -X GET 'https://api.clusters.xyz/v1/auth/message'
```

Response

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

```json
{
  "message": "clusters.xyz verification\n\nBefore interacting with certain functionality, we require a wallet signature for verification.\n\n2024-05-14T19:08:27.985Z",
  "signingDate": "2024-05-14T19:08:27.985Z"
}
```

{% endtab %}
{% endtabs %}

#### **Get Authentication Key**

1. `POST /v1/auth/token`
2. &#x20;Send in body,  the `signature`, `signingDate`, `wallet` as owner's wallet, and `type` as "evm"
3. Save this token, you will need it every time to register a name on behalf of a user

```bash
curl --request POST \
  --url https://api.clusters.xyz/v1/auth/token \
  --header 'Content-Type: application/json' \
  --data '{
    "signature": "0x68b3eaa1fd6...",
    "signingDate": "2024-05-14T19:08:27.985Z",
    "type": "evm",
    "wallet": "0x0000000000000000000000000000000000000000"
  }'
```

Response

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

```json
{
  "token": "RmUyNi4yKjEqZjkyOGU3OWNjMmY5NDlkODZmM2I4...."
}
```

{% endtab %}
{% endtabs %}

***

## Endpoints Used in Registration Process

#### [Get Names by Owner](https://docs.clusters.xyz/getting-started/api/v1/address-cluster-name#get-names-by-owner)

1. `GET v1/names/owner/address/:address`
2. Check if a wallet address is part of your Clusters community
3. Returns all Clusters and community names registered to a single wallet address

```bash
curl -X GET 'https://api.clusters.xyz/v1/names/owner/address/0x5755d1dcea21caa687339c305d143e6e78f96adf
```

Response

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

```json
[
    {
        "name": "cypherpunks/satoshi",
        "owner": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
        "totalWeiAmount": "0",
        "createdAt": "2025-04-28 18:10:05.805+00",
        "updatedAt": "2025-04-28 18:10:05.805+00",
        "updatedBy": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
        "isTestnet": false,
        "clusterId": "0x...",
        "expiresAt": null
    },
    {
        "name": "mclovin/satoshi",
        "owner": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
        "totalWeiAmount": "0",
        "createdAt": "2025-04-28 18:10:05.805+00",
        "updatedAt": "2025-04-28 18:10:05.805+00",
        "updatedBy": "0x5755d1dcea21caa687339c305d143e6e78f96adf",
        "isTestnet": false,
        "clusterId": "0x...",
        "expiresAt": null
    }
]
```

{% endtab %}
{% endtabs %}

#### [Check Community Name Availability](https://docs.clusters.xyz/getting-started/api/v1/registration/communities#check-availability)

1. `GET /v1/names/community/:clusterName/check/:name`
2. Check the availability of a community name before calling the register endpoint.

```bash
curl -X GET 'https://api.clusters.xyz/v1/names/community/cypherpunks/check/desiredName'
```

Response

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

```json
{
  "name": "cypherpunks/desiredName",
  "isAvailable": true
}
```

{% endtab %}
{% endtabs %}

#### [Register a Community Name](https://docs.clusters.xyz/getting-started/api/v1/registration/communities#register-a-community-name)

1. `POST /v1/names/community/:clusterName/register`
2. This call should only be made from your server because the header includes the `AUTHKEY`, which you generated by authenticating the owner's wallet.
3. The body of the call includes the user's desired community name and their associated wallet address.

{% hint style="warning" %}
**After a user registers a community name, it cannot be removed and the name cannot be edited**. We recommend adding reCAPTCHA verification to register a name.
{% endhint %}

```bash
curl --request POST \
  --url https://api.clusters.xyz/v1/names/community/cypherpunks/register \
  --header 'Authorization: Bearer AUTHKEY'
  --header 'Content-Type: application/json' \
  --data '{ 
    "name": "desiredName",
    "walletAddress": "0x0000000000000000000000000000000000000000"
  }'
```

Response

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

```json
{
  "clusterName": "cypherpunks/desiredName",
  "owner": "0x0000000000000000000000000000000000000000"
}
```

{% endtab %}
{% endtabs %}

***

## How it Comes Together in the Demo repo

Below is a breakdown of how [claim-community-cluster-demo](https://github.com/ayv8er/claim-community-cluster-demo) app uses the Cluster API v1 to create the flow for a user to claim a community name.

#### Fetch Community Name

When user connects wallet, [wagmi `useAccount` hook](https://wagmi.sh/react/api/hooks/useAccount) defines `address` of connected wallet, triggering [tanstack `useQuery` hook](https://tanstack.com/query/latest) to call the [Get Names by Owner endpoint](#get-names-by-owner).&#x20;

Look for whether this address already has a registered to your Clusters community as the response can contain many unrelated entries (e.g. other communities or personally owned Clusters).

```typescript
// src/hooks/useCommunityNameQuery.ts
import { fetchCommunityName } from '../lib/api/clusters';
import { useQuery } from '@tanstack/react-query';
import { useAccount } from 'wagmi';
import { CommunityName } from '../types/cluster';

export const COMMUNITY_NAME_QUERY_KEY = 'communityName';

export function useCommunityNameQuery() {
  const { address } = useAccount();

  return useQuery({
    queryKey: [COMMUNITY_NAME_QUERY_KEY, address],
    queryFn: async () => {
      if (!address) return null;
      const data: CommunityName[] = await fetchCommunityName(address);
      const communityMember = data.find(member => member.name.startsWith('yourCommunityCluster/'));
      return communityMember?.name ?? null;
    },
    enabled: !!address,
  });
}
```

The root of the application reacts to the returned loading state or `clusterName` from the `useCommunityNameQuery` hook.

```typescript
// src/app/page.tsx
import { useCommunityNameQuery } from "../hooks/useCommunityNameQuery";

const { data: communityName, isLoading } = useCommunityNameQuery();

// UI displayed conditionally
```

#### Claiming Community Name

When a user types their desired community name, the `useCommunityNameAvailability` hook triggers a debounced check for its availability via call to [Check Community Name Availability endpoint](#check-community-name-availability), after the user pauses typing.

```typescript
// src/hooks/useCommunityNameAvailability
import { useState, useCallback, useEffect } from 'react';
import { checkNameAvailability } from '../lib/api/clusters';
import useDebounce from './useDebounce';

export function useCommunityNameAvailability(delay = 500) {
  const [isAvailable, setIsAvailable] = useState<boolean | null>(null);
  const [isChecking, setIsChecking] = useState(false);
  const [desiredName, setDesiredName] = useState("");
  
  const debouncedDesiredName = useDebounce(desiredName, delay);

  const checkAvailability = useCallback(async (name: string) => {
    if (!name) {
      setIsAvailable(null);
      return;
    }

    setIsChecking(true);
    
    try {
      const data = await checkNameAvailability(name);
      setIsAvailable(data.isAvailable);
    } catch (error) {
      console.error('Error checking name availability:', error);
      setIsAvailable(null);
    } finally {
      setIsChecking(false);
    }
  }, []);

  useEffect(() => {
    checkAvailability(debouncedDesiredName);
  }, [debouncedDesiredName, checkAvailability]);

  return {
    desiredName,
    setDesiredName,
    isAvailable,
    isChecking
  };
}
```

The `ClaimModal` component uses the hook's returned state to update UI.&#x20;

```typescript
// src/components/ClaimModal.tsx
const { desiredName, setDesiredName, isAvailable, isChecking } = useCommunityNameAvailability();
const { claimName, isClaiming } = useCommunityNameClaim();

const handleClaimName = useCallback(async () => {
  const success = await claimName(desiredName);
  if (success) {
    setDesiredName("");
  }
}, [desiredName, claimName, setDesiredName]);
```

When user triggers `claimName` function from `useCommunityNameClaim` hook, it communicates with the `api/cluster/register_community_name/route.ts` NextJS API route to call [Register a Community Name endpoint](#register-a-community-name).

```typescript
// src/lib/server/clusters.ts
const { name, walletAddress, communityName, apiKey, authKey } = params;

try {
  const response = await fetch(
    `https://api.clusters.xyz/v1/names/community/${communityName}/register?testnet=true`, 
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-API-Key": apiKey, // optional
        "Authorization": `Bearer ${authKey}` // Community Cluster owner's Authentication Key
      },
      body: JSON.stringify({
        name, // user's desired community name
        walletAddress // the user's wallet address to be associated to community name
      })
    }
  );
  
  const data = await response.json();
  return { ...data, success: response.ok };
```

If successful, `useCommunityNameQuery` hook will trigger a refetch and the updated community name will display on the UI. Your user is now a bona-fide community member!


