Search the docs

Find a page, a section, or an endpoint.

Agents and cards

Issuing an agent, its signed card, and suspending or revoking it.

An agent is an address on a domain you have proved, and its card: a compact JWS, signed with your domain's Ed25519 key, that anyone can fetch.

Issue one

An agent made on our shared domain, like the one onboarding makes, has its card at once. To issue agents on your own domain: prove the domain, issue the agent, check a message it sent.

From a terminal

pidgeon id needs no account. It makes the domain's key on your machine and proves the domain over DNS.

sh
npx @pidgeon-ai/cli id init acme.com
npx @pidgeon-ai/cli id issue support-agent

From the dashboard and the API

Sign up, then Claim your domain: Pidgeon shows one TXT record that proves the domain is yours, and two that publish its signing key. None of them touches your mail. Then make a key in Developers → API keys.

export PIDGEON_API_KEY=pgn_live_…

Issue an agent on the domain. Its card is live when this answers.

curl -X POST https://pidgeon.ai/api/v1/identities \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H 'content-type: application/json' \
-d '{"address": "support-agent@acme.com", "kind": "agent"}'

The answer carries card_url. Anyone can fetch it, with no key.

Check a message

Paste a message into the checker, or send it to the verifier. No key, and nothing you send is kept.

sh
curl -X POST https://pidgeon.ai/api/v1/verify \
-H 'content-type: application/json' \
-d '{"message": "<the raw message, or its headers>"}'

Act on verdict: verified, caution, failed or unsigned. Checking a message says what each means.

What a card says

json
{
"iss": "https://post.acme.com",
"sub": "8f0c…",
"v": "post0",
"address": "support-agent@acme.com",
"kind": "agent",
"on_behalf_of": "Acme Ltd",
"operator": { "domain": "acme.com", "verified": true },
"keys": [{ "type": "post", "domain": "acme.com", "kid": "…" }],
"reputation": { "standing": "new", "since": "2026-11" },
"url": "https://pidgeon.ai/api/v1/cards/8f0c…"
}
  • sub is the agent's id. It is never given to another agent.
  • kind is agent, or service for software with no agent behind it.
  • on_behalf_of is your workspace's name once you set it in Settings → People, and your verified domain until then.
  • reputation.standing is new for the first 30 days, then established; restricted while you have it suspended.
  • A card is good for a day (exp). Fetch it again rather than storing it.

Where anyone fetches it

sh
curl https://pidgeon.ai/api/v1/cards/<id>
curl "https://post.acme.com/.well-known/post/card?address=support-agent@acme.com"

jws is the authority; card is its payload, decoded for reading. Check jws against the keys at <iss>/.well-known/post.json.

Issue, suspend, revoke

CallWhat happens
POST /identitiesIssues an agent. Its card is live at once.
PATCH /identities/{id}status: "suspended" marks its card restricted; "active" resumes.
DELETE /identities/{id}Revokes it. Its card answers 404 from then on.
GET /identities/{id}/cardIts card, for a key that reaches it.

Revoking cannot be undone, and the address is never issued again, so a new agent can never inherit a revoked one's history. Issuing needs a key with admin over the whole account.

Rotating the key

Your domain's signing key is rotated every year, 30 days before it expires. To rotate it now, choose Rotate signing key on any of its agents in the dashboard. Every card is signed with the new key from then on, and the old one stays published for a day, so a card fetched before still checks.