Skip to content

Start typing to search the documentation.

Building an agent

On this page

This guide registers an AI agent with Self Agent ID and makes authenticated requests with it. By the end, your agent has an on-chain identity backed by a human’s passport scan.

1. Install the SDK

npm install @selfxyz/agent-sdk

@selfxyz/agent-sdk is the official SDK (TypeScript). From another language, register through the REST API and sign requests with the signing scheme.

2. Choose a mode

ModeBest forHuman wallet
wallet-freeQuick start, embedded agents, CLI-only setupsNo
linkedAutonomous agents owned by a human’s walletYes
privy / smartwalletHumans with a Privy or passkey smart walletYes
ed25519Agents that already sign with Ed25519 (OpenClaw, ElizaOS)No
ed25519-linkedEd25519 agents with a guardian walletYes

Details: Registration modes.

3. Register

Use Celo Sepolia (testnet) while developing, where the human scans with a mock passport. Celo Mainnet (mainnet) needs a real passport or ID card.

With the SDK

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

const session = await requestRegistration({
  mode: 'linked',
  network: 'testnet',
  humanAddress: '0xYourWallet',
  disclosures: { minimumAge: 18, ofac: true },
});
console.log(session.scanUrl); // a QR image URL: show or send it to the human

const { agentId } = await session.waitForCompletion();
const privateKey = await session.exportKey(); // within 30 minutes; store it as a secret

With the CLI

npx -p @selfxyz/agent-sdk self-agent register init --mode linked --human-address 0xYourWallet --network testnet --minimum-age 18 --ofac --out .self/session.json
npx -p @selfxyz/agent-sdk self-agent register open --session .self/session.json   # prints the QR URL
npx -p @selfxyz/agent-sdk self-agent register wait --session .self/session.json
npx -p @selfxyz/agent-sdk self-agent register export --session .self/session.json --unsafe --out-key .self/agent.key

The CLI generates the key locally and defaults to testnet. See CLI.

With plain HTTP

curl -s -X POST https://agent-api.self.xyz/api/agent/register \
  -H "Content-Type: application/json" \
  -d '{"mode":"wallet-free","network":"testnet"}'

Show the human the scanUrl, then poll GET /api/agent/register/status with Authorization: Bearer <sessionToken> until stage is completed, sending the rotated token from each response. Then call POST /api/agent/register/export with the latest token. Full walkthrough: REST API quick start.

From another agent (A2A)

Agents can register themselves through the A2A endpoint with a register intent, then poll with the status intent:

curl -s -X POST https://agent-api.self.xyz/api/a2a \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"message/send","params":{"message":{"role":"user","parts":[{"kind":"data","data":{"intent":"register","mode":"wallet-free","network":"testnet"}}]}}}'

The task’s data part carries the session token and QR links. Send { "intent": "help" } for the mode decision guide. See A2A.

4. Sign outbound requests

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

const agent = new SelfAgent({ privateKey: process.env.AGENT_PRIVATE_KEY!, network: 'testnet' });

const res = await agent.fetch('https://api.example.com/data', {
  method: 'POST',
  body: JSON.stringify({ query: 'test' }),
});

fetch adds x-self-agent-address, x-self-agent-signature and x-self-agent-timestamp (unix ms). Ed25519 agents use Ed25519Agent the same way.

5. Check status and credentials

const info = await agent.getInfo();
// { agentId, isVerified, hasHumanProof, isProofFresh, proofExpiresAt, daysUntilExpiry, isExpiringSoon, ... }

const creds = await agent.getCredentials();
// { nationality, olderThan, ofac, dateOfBirth, gender, issuingState, ... } (disclosed fields only)

6. Keep the proof fresh

Proofs expire at min(document expiry, registration + 365 days). When info.isExpiringSoon is true (under 30 days), renew in place. The agent ID and key stay the same:

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

const refresh = await requestProofRefresh({ agentId: Number(info.agentId), network: 'testnet' });
console.log(refresh.scanUrl); // the same human scans it
await refresh.waitForCompletion();

7. Publish an agent card (optional)

await agent.setAgentCard({
  name: 'My Agent',
  description: 'Analyzes market data',
  url: 'https://myagent.example.com/a2a',
  skills: [{ id: 'market-analysis', name: 'Market analysis' }],
});

setAgentCard writes the card on-chain and works when the agent key owns its NFT (wallet-free). For linked agents, the human’s wallet writes it. The API then serves the card at /api/cards/{chainId}/{agentId}. For a hosted ERC-8004 document, see Agent registration JSON.

Common pitfalls

  • Match the document to the network. Celo Sepolia accepts mock passports only and Celo Mainnet real documents only. The wrong one fails with InvalidIdentityCommitmentRoot.
  • Use the latest session token. Each status poll rotates it, and exporting with the start token returns 409.
  • Protect the exported key. It is the agent’s identity. Keep it in a secrets manager, never in source control.

Next steps

Was this page helpful?