Clusters
  • Introduction
    • Overview
    • Concepts
      • Cluster Name
      • Wallet Name
    • Features
      • Communities
      • Multichain
      • Wallet Bundles
      • Antisquatting
      • Wallet Generation
      • Selective Wallet Sharing
  • Getting started
    • Javascript
      • Authentication
      • Clusters
      • Address → Cluster Name
      • Cluster Name → Address
      • Registration
        • Communities
      • Event Indexing
    • API
      • v1
        • Authentication
        • Clusters
        • Address → Cluster Name
        • Cluster Name → Address
        • Registration
          • Communities
        • Event Indexing
      • v0.1 (Deprecated)
        • Address → Cluster
        • Cluster → Address
        • Cluster → Metadata
        • Registration
        • Managing Wallets
  • Resources
    • Smart Contracts
    • Address Types
    • Using Clusters for ETH->SOL Airdrops
  • Integration Guides
    • Convert hex address to clusters name
    • Registering a name
      • Ethereum Networks
      • Solana
    • Whitelabel Communities Registration Flow
Powered by GitBook
On this page
  • What's the Difference?
  • Prerequisites
  • Authenticate Owner
  • Endpoints Used in Registration Process
  • How it Comes Together in the Demo repo
  1. Integration Guides

Whitelabel Communities Registration Flow

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

PreviousSolana

Last updated 6 days ago

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.

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 . The guide uses direct HTTP calls only, making it easy to extend or port to other stacks.

We will reference the repo.


Prerequisites

  1. Register a Cluster through our GUI at , or programmatically through .

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

Test Clusters can be registered on . When testing, add the testnet=true query param to each endpoint.


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.

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.

Get Signing Message

  1. GET /v1/auth/message

  2. Returns a message and a signingDate

  3. Sign the message with the owner's wallet

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

Response

{
  "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"
}

Get Authentication Key

  1. POST /v1/auth/token

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

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

{
  "token": "RmUyNi4yKjEqZjkyOGU3OWNjMmY5NDlkODZmM2I4...."
}

Endpoints Used in Registration Process

  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

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

Response

[
    {
        "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
    }
]
  1. GET /v1/names/community/:clusterName/check/:name

  2. Check the availability of a community name before calling the register endpoint.

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

Response

{
  "name": "cypherpunks/desiredName",
  "isAvailable": true
}
  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.

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.

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

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

How it Comes Together in the Demo repo

Fetch Community Name

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

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

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

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

// UI displayed conditionally

Claiming Community Name

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

// 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]);
// 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!

To generate one quickly, a function called getAuthKey has been included in the useAuthKey hook in the repo for your convenience.

The following endpoints are in :

Below is a breakdown of how app uses the Cluster API v1 to create the flow for a user to claim a community name.

When user connects wallet, defines address of connected wallet, triggering to call the .

When a user types their desired community name, the useCommunityNameAvailability hook triggers a debounced check for its availability via call to , after the user pauses typing.

When user triggers claimName function from useCommunityNameClaim hook, it communicates with the api/cluster/register_community_name/route.ts NextJS API route to call .

More information on that here.
Clusters API v1 - Communities
claim-community-cluster-demo
https://clusters.xyz/register
API v1 - Registration
https://testnet.clusters.xyz/register
claim-community-cluster-demo
API v1 - Authentication
Get Names by Owner
Check Community Name Availability
Register a Community Name
claim-community-cluster-demo
wagmi useAccount hook
tanstack useQuery hook
Get Names by Owner endpoint
Check Community Name Availability endpoint
Register a Community Name endpoint