AgentDoc Specification

Version 0.2.0-draft · Working Draft · 2026-08-29

An agent already describes itself in half a dozen places. Its card says what it does. Its registration says what it is. Its standing terms say what it charges and what it promises about your data. A directory listing says what somebody checked. A reputation report says how it has gone. Which interfaces it implements is a seventh thing, usually recorded nowhere at all.

Every one of those is reachable. None of them is reachable together, and nobody deciding whether to engage an agent wants six fetches and a mental merge. AgentDoc is one document that assembles them, served from a known location, in a form a program can act on and a person can read.

It is the same trick JavaDoc and Swagger played, and it is worth being precise about which part of that trick matters. Neither invented facts. Both took what was already true of a program and put it in one place, in two renderings: one for a machine, one for a reader. AgentDoc does that for an agent, with one addition neither of them needed. An API description has no interested party. An agent description is written by somebody selling something, so every part of an AgentDoc says where it came from.

This specification is part of the family that includes Agent SoW, Agent RFP, Agent Mandate, Agent Reputations, and Agent Roles. Those standardize documents between parties and recurring jobs. This one standardizes the view over all of them. It is transport-neutral; nothing here requires a particular network or vendor.

1. Motivation

Ask a practical question about an agent you are considering hiring. Can it do the work? Will it tell you it is busy? What does it charge? Where does it process the data? Has anybody checked any of that, or is it all the operator's own copy?

Today those answers live in different documents, published by different parties, in different formats, at different addresses, and several of them are optional. The reader who does the work of gathering them ends up holding a merged picture that exists only in their head, cannot be passed to anybody else, and is stale the moment they look away. That is a document-shaped problem, and it has a document-shaped answer.

1.1 The bet: assembled, never authored

The tempting version of this idea is a file an operator writes about their agent. That version fails, and we can say exactly how, because the industry already ran the experiment. Hand-written API descriptions drift from the API they describe, and then they lie, and then people stop trusting the format rather than the file.

So an AgentDoc is generated, and this specification makes that structural rather than advisory: an AgentDoc MUST NOT contain a fact that is not present in a source it lists (§4.2). There is nowhere to type a claim. An operator who wants their AgentDoc to say something changes the signed document it would be assembled from.

1.2 What it is not

It is not a new source of truth, and it is not a registry. The card, the terms, the listing, and the reputation report remain what they always were. An AgentDoc is a view, and a view that outlives its sources is a lie with a timestamp, which is why §4.4 makes staleness visible rather than fatal.

It also does not compete with the A2A Agent Card. Where the two overlap, an AgentDoc restates the card and cites it (§9). Forking a format that already has adoption and a known address would be a way of helping nobody.

2. Conformance language and terminology

The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted as described in RFC 2119.

  • AgentDoc: one assembled document describing one agent, in the shape §5 defines.
  • Subject: the agent an AgentDoc describes.
  • Assembler: whoever produced this AgentDoc by gathering the sources. It MAY be the agent's own operator, a directory, or an unrelated third party.
  • Source: a document or observation an AgentDoc was assembled from, listed in the AgentDoc itself with enough detail to be fetched again.
  • Section: one part of an AgentDoc's description of the subject, each naming the sources it came from.
  • Not-stated: a member the subject has published nothing about. Named explicitly (§6), never omitted and never defaulted.
  • Owner, Agent, Engagement: as defined in Agent SoW §2. Role, Interface: as defined in Agent Roles §3 and §11.

3. Where an AgentDoc lives

An agent reachable at a domain SHOULD serve its AgentDoc at:

https://<domain>/.well-known/agentdoc.json

beside the A2A Agent Card, which is already at /.well-known/agent-card.json. A reader who has one address has both.

Two things follow, and both are deliberate.

An AgentDoc MAY also be served by somebody who is not the subject. A directory that assembles AgentDocs for every agent it lists is a conforming assembler, and its documents are as valid as the subject's own, because §4 makes the sources checkable rather than asking anybody to be trusted. The location above is where the subject's copy goes, not a claim of exclusivity.

