The Agent Card Schema, Field by Field, and the Two Fields You Cannot Branch On
What the document is
A JSON document served without authentication at the address the passport token points to. A type, a version, an identifier, a resolution block, a registry block, a credential list and a timestamp. That is all of it.
The fields, and what each is safe to rely on
type and version are literals. Branch on them freely; a change to either is a breaking change
by construction.
did is nullable. A null there is not an error, it is a lifecycle state: an agent created
with a key you hold is staged until you sign, and until then it has no identifier. The schema does
not name that state, so a consumer has to know it.
erc8004 is nullable too, and independently. Minting is asynchronous, so a card can legitimately
carry an identifier and no passport for a while.
Those two nulls together encode four states and the document names none of them. If you are writing a client, decide what each combination means before you meet one.
credentials is a list of entries carrying a type, an issue time, an on-chain credential
identifier, a link to a descriptor and a status reference. Revoked entries are filtered out before
the list is built, so absence means either never issued or revoked, and the document does not
distinguish those either.
updatedAt is the time you asked, not the time anything changed.
There is no modification timestamp behind it, so it changes on
every fetch and tells you nothing about change.
The field you most want to branch on is prose
didMethod.registryStatus is declared as a free-form string.
It carries a sentence about where the identifier method stands in the registry, written for a human, with no constrained vocabulary behind it. So a client that wants to act on it must string-match against prose that can change without notice.
The cost of that has already been paid once, and it is worth keeping the receipt. For a stretch after the registry entry merged in July, the live document's value said the method was under review and not yet registered while the repository said registered. It has since been corrected. A stale sentence is invisible to a schema check because any sentence satisfies "string", so nothing flagged the drift while it lasted, and nothing flagged the correction either.
If you are consuming this, do not parse that field. Treat it as a note to a reader and check the registry yourself.
What the surface discipline gets right, published in full
The public specification is generated against an allowlist, and a test fails if any route outside it appears.
And the reason is written into the test rather than lost: a billing route had already reached the specification carrying the plan enumeration, a free-tier limit and a metering metric. Our pricing model, machine-readable, in every generated client. The allowlist exists because that happened.
That is a whitelist by construction rather than a filter, which is the only shape that stays correct as routes are added. An estate that only published defects would have skipped this, and it is one of the better things in the codebase.
And the card itself carries no claim data
No name, no jurisdiction, no assurance level, no score. The credential entries carry identifiers, a type and a status pointer, and the claims themselves live behind a selective-disclosure presentation the holder controls.
What to do if you are writing a client
- Branch on
typeandversiononly, and treat everything else as data. - Handle all four combinations of the two nullable blocks before you meet one.
- Do not parse the registry-status sentence. Check the registry.
- Do not cache on
updatedAt, which changes every time you ask. - Treat an empty credential list as uninformative, since revoked and never-issued look identical.
Keep reading
- Who Needs Agent Identity Today, and the Honest Answer About How We Would Know
- Composing With Olas, and a Claim We Found in Our Own Package That We Will Not Repeat
- Composing With OriginTrail: A Thesis With No Code Behind It, Said First Rather Than Last
- Base Sepolia and Ethereum Mainnet for ERC-8004: The Address Does Not Tell You the Network

