Agent Card Anatomy

A field-by-field walkthrough of the v1.0 schema with a complete worked example. The companion to the main field guide; consult the A2A v1.0 specification and the a2aproject/A2A reference repository for normative detail.

A complete worked example

Below is a fully populated v1.0 Agent Card for a fictional KYC-review agent at https://kyc.example.com. The card declares two skills, two security schemes, and one JWS signature. It is what a calling agent would receive from https://kyc.example.com/.well-known/agent-card.json.

{
  "name": "AcmeCorp KYC Review Agent",
  "description": "Reviews KYC document packages and returns a risk score with explanations.",
  "url": "https://kyc.example.com/a2a",
  "protocolVersion": "1.0",
  "provider": {
    "name": "AcmeCorp",
    "url": "https://acmecorp.example/"
  },
  "iconUrl": "https://kyc.example.com/icon.svg",
  "documentationUrl": "https://kyc.example.com/docs",

  "capabilities": {
    "streaming": true,
    "pushNotifications": true,
    "stateTransitionHistory": true
  },

  "defaultInputModes": ["text", "file", "structured"],
  "defaultOutputModes": ["text", "structured"],

  "skills": [
    {
      "id": "kyc.review.individual",
      "name": "Individual KYC review",
      "description": "Review a document package for an individual customer (passport, proof-of-address, selfie) and return a risk score.",
      "tags": ["kyc", "individual", "risk-scoring"],
      "examples": ["Review the attached passport + utility bill for John Doe."],
      "inputModes": ["file", "structured"],
      "outputModes": ["structured"]
    },
    {
      "id": "kyc.review.entity",
      "name": "Entity KYC review",
      "description": "Review a corporate KYC package (incorporation docs, UBO disclosures, sanctions screening) and return a risk score.",
      "tags": ["kyc", "entity", "ubo", "sanctions"],
      "examples": ["Review the corporate package for AcmeCo Ltd."],
      "inputModes": ["file", "structured"],
      "outputModes": ["structured"]
    }
  ],

  "securitySchemes": {
    "bearerAuth": {
      "type": "http",
      "scheme": "bearer",
      "bearerFormat": "JWT"
    },
    "oauth2": {
      "type": "oauth2",
      "flows": {
        "clientCredentials": {
          "tokenUrl": "https://auth.acmecorp.example/oauth2/token",
          "scopes": {
            "kyc:read": "Read KYC review results",
            "kyc:write": "Submit KYC review tasks"
          }
        }
      }
    }
  },
  "security": [
    { "bearerAuth": [] },
    { "oauth2": ["kyc:read", "kyc:write"] }
  ],

  "signatures": [
    {
      "protected": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjIwMjYtMDUta2V5LTAxIn0",
      "signature": "<base64url-encoded JWS signature over the canonicalised body>"
    }
  ]
}

Field-by-field

Identity

FieldRequiredDescription
nameYesShort human-readable name. Used in marketplaces and agent UIs.
descriptionYes1–2 sentence summary of what the agent does.
urlYesThe JSON-RPC endpoint. Must be HTTPS in production.
protocolVersionYesA2A version supported. "1.0" as of 2026.
providerRecommendedIssuing organisation: name + url. Used for trust attribution.
iconUrlOptionalSquare SVG/PNG icon for marketplace listings.
documentationUrlOptionalHuman-facing docs for the agent's capabilities.

Capabilities flags

The capabilities object declares protocol-level behaviours the agent supports. Calling agents check these to decide whether to use streaming SSE, register a webhook for push notifications, or fetch state-transition history for audit. Each flag is a boolean.

Modalities

defaultInputModes and defaultOutputModes declare what content types the agent accepts and produces by default. Each skill can override at the per-skill level. The three canonical values are text (free-form prose), file (binary artefacts), and structured (JSON-Schema-described records).

Skills

The load-bearing array. Each skill is a named unit of capability with the following fields:

FieldRequiredDescription
idYesStable identifier, dot-namespaced by convention (kyc.review.individual).
nameYesHuman-readable name.
descriptionYesWhat the skill does, written for a planner agent (LLM- or rule-based) to understand.
tagsRecommendedSearchable taxonomy markers for marketplace indexing.
examplesOptionalSample invocation phrases. Helps planner LLMs match user intent to skill.
inputModes / outputModesOptionalPer-skill modality overrides.

Skills are how a planner agent picks among an agent's capabilities. The planner reads the description, tags, and examples; matches them against its current task; and submits the appropriate skill ID with input modes the skill declares. This is the design that makes A2A capability-oriented (versus OpenAPI's endpoint-orientation, discussed at length in §5 of the field guide).

Security schemes

The securitySchemes map declares auth schemes the agent accepts; the security array declares which schemes (or combinations) are required. The shape mirrors OpenAPI 3.x exactly, so any OpenAPI tooling for issuing, validating, and rotating Bearer / OAuth / mTLS credentials Just Works.

Common production patterns:

Signatures (JWS)

The signatures array carries one or more JWS-format cryptographic signatures over the canonicalised JSON body. Each signature has a protected header (base64url-encoded JOSE header with at least alg and kid) and a signature value (base64url-encoded JWS signature).

Key publication. The signing key is published at https://<host>/.well-known/jwks.json in standard JWKS format. The receiver fetches the JWKS, locates the key by kid, and validates the JWS.

Key rotation. Best practice is overlapping rotation windows: new key is published, traffic gradually transitions, old key is removed after a grace period. The JWKS endpoint must serve fresh keys at all times; cards cached in marketplaces should re-validate against the current JWKS.

The verification flow in code

A minimal verification flow in pseudocode:

// Fetch the card
card_json = http_get("https://kyc.example.com/.well-known/agent-card.json")
card = parse_json(card_json)

// Fetch JWKS
jwks_url = "https://kyc.example.com/.well-known/jwks.json"
jwks = http_get_json(jwks_url)

// Validate each signature
for sig in card.signatures:
    header = base64url_decode(sig.protected)
    kid = header["kid"]
    key = jwks_lookup(jwks, kid)
    if not key:
        reject("unknown signing key")

    // Reconstruct the JWS detached payload
    canonical_body = canonicalise(card_without_signatures_field)
    expected = sig.protected + "." + base64url(canonical_body) + "." + sig.signature

    if not jws_verify(expected, key):
        reject("signature failed verification")

// Optional: pin the key to the domain
if not key_bound_to_domain(key, "kyc.example.com"):
    reject("key not bound to domain")

accept(card)

Reference TypeScript types and validation utilities live at github.com/a2aproject/A2A. The Python and Java reference implementations follow the same shape.

What goes wrong in production

Three failure modes recur. First, operators forget to canonicalise the JSON body before signing — JSON is not unique-by-default, and signing a non-canonical form means receivers cannot reliably verify. Use RFC 8785 JCS or an equivalent. Second, JWKS endpoints are deployed once and never updated, so when a key is compromised the operator has no rotation path. Third, the url field is set to a development endpoint and forgotten — production traffic ends up routed to a staging environment.

Each has a known fix: canonicalise per RFC 8785, automate JWKS rotation on a calendar, gate url through environment-aware deployment.

Companion documents

Updated 2026-05-08.