An agent that is not reachable at a domain, which is most agents on a mesh, has no well-known location to use. Its AgentDoc is served wherever its operator or its directory serves it, and the document is unchanged. The format does not depend on the address.

Every AgentDoc SHOULD also be available as a human page at the same path without the .json extension, rendered from the same document (§8).

4. The rules that make it worth reading

4.1 Every section names its sources

Each section of an AgentDoc carries from, listing the source identifiers it was assembled from. A section with no source does not conform. This is what lets a reader take any single line and go and check it.

4.2 An assembler adds no facts

An AgentDoc MUST NOT state anything that is not in a listed source. Reformatting, unit conversion, and restating a signed value in a section are all fine. Inference is not: an assembler that concludes an agent "probably handles European data" from a European address has manufactured a claim the agent never made, and the agent will be held to it.

Where an assembler wants to publish its own opinion about a subject, that is a recommendation and Agent Roles §9 already defines the role that produces one.

4.3 There is no summary grade

An AgentDoc MUST NOT carry a single overall score, badge, tier, or trust label for its subject.

This is the one rule most likely to be argued with, so here is the reasoning. The family already learned it once, in the Agent Listing Model: a listing's evidence tier is about who is speaking, and it is deliberately one value for the whole entry, because the day a per-field verified mark exists is the day a verified badge starts implying verified content. A publisher whose identity was checked is still the only source for what their agent can do.

An assembled document does not need a badge, because it has something better. It says where each part came from, and the reader can see that the price is from a signed standing proposal and the capability blurb is from a text field the operator typed. Provenance is the evidence. A badge is a summary of provenance that discards the part that mattered.

4.4 It says when it was assembled, and it goes stale

Every AgentDoc carries assembled_at, and every source carries when it was fetched. A reader MUST be able to tell how old the view is.

An AgentDoc MAY carry stale_after. Past that moment a reader SHOULD treat it as an old view rather than a false one: the sources it cites are still fetchable, and re-assembling is the remedy. An expired AgentDoc is not invalid, it is out of date, and those are different enough to be worth separate words.

4.5 What the assembler signs, and what it does not

An assembler MAY sign an AgentDoc with agentdoc-assembly-v1.

The signature means exactly one thing: this assembler gathered these sources at this time and reported them faithfully. It is not a claim that the subject's statements are true, and it MUST NOT be presented as one. An assembler signs its own work, never the subject's.

Where a source was itself signed, the AgentDoc records that separately, per source (§5.2). Those two signatures answer different questions and must never be shown as one.

5. The document

5.1 Top level

{
  "agentdoc": "v1",
  "subject": { "id": "…", "handle": "…", "name": "…" },
  "assembled_at": "2026-08-29T18:04:00Z",
  "assembled_by": "…",
  "stale_after": "2026-09-05T18:04:00Z",
  "sources": [ … ],
  "sections": { … },
  "not_stated": [ … ],
  "signatures": [ ]
}

subject.id is the agent's stable identifier, normally its public key. handle and name are conveniences and MAY be absent; id MUST NOT be.

assembled_by identifies the assembler. Where the assembler is the subject's own operator, it says so, because a self-assembled AgentDoc and a third-party one are different things to a reader and the difference should not require inference.

5.2 Sources

Each entry describes one thing the AgentDoc was built from:

{
  "id": "card",
  "kind": "a2a-card",
  "url": "https://example.com/.well-known/agent-card.json",
  "fetched_at": "2026-08-29T18:03:58Z",
  "digest": "sha256:…",
  "signature": "verified"
}

id is referenced by sections. kind names the sort of document, so a reader knows what rules govern it: a2a-card, agent-manifest, sow-standing-proposal, listing, reputation-report, roles-declaration, interface-definition, and observation are defined here; others MAY be used and SHOULD be namespaced.

An interface-definition source is the published interface itself, fetched from the registry that publishes it. It describes the exchanges rather than the subject, and it is what §5.3.1 expands. Listing it is what keeps an expanded interface within §4.2: the exchanges are quoted from a document, not supplied by the assembler.

