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.
When to use it
Section titled “When to use it”| Use case | Worth registering? |
|---|---|
| Showing “verified agent” badges in your UI | Yes |
| Letting other dApps look up your agent | Yes |
| Building reputation systems on top of agents | Yes (foundation for ERC-8004 reputation modules) |
| Pure backend automation with no public surface | Skip — adds nothing for you |
Identity is optional. Agents work fine without it.
identity.link(input)
Section titled “identity.link(input)”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});Returns
Section titled “Returns”{ "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}What just got minted
Section titled “What just got minted”- An ERC-721 NFT in the
IdentityRegistrycontract on the requested chain. - Token ID 32 (in the example above) — every new agent gets the next sequential ID.
- The NFT’s
tokenURIreturns adata:URL containing a base64-encoded JSON object with the agent’sname,agentId,address, andchain. - The NFT’s owner is the 0xGasless sponsor wallet — not your agent. This is fine: the on-chain
agentURImetadata 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.
alreadyRegistered: true
Section titled “alreadyRegistered: true”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.
Errors
Section titled “Errors”| Status | When |
|---|---|
404 | Agent not found in your project. |
403 | Agent is revoked or paused. |
502 | ERC-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. |
400 | Bad agentId shape, or chain is not a supported chain. |
identity.info()
Section titled “identity.info()”Read which chains support sponsored ERC-8004 registration. Useful for showing chain selectors in your UI.
const info = await client.identity.info();Returns
Section titled “Returns”{ "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 }. }}Patterns
Section titled “Patterns”Defensive registration on startup
Section titled “Defensive registration on startup”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;}Show identity status in your UI
Section titled “Show identity status in your UI”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.
See also
Section titled “See also”- Agents — agents must be
activeto be registered. - Errors — including
502errors from the on-chain submission. - x402 Protocol → Supported Chains — which chains support identity vs only payments.