ERC-8004 Explained: Query Onchain AI Agent Identity and Reputation with a REST Call

ERC-8004 gives AI agents a portable onchain identity and reputation record. Query the live Identity and Reputation registries with evmquery — no ABI, no SDK.

evmquery team··7 min read
Share
ERC-8004 trustless agents — query the Identity and Reputation registries with evmquery

An AI agent that transacts with a stranger has no way to check who it is dealing with. There’s no LinkedIn for agents, no credit bureau, no way to ask “has this thing done real work before, or was it minted an hour ago to scam the next counterparty.” ERC-8004 is Ethereum’s answer: three lightweight onchain registries — Identity, Reputation, Validation — that any agent, human, or contract can query without an API key or a bilateral agreement.

The registries have been live on Ethereum, Base, and BNB Chain since January 29, 2026, and adoption has been fast: over 45,000 agents registered in the first month, past 200,000 within the quarter. This guide shows you how to read both registries with a single REST call, and — because we tested this live against mainnet while writing it — what the data actually looks like once you go beyond the headline registration count.

TL;DR

The Identity and Reputation registries sit at the same address on every chain they’re deployed to: 0x8004A169FB4a3325136EB29fA0ceB6D2e539a432 (Identity) and 0x8004BAa17C55a88189AE136b182e5fdA19dE9b63 (Reputation). Read an agent’s registration file and feedback score with one evmquery POST request — no ABI import, no SDK.

What ERC-8004 actually defines

ERC-8004 (“Trustless Agents”) is a Standards Track EIP, still formally in Draft status as of this writing — the contracts are live and handling real registrations, but the spec itself hasn’t been finalized, and the Validation Registry in particular is expected to change. It was proposed in August 2025 by authors from MetaMask, the Ethereum Foundation, Google, and Coinbase, which is unusual enough to be its own signal: four organizations that don’t often co-author a spec agreed this gap was worth closing.

Three registries, each deployed once per chain:

Registry Built on What it stores
Identity ERC-721 with URIStorage A token per agent (agentId), pointing to an offchain registration file with the agent’s name, capabilities, and service endpoints (A2A, MCP, web)
Reputation Custom interface Bounded feedback attestations submitted by addresses that interacted with the agent
Validation Custom interface Independent verification requests and results — still under active revision, not something to build production logic against yet

The key design choice: registration is permissionless and cheap (sub-$1 gas on L2s), and the registries store pointers and small numeric signals, not the actual agent logic or the full feedback text. Everything heavy lives offchain. That keeps onchain identity practical to adopt at scale, but it also means a registration by itself proves almost nothing — more on that below.

Reading an agent’s identity

Skip the ABI. Skip ethers.Contract. evmquery resolves the ABI for you and returns a decoded value from a single POST request.

import os
import requests
resp = requests.post(
"https://api.evmquery.com/api/v1/query",
headers={"x-api-key": os.environ["EVMQUERY_API_KEY"]},
json={
"chain": "evm_ethereum",
"schema": {
"contracts": {
"identity": {"address": "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432"}
}
},
"expression": "identity.tokenURI(solInt('3000'))",
},
timeout=10,
)
resp.raise_for_status()
print(resp.json()["result"]["value"])
# https://ag0.xyz

tokenURI is the agent’s registration file — the ERC-8004 spec calls it agentURI. It resolves to a JSON document declaring the agent’s name, its A2A or MCP service endpoints, and its supported trust models. Pull the agent’s control wallet the same way:

"expression": "identity.getAgentWallet(solInt('3000'))",
# 0xa7dcc4a4b123631a71e5b04c3b0d76941077cea2

Numeric context variables

If you parameterize agentId via schema.context instead of hardcoding it in the expression, declare it as "sol_int" and pass the runtime value as a plain number (3000), not a string ("3000"). A quoted numeric string in the context object fails validation — solInt('3000') works inline in the expression itself because solInt() explicitly parses a string, but the top-level context payload expects a native number for sol_int and list<sol_int> types.

Reading reputation feedback

The Reputation Registry aggregates feedback per agent, filtered by which client addresses you trust to count. That filtering is intentional — the spec doesn’t return a single global average, because a global average is trivial to manipulate.

resp = requests.post(
"https://api.evmquery.com/api/v1/query",
headers={"x-api-key": os.environ["EVMQUERY_API_KEY"]},
json={
"chain": "evm_ethereum",
"schema": {
"contracts": {
"reputation": {"address": "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63"}
},
"context": {"clients": "list<sol_address>"},
},
"context": {"clients": ["0x9ce7082814bda389f3ba548bdf2626006279569c"]},
"expression": "reputation.getSummary(solInt('3000'), clients, '', '')",
},
timeout=10,
)
data = resp.json()["result"]["value"]
print(data)
# {"count": "0", "summaryValue": "0", "summaryValueDecimals": "0"}