signature is one of verified, unsigned, failed, or not_checked. unsigned and not_checked are different facts and MUST NOT be collapsed: one says the document carried no signature, the other says this assembler did not look.

An observation source is something the assembler did rather than fetched: asking an agent whether it was free, and recording the answer and the moment. It carries at and what in place of url, and it is the only source kind that can go out of date within a minute, which is why §5.3's asked section carries its own timestamps.

5.3 Sections

Every section is optional. An absent section means the subject published nothing to build it from, and §6 requires that absence be named.

SectionWhat it holds
identityWho it is. Name, operator, address, and what signs for it.
shapeWhat it is. Whether it is a service or a person's own agent, what it runs on, whether it is one process or a fleet behind one identity.
askedWhat it can be asked. The largest section, and the one a caller acts on. Defined in full in §5.3.1.
roleThe standard role it holds, if any.
sellsIts offerings, and the terms they are sold on, cited to the standing proposals that carry them.
refusesWhat it will not do. Drawn from its declared scope, its role's refusals, and its terms. Usually the most useful section on the page, and usually the emptiest, which is itself worth seeing.
handlingWhere it processes data and how long it keeps it, cited to the terms that promise it.
standingWhat third parties have published about it: reputation reports, attestations, directory checks. Carried, never audited, and attributed.

5.3.1 The asked section, in full

This is the section a caller actually acts on, and it is the reason an AgentDoc is worth fetching rather than reading a description. Everything else tells a reader what an agent is. This tells them what they can send it and what will come back.

An interface entry names the interface, the version, where its definition lives, and every exchange that definition publishes, each with its own state. It MUST NOT be reduced to a name and a version: a reader who has to go and fetch the interface definition to find out what they may send has been handed a footnote instead of a description.

"asked": {
  "from": ["card", "iface-worker", "probe"],
  "interfaces": [
    {
      "name": "interface-worker",
      "version": "v1",
      "definition": "https://agentroles.ai/worker.html",
      "declared_in": "card",
      "exchanges": [
        {
          "id": "free",
          "asks": "are you free?",
          "answer_carries": ["free", "reason when not", "job when not"],
          "state": "confirmed",
          "confirmed_at": "2026-08-29T18:04:00Z",
          "confirmed_by": "probe"
        },
        {
          "id": "estimate",
          "asks": "what would this take?",
          "answer_carries": ["can (CAN|CANNOT|UNSURE)", "commitment", "note"],
          "state": "declared"
        }
      ]
    }
  ]
}

Three rules govern it.

The exchange list comes from the interface definition, and the definition is a source. An assembler does not invent the exchanges; it resolves the published interface and lists what that publication says, citing it like any other source (kind: "interface-definition"). This keeps §4.2 intact: the exchanges are facts from a document, not the assembler's summary of one.

State is per exchange, never per interface. An assembler that asked one exchange and got a conforming answer has confirmed one exchange. Marking the whole interface confirmed on that basis is exactly the overclaiming §4.2 forbids, in the place a reader is most likely to rely on it. The states are declared (the subject says it implements this and nobody checked), confirmed (this assembler asked and got a conforming answer, with the timestamp and the observation that did it), and failed (this assembler asked and the answer did not conform, which is a fact worth carrying and MUST NOT be silently downgraded to declared).

A declared interface with no exchanges listed does not conform. If the assembler could not resolve the definition, it says so with the interface present and its exchanges absent, and names the interface in not_stated. Silently emitting a bare name reads as a complete answer.

5.4 What is not stated

not_stated is an array of section or member paths the subject has published nothing about, for example ["role", "handling.retention_max_days"].

This exists because silence is the most common answer in this whole family and the easiest one to lose. A missing field renders as nothing at all, and nothing at all looks like a document that simply did not mention an unimportant detail. Naming it makes the reader see the agent declined, or never got round, to say where it processes your data.

6. Silence never resolves to the flattering value

An assembler MUST NOT substitute a default for a member the subject has not stated, and MUST NOT choose the reading that favours the subject.

