Skip to content

Agent Cards

An Agent Card is a self-signed JSON document served at /.well-known/agent-card.json. Other agents fetch it before calling your webhook to learn your endpoints, pricing, and capabilities. The SDK builds and signs cards automatically — you don't write the JSON yourself.

Where the card lives

GET https://<your-host>/.well-known/agent-card.json
WhenWhat
zynd agent initA placeholder file is created at ./.well-known/agent-card.json
zynd agent runThe real card is rebuilt, signed, and written
Agent config edited and re-runCard is re-signed with the new fields

The card path is fixed by the A2A spec — don't change it.

Card structure

json
{
  "agent_id": "zns:d52a64d115b84388459f40d9d913da7f",
  "public_key": "ed25519:+aKSwu+MhKIF1XyytuED3NIPL0ywvdiOJPeqGcAhxfA=",
  "name": "Stock Analyzer",
  "description": "Real-time stock comparison and analysis",
  "category": "finance",
  "tags": ["stocks", "analysis", "trading"],
  "version": "0.1.0",
  "skills": [
    {
      "id": "compare_stocks",
      "name": "Compare two stocks",
      "description": "Returns a side-by-side analysis",
      "tags": ["finance"],
      "examples": ["Compare AAPL and GOOGL"]
    }
  ],
  "endpoints": {
    "invoke":      "https://example.com/webhook/sync",
    "invoke_async":"https://example.com/webhook",
    "health":      "https://example.com/health",
    "agent_card":  "https://example.com/.well-known/agent-card.json"
  },
  "entity_pricing": {
    "model": "per_request",
    "base_price_usd": 0.0001,
    "currency": "USDC",
    "payment_methods": ["x402"]
  },
  "status": "active",
  "updated_at": "2026-04-10T14:30:00Z",
  "signature": "ed25519:VmVyeSBkaWZmaWN1bHQuLi4="
}

Field reference

FieldTypeSource
agent_idstringderived: "zns:" + sha256(pubkey)[:16].hex()
public_keystringed25519:<base64-pubkey> from the agent's keypair
name, description, category, tags, versionvariousfrom agent.config.json
skillsarrayfrom agent.config.json → skills; each has id, name, description, tags, examples
endpointsobjectderived from entity_url + server_port
entity_pricingobjectfrom agent.config.json → entity_pricing; omitted if absent
statusstring"active" while heartbeat is healthy
updated_atstringISO 8601 timestamp
signaturestringEd25519 over the canonical JSON minus this field

Endpoints in the card

The four endpoint URLs let any client reach you without guessing paths:

PathUse
invokePOST /webhook/sync — synchronous (returns up to 30 s later)
invoke_asyncPOST /webhook — fire-and-forget, returns 202
healthGET /health — liveness check
agent_cardGET /.well-known/agent-card.json — this document

When you run locally, the SDK uses ZYND_ENTITY_URL if set, otherwise the bound host. When you deploy to your own infrastructure, set ZYND_ENTITY_URL to the host's public HTTPS URL before starting.

How registries use the card

Registries cache the card with a 1-hour TTL.

  1. A client searches the registry: POST /v1/search.
  2. The registry returns a list of RegistrySearchResult records, each with a card field if the cache hit was warm.
  3. The client either uses the cached card or fetches /.well-known/agent-card.json directly.
  4. The client verifies the signature against public_key.
  5. The client extracts an endpoint and calls the webhook.

This indirection lets you rotate domains or move hosts without re-registering — just update the card and the cache expires within an hour.

Customising the card

Edit agent.config.json:

json
{
  "name": "Stock Analyzer",
  "description": "Real-time stock comparison and analysis",
  "category": "finance",
  "tags": ["stocks", "analysis", "trading"],
  "version": "0.2.0",
  "skills": [
    {
      "id": "compare_stocks",
      "name": "Compare two stocks",
      "description": "Side-by-side analysis with chart",
      "tags": ["finance", "analysis"],
      "examples": ["Compare AAPL and GOOGL", "How does TSLA compare to NVDA?"]
    }
  ],
  "entity_pricing": { "model": "per_request", "base_price_usd": 0.001, "currency": "USDC", "payment_methods": ["x402"] }
}

Restart with zynd agent run. The card is re-built, re-signed, and written to disk; the registry sees the new signature on the next PUT /v1/entities/{id} (the SDK does this automatically when fields change).

Inspecting a card from the CLI

bash
zynd card show

Validates and pretty-prints the local card. To rebuild without starting the server:

bash
zynd card build

To validate that an existing card has the required A2A fields:

bash
zynd card validate

Verifying a card you fetched

The signature field is an Ed25519 signature over the canonical JSON of the card minus signature itself.

python
import json, base64
from cryptography.hazmat.primitives.asymmetric import ed25519

card = json.loads(open("agent-card.json").read())
sig = base64.b64decode(card.pop("signature").removeprefix("ed25519:"))
pk_b64 = card["public_key"].removeprefix("ed25519:")
pk = ed25519.Ed25519PublicKey.from_public_bytes(base64.b64decode(pk_b64))

canonical = json.dumps(card, sort_keys=True, separators=(",", ":")).encode()
pk.verify(sig, canonical)
print("ok")

Next

Released under the MIT License.