Quick start · Docs · EIP · Postman · API spec · npm
A human scans their passport once with the Self app. Their AI agent gets a soulbound ERC-8004 NFT on Celo and signs every request with its own key. Services check the signature plus the on-chain proof. No API keys.
Needs Node 18+ and the Self app with a passport or ID card.
1. Register (~3 min, mostly the scan):
import { requestRegistration } from "@selfxyz/agent-sdk";
const session = await requestRegistration({
mode: "wallet-free", // or "linked" with humanAddress: "0xYourWallet"
network: "testnet",
disclosures: { minimumAge: 18, ofac: true },
});
console.log(session.deepLink); // open on your phone
const { agentId } = await session.waitForCompletion();
const privateKey = await session.exportKey(); // within 30 min of completion2. Sign (agent side):
import { SelfAgent } from "@selfxyz/agent-sdk";
const agent = new SelfAgent({ privateKey, network: "testnet" });
await agent.fetch("https://api.example.com/data", {
method: "POST",
body: JSON.stringify({ q: "hi" }),
});3. Verify (service side, Express):
import { SelfAgentVerifier } from "@selfxyz/agent-sdk";
const verifier = SelfAgentVerifier.create()
.network("testnet")
.requireAge(18)
.sybilLimit(0)
.build();
// The signature covers the exact body bytes: keep them as req.rawBody.
app.use(
express.json({
verify: (req, _res, buf) => ((req as { rawBody?: Buffer }).rawBody = buf),
}),
);
app.use("/api", verifier.auth()); // sets req.agentDone when your service answers 200 with req.agent.agentId set.
No code?
- CLI:
npx -p @selfxyz/agent-sdk self-agent register init --mode wallet-free --minimum-age 18 --out s.json, thenregister open,wait,export --unsafe --print-private-key(CLI docs) - Postman: fork the Self Agent ID workspace; defaults hit verified agent
5on Celo Sepolia
| Error | Fix |
|---|---|
Human has N agents (max 1) |
Verifier default is 1 agent per human: .sybilLimit(0) = no limit |
Agent not verified on-chain |
Wrong network, or body changed after signing: verify req.rawBody |
does not meet minimum age (got: 0) |
Agent has no age disclosure (e.g. demo agent 5): register with minimumAge: 18, or drop .requireAge |
429 + Retry-After |
Per-IP limit, shared by everyone on the same Wi-Fi: wait retryAfterMs |
More: troubleshooting.
| Network | Chain ID | Registry | RPC |
|---|---|---|---|
| Celo Mainnet | 42220 |
0xaC3DF9ABf80d0F5c020C06B04Cced27763355944 |
https://forno.celo.org |
| Celo Sepolia | 11142220 |
0x043DaCac8b0771DD5b444bCC88f2f8BBDBEdd379 |
https://forno.celo-sepolia.celo-testnet.org |
Other addresses: contracts/README.md. On-chain cap: 10 agents per human on mainnet, unlimited on Sepolia.
Gate a contract:
bytes32 key = bytes32(uint256(uint160(msg.sender)));
require(registry.isVerifiedAgent(key) && registry.isProofFresh(registry.getAgentId(key)), "Not human-verified");Add to .mcp.json (Claude Code, Cursor, Windsurf, Codex):
{
"mcpServers": {
"self-agent-id": { "url": "https://agent-api.self.xyz/api/mcp" }
}
}To sign as your own agent, run @selfxyz/mcp-server locally.
| Path | What |
|---|---|
app/ |
API service: REST, A2A, MCP, discovery |
typescript-sdk/ |
@selfxyz/agent-sdk + self-agent CLI |
mcp/ |
@selfxyz/mcp-server, local MCP server with your agent key |
contracts/ |
Solidity registry (Foundry), deployed addresses |
plugin/ |
Claude Code plugin: 6 skills + hosted MCP |
docs/ |
EIP draft for the proof-of-human extension |
git submodule update --init --recursive(cd contracts && forge build && ./scripts/test.sh)(cd typescript-sdk && npm install && npm test)(cd app && cp .env.example .env.local && npm install && npm run dev)(setSESSION_SECRET)npm install && npm run format:check && npm run lint
BUSL-1.1: non-commercial use allowed; commercial use needs a license from Social Connect Labs, Inc. Converts to Apache-2.0 on 2029-06-11. contracts/ and mcp/ are MIT.