An agent that has said nothing about retention has not promised to delete anything. An agent with no published price is not free. An agent that has not declared an interface does not implement it, whatever its prose says. An unreadable or malformed member is not-stated, not the safe-sounding value: a typo must never manufacture a promise.

Where a reader needs a default in order to act, the default belongs to the reader's own policy, applied by the reader, visibly. It does not belong in a document that describes somebody else.

7. The subject's say, and its limits

A subject MUST be able to correct an AgentDoc about itself, and the mechanism is already the whole design: change the signed source, and the next assembly changes with it. An assembler that continues to publish a section citing a source that has since changed, without re-fetching, is not conforming.

A subject MUST NOT be able to compel an assembler to omit a source it lawfully holds, or to remove not_stated entries. An AgentDoc that a subject can edit directly is the hand-written file §1.1 exists to prevent.

8. The human rendering

An AgentDoc SHOULD be renderable as a page, from the same document, with no additional input.

The rendering MUST show, for every section, which sources it came from, and MUST show not_stated as visibly as it shows what was stated. A renderer that hides absence produces exactly the flattering picture §6 forbids, by omission rather than by assertion.

The rendering MUST NOT introduce a badge, score, or trust mark that the document does not contain (§4.3).

9. Relationship to the A2A Agent Card

The card is a source, and normally the first one. An AgentDoc restates card members in its sections and cites the card; it never contradicts it. Where the card carries family extensions, the terms extension in particular, the AgentDoc follows them to the documents they commit to and lists those as further sources.

An AgentDoc is therefore a superset of a card's content and a subset of a card's reach: the card is fetched by tools that have never heard of this specification, and it MUST remain valid and useful on its own. Nothing here asks an operator to stop publishing a card, or changes what a card means.

10. Conformance testing

Mechanical, and unusually easy for a document of this kind, because an AgentDoc makes claims about its own construction.

Given an AgentDoc and the ability to fetch its sources, a harness checks: that every section's from names sources that exist in the document; that every listed source is fetchable and, where a digest is recorded, matches it; that every fact in every section appears in one of that section's cited sources; that no member of the document is a summary grade; that assembled_at and per source fetched_at are present; that a member absent from every source appears in not_stated rather than being silently missing; and that no not-stated member has been given a default.

The one thing a harness cannot check is whether the subject's statements are true, and no version of this specification will claim otherwise.

11. Out of scope

  • Whether the subject is any good. AgentDoc reports what was said and by whom. Judgment is a matchmaker's job and quality is the reputation bureau's.
  • The formats of the sources. AgentDoc cites documents defined elsewhere and does not redefine any of them.
  • Discovery. Finding agents is a directory's job. This describes one agent to somebody who already has its address.
  • Access control. An AgentDoc is a description, not a credential, and grants nothing.
  • Deciding to engage. That is an act, taken with a reader's own key, on terms the sources define.

12. Signature tags

  • agentdoc-assembly-v1: an assembler's statement that it gathered the listed sources at the stated time and reported them faithfully (§4.5). Signed with the family convention: the ASCII tag, one newline, then the JCS (RFC 8785) canonical JSON with signatures removed.

The subject signs nothing here. Its statements are signed in their own documents, which is where a reader should go to check them, and saying so is the whole point of the format.

v0.1.0-draft (2026-08-29): first draft. Section 4 is load-bearing; argue with 4.2 and 4.3 first. The first decides whether this stays a view or becomes another place to type a claim, and the second decides whether it stays useful or becomes a badge people optimise for.

v0.2.0-draft (2026-08-29, later): the asked section becomes the centre of the document rather than one row in a table (§5.3.1). In the first draft it was a name and a version per interface, which is a footnote: a caller still had to go and fetch the interface definition to learn what they were allowed to send. It now carries every exchange the definition publishes, with what each answer must carry, and the interface definition is itself a listed source (interface-definition) so the expansion stays inside §4.2 rather than becoming the assembler's summary. State moved from per interface to per exchange, because an assembler that asked one exchange has confirmed one exchange and marking the whole interface confirmed is overclaiming in the one place a reader is most likely to act on it. A third state, failed, is carried rather than quietly downgraded to declared.