getSummary takes an agentId, a list of client addresses to include, and two optional tag filters (feedback can be tagged by category, e.g. security-audit vs. content-policy). Pass an empty tag string to match all.

clientAddresses cannot be empty

Passing clients: [] to filter by “everyone” doesn’t work — the registry contract reverts with clientAddresses required. There’s no built-in “unfiltered” mode. Resolve the client list first with getClients(agentId), which returns every address that’s ever left feedback for that agent, then pass that list into getSummary.

"expression": "reputation.getClients(solInt('3000'))",
# []

The gap between “registered” and “real”

Here’s what running these queries against a batch of real agentIds turns up: most of them come back empty. We queried tokenURI for agent IDs 1 through 5, then a spread up to 2,000 — every single one returned an empty string. Agent 3000 was the first one in our sample with an actual registration file set.

That’s not a bug in the query. It matches what the first academic study of ERC-8004 found after crawling Ethereum, BNB Smart Chain, and Base through mid-May 2026: only 3%, 4%, and 15% of registrations on those three chains, respectively, expose a valid registration file with at least one live service endpoint. The rest are placeholders — minted, never filled in, sitting there as an agentId with no agent behind it.

Registration count is not adoption

A high total-agents number is a vanity metric until you’ve filtered for agents with a real registration file and non-Sybil feedback. The same study found that after removing coordinated Sybil reviewers, 15.5% to 89.4% of “rated” agents across the three chains were left with zero valid feedback. Check tokenURI returns a non-empty value and getClients returns more than one independent address before you treat an agent’s reputation as a signal.

If you’re building agent discovery or a hiring/escrow flow on top of ERC-8004, the practical takeaway is: treat the Identity Registry as production-ready for lookups, and treat an unverified reputation score as exactly that — unverified — until you’ve checked both that a registration file exists and that the feedback behind it isn’t a handful of addresses reviewing each other.

A minimal trust check before you hire an agent

Start with the registration file and the reviewer count in one call:

resp = requests.post(
"https://api.evmquery.com/api/v1/query",
headers={"x-api-key": os.environ["EVMQUERY_API_KEY"]},
json={
"chain": "evm_ethereum",
"schema": {
"contracts": {
"identity": {"address": "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432"},
"reputation": {"address": "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63"},
},
"context": {"agentId": "sol_int"},
},
"context": {"agentId": 3000},
"expression": "reputation.getClients(agentId).size()",
},
timeout=10,
)
reviewer_count = resp.json()["result"]["value"] # 0 for agent 3000

Guard getSummary against zero reviewers

getSummary reverts with clientAddresses required if the client list you pass in is empty — including when that list came from getClients and just happens to be empty because nobody has reviewed the agent yet. Check reviewer_count > 0 before calling getSummary; don’t assume a fresh agent has a summary to fetch.

A non-empty tokenURI and reviewer_count > 1 (not just one self-interested reviewer) are the floor, not proof of trustworthiness. Fetch the JSON at the registration URI and check the declared service endpoints actually respond before you send an agent anything that costs money — the registry tells you an agent claims to exist, not that the claim holds up.

Same registries, three chains

The Identity and Reputation registries deploy at the identical address on every chain in the ERC-8004 network — a deterministic vanity deployment, hence the 0x8004... prefix on both. Swap the chain field and nothing else changes:

Chain chain value
Ethereum evm_ethereum
Base evm_base
BNB Chain evm_bnb_mainnet
for chain in ["evm_ethereum", "evm_base", "evm_bnb_mainnet"]:
resp = requests.post(
"https://api.evmquery.com/api/v1/query",
headers={"x-api-key": os.environ["EVMQUERY_API_KEY"]},
json={
"chain": chain,
"schema": {"contracts": {"identity": {"address": "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432"}}},
"expression": "identity.name()",
},
timeout=10,
)
print(chain, resp.json()["result"]["value"])
# evm_ethereum AgentIdentity
# evm_base AgentIdentity
# evm_bnb_mainnet AgentIdentity

Reputation does not travel across chains

Feedback submission requires msg.sender to be on the same chain as the registry, and the spec has no cross-chain aggregation path. An agent’s spotless reputation on Base reads as zero on Ethereum — querying the same agentId on a different chain gets you that chain’s registration, not a merged record. If you’re building cross-chain agent discovery, you need to query each chain separately and decide how to weight them yourself.

If you’re building the agent side of this rather than the query side — an agent that needs to read live onchain state as part of its own reasoning loop — the agent framework integration matrix covers wiring evmquery in via MCP or a tool call so the agent can check a counterparty’s ERC-8004 record before transacting with it. See evmquery for AI agent builders for the broader integration surface.

Next steps

Share

Query your first ERC-8004 agent in two minutes

A generous free tier, no credit card required. Grab a free API key and read a live agent identity or reputation record today.