Skip to content

Identity (ERC-8004)

ERC-8004 is a standard for on-chain agent identity. Each agent gets minted an NFT in a public IdentityRegistry. Anyone — your dApp, a counterparty, another agent — can look up the NFT and read the agent’s verifiable profile.

0xGasless sponsors the registration gas, so identity is free for developers.

Use caseWorth registering?
Showing “verified agent” badges in your UIYes
Letting other dApps look up your agentYes
Building reputation systems on top of agentsYes (foundation for ERC-8004 reputation modules)
Pure backend automation with no public surfaceSkip — adds nothing for you

Identity is optional. Agents work fine without it.

Register the agent on the ERC-8004 IdentityRegistry on the requested chain. Idempotent per (agentId, chain).

const identity = await client.identity.link({
agentId: 'trading-bot-1', // required
chain: 'fuji', // optional — default = the agent's stored chain
});
{
"agentId": "trading-bot-1",
"address": "0xb0e6ec46cd2d97de006f9318ac5c7994b7aadd2b",
"agentTokenId": "32",
"chain": "avalanche-fuji",
"chainId": 43113,
"txHash": "0x9d42f10089cd30bd182cdc65880beabbfa19c843f3d59a32db6a6149bf854507",
"blockNumber": 56206671,
"registry": "0x...registry contract...", // returned for convenience; check explorer
"agentURI": "data:application/json;base64,eyJhZGRyZXNzIjoiMHguLi4iLCJhZ2VudElkIjoidHJhZGluZy1ib3QtMSIsImNoYWluIjoiZnVqaSIsIm5hbWUiOiJ0cmFkaW5nLWJvdC0xIn0=",
"registeredAt": "2026-06-11T21:44:03.347Z",
"alreadyRegistered": false
}
  • An ERC-721 NFT in the IdentityRegistry contract on the requested chain.
  • Token ID 32 (in the example above) — every new agent gets the next sequential ID.
  • The NFT’s tokenURI returns a data: URL containing a base64-encoded JSON object with the agent’s name, agentId, address, and chain.
  • The NFT’s owner is the 0xGasless sponsor wallet — not your agent. This is fine: the on-chain agentURI metadata is what other contracts read to verify the agent’s identity. The sponsor model lets us pay for the registration without you needing to fund anything.

Calling link() twice for the same (agentId, chain) returns the existing record with alreadyRegistered: true. No new transaction is submitted. Safe to call defensively on app startup.

StatusWhen
404Agent not found in your project.
403Agent is revoked or paused.
502ERC-8004 contract is not deployed on the requested chain (Base, for now), or the on-chain submission reverted. The error body has the underlying revert reason.
400Bad agentId shape, or chain is not a supported chain.

Read which chains support sponsored ERC-8004 registration. Useful for showing chain selectors in your UI.

const info = await client.identity.info();
{
"sponsoredRegistration": "enabled",
"chains": [
{
"chain": "avalanche",
"chainId": 43114,
"registry": "0x06d49e79da8a241dd2c412bf5d22e19c619a39d1",
"sponsor": "0x6c2d1A7932FFB8705E246222Bc5f526603a993f1",
"explorer": "https://snowtrace.io"
},
{
"chain": "avalanche-fuji",
"chainId": 43113,
"registry": "0x372d406040064a9794d14f3f8fec0f2e13e5b99f",
"sponsor": "0x6c2d1A7932FFB8705E246222Bc5f526603a993f1",
"explorer": "https://testnet.snowtrace.io"
}
]
}

If a chain isn’t in the list, identity isn’t yet supported there. You can still use the agent for x402 payments on that chain — only the identity feature is gated.

Looking up an agent’s identity from another contract

Section titled “Looking up an agent’s identity from another contract”

Once minted, you can read the agent’s identity from any contract on the same chain:

interface IIdentityRegistry {
function tokenURI(uint256 tokenId) external view returns (string memory);
function ownerOf(uint256 tokenId) external view returns (address);
}
contract MyContract {
IIdentityRegistry constant registry = IIdentityRegistry(0x372d406040064a9794d14f3f8fec0f2e13e5b99f); // Fuji
function checkAgent(uint256 tokenId) external view returns (string memory) {
return registry.tokenURI(tokenId);
// Returns: "data:application/json;base64,eyJhZGRyZXNzIjoiMHguLi4iLCJhZ2VudElkIjoidHJhZGluZy1ib3QtMSIsLi4ufQ=="
// Decode the base64 to get { name, agentId, address, chain }.
}
}
async function ensureIdentity(agentId: string, chain: string) {
const result = await client.identity.link({ agentId, chain });
if (result.alreadyRegistered) {
console.log(`Agent already registered as #${result.agentTokenId}`);
} else {
console.log(`Minted new identity #${result.agentTokenId}, tx ${result.txHash}`);
}
return result;
}
const agent = await client.agents.get('trading-bot-1');
if (agent.identity) {
console.log(`Identity #${agent.identity.agentTokenId} on ${agent.identity.chain}`);
console.log(`View on explorer: https://testnet.snowtrace.io/tx/${agent.identity.txHash}`);
} else {
console.log('No identity registered yet.');
}

Register the same agent on multiple chains

Section titled “Register the same agent on multiple chains”

Each (agentId, chain) is a separate registration. Useful for agents that operate on multiple chains.

await client.identity.link({ agentId: 'multi-bot', chain: 'avalanche' });
await client.identity.link({ agentId: 'multi-bot', chain: 'fuji' });

You get two distinct NFTs — one on each chain — both pointing at the same agentURI.