Skip to content
Skip the search — install the Self skill Let your AI agent integrate Self for you.

Start typing to search the documentation.

Register an Agent with the SDK

Register an agent end to end from your own app: render the passport-scan QR with the SDK, the Self mobile app submits the proof on-chain, and the registry mints the agent. Signing and verification are on-chain too.

This page shows the full loop with the SDK: register, sign, verify. It needs no hosted service at all. If you prefer, you can also register through the CLI or the REST/A2A API at https://agent-api.self.xyz, which return the QR for you to display.

What you need

  • npm install @selfxyz/agent-sdk @selfxyz/qrcode ethers
  • The Self mobile app on a phone. On testnet you can generate mock documents in the app, so no real passport is needed.
  • A Celo RPC. Public defaults: https://forno.celo.org (mainnet), https://forno.celo-sepolia.celo-testnet.org (testnet).
NetworkChain IDRegistry
Celo Mainnet422200xaC3DF9ABf80d0F5c020C06B04Cced27763355944
Celo Sepolia (testnet)111422200x043DaCac8b0771DD5b444bCC88f2f8BBDBEdd379

1. Register an agent

Render the QR in your own frontend. The proof goes from the Self app to the on-chain Hub, which mints the agent NFT into the registry. No backend is involved.

import { useEffect, useState } from 'react';
import { SelfAppBuilder, SelfQRcodeWrapper } from '@selfxyz/qrcode';
import { signRegistrationChallenge, buildAdvancedRegisterUserDataAscii } from '@selfxyz/agent-sdk';
import { Wallet } from 'ethers';

const REGISTRY = '0xaC3DF9ABf80d0F5c020C06B04Cced27763355944'; // Celo mainnet

export function RegisterAgent({ humanAddress }: { humanAddress: string }) {
  const [selfApp, setSelfApp] = useState<any>(null);
  const [agentKey] = useState(() => Wallet.createRandom());

  useEffect(() => {
    (async () => {
      // The agent key signs a challenge proving it controls the key
      const sig = await signRegistrationChallenge(agentKey.privateKey, {
        humanIdentifier: humanAddress,
        chainId: 42220,
        registryAddress: REGISTRY,
        nonce: 0,
      });

      // Encode the registration payload (config index is derived from disclosures)
      const disclosures = { minimumAge: 18, ofac: true };
      const userDefinedData = buildAdvancedRegisterUserDataAscii({
        agentAddress: agentKey.address,
        signature: sig,
        disclosures,
      });

      // Build the Self verification request; the registry is the on-chain endpoint
      setSelfApp(
        new SelfAppBuilder({
          version: 2,
          appName: 'My Agent',
          scope: 'self-agent-id',
          endpoint: REGISTRY,
          endpointType: 'celo', // 'staging_celo' for testnet
          userId: humanAddress,
          userIdType: 'hex',
          userDefinedData,
          disclosures,
        }).build(),
      );
    })();
  }, [humanAddress]);

  return selfApp ? (
    <SelfQRcodeWrapper
      selfApp={selfApp}
      onSuccess={() => {
        // Minted. Save agentKey.privateKey securely — it is the agent's key.
      }}
      onError={() => console.error('verification failed')}
    />
  ) : (
    <p>Loading…</p>
  );
}

The user scans the QR with the Self app. On onSuccess the agent NFT is minted. Save the agent private key. This example uses linked mode; for other modes (wallet-free, Ed25519, etc.) see Registration Modes.

Agents minted this way start with a blank agentURI. Publish a JSON document so services and indexers can discover the agent, then point the agent at it.

import { generateRegistrationJSON } from '@selfxyz/agent-sdk';

const doc = generateRegistrationJSON({
  name: 'My Agent',
  description: 'A human-backed AI agent',
  image: 'https://my-agent.example.com/avatar.png',
  services: [{ name: 'A2A', endpoint: 'https://my-agent.example.com/a2a', version: '1.0' }],
});
// Host doc over HTTPS, then call registry.setAgentURI(agentId, url)

3. Sign requests as the agent

import { SelfAgent } from '@selfxyz/agent-sdk';

const agent = new SelfAgent({
  privateKey: process.env.AGENT_PRIVATE_KEY!,
  registryAddress: '0xaC3DF9ABf80d0F5c020C06B04Cced27763355944',
  rpcUrl: 'https://forno.celo.org',
});

const res = await agent.fetch('https://some-service.example.com/api/protected', {
  method: 'POST',
  body: JSON.stringify({ hello: 'world' }),
});

4. Verify the agent in a service

The verifier recovers the signer and checks the registry on-chain. No API, no shared secret.

import { SelfAgentVerifier } from '@selfxyz/agent-sdk';

const verifier = new SelfAgentVerifier({
  registryAddress: '0xaC3DF9ABf80d0F5c020C06B04Cced27763355944',
  rpcUrl: 'https://forno.celo.org',
});

app.use('/api', verifier.auth()); // rejects requests from unverified agents

That is the whole product: register on-chain, sign, verify on-chain. Nothing here depends on a hosted Self website.

Next steps

Was this page helpful?