The short answer
A website tells which AI agent made a request by checking the request's signature: the agent signs it with HTTP Message Signatures (RFC 9421) in Web Bot Auth's profile, and names a key directory where anyone can fetch the public key. Pidgeon signs in that same profile and adds the agent's address, so POST /v1/verify with the request returns not only that the signature holds but which agent sent it and which domain operates it. A site that already checks Web Bot Auth signatures needs nothing new to read a Pidgeon agent's.
Why a User-Agent string is not an answer
An agent can name itself in its User-Agent header, and a site can look at the IP range a request came from. Neither proves anything. Anyone can send User-Agent: SomeAgent/1.0, and an IP range says which network the request left from, not which of the agents on that network made it.
A signature is different in kind. The agent holds a private key, the site fetches the public half from a place the agent's domain controls, and a request whose covered parts changed by one byte fails. The question moves from "what does this request say about itself" to "which key signed it, and who publishes that key".
What a signed request carries
Web Bot Auth defines how an automated client signs: an Ed25519 key, a keyid that is the key's JWK thumbprint, the tag web-bot-auth, and a Signature-Agent header naming where the key directory lives. Pidgeon's signer follows that profile and covers more of the request, so a signature holds for this one request and not for any request to the same host:
@method,@authority,@path, and@querywhen there is one.Signature-Agent, which ishttps://post.acme.comfor an agent on acme.com.From, the agent's address,support-agent@acme.com. This is the part Post adds: one domain's key can sign for many agents, andFromsays which one.Content-Digest, a SHA-256 of the body, when there is a body.
Each signature is good for five minutes. The key directory sits at https://post.acme.com/.well-known/http-message-signatures-directory as a JSON Web Key Set, and lists the same keys the domain uses to sign its agents' email. One key, published once, signs the agent's mail and its HTTP requests.
Checking a request with Pidgeon
Send the request as you received it: the method, the full URL, the headers and the body. No key is needed.
curl -X POST https://pidgeon.ai/api/v1/verify \
-H 'content-type: application/json' \
-d '{
"request": {
"method": "POST",
"url": "https://api.yourshop.com/v1/orders",
"headers": {
"signature-agent": "\"https://post.acme.com\"",
"from": "support-agent@acme.com",
"content-digest": "sha-256=:…:",
"signature-input": "sig1=(\"@method\" \"@authority\" \"@path\" \"signature-agent\" \"from\" \"content-digest\");created=…;expires=…;keyid=\"…\";alg=\"ed25519\";nonce=\"…\";tag=\"web-bot-auth\"",
"signature": "sig1=:…:"
},
"body": "{\"sku\":\"A-1\",\"qty\":1}"
}
}'
Pidgeon finds the key by its keyid among the domains proved with pidgeon id, or, for any other signer, in the directory its Signature-Agent names. The answer is the same shape as for an email: a verdict to act on, sender.address when the covered From is at the signing domain, operator.domain, body (intact when the digest matched), and signatures.request with the keyid. When the agent's domain was proved with Pidgeon, the answer also carries its agent card: kind (agent or service), who it acts for, and its standing.
The same check is the request field of the MCP tool verify_sender, for an agent that is itself receiving calls.
Signing as the agent
pidgeon id gives an agent on your own domain an identity with no account. It makes an Ed25519 key on your machine, which never leaves it, and asks you to add one TXT record and one CNAME:
npm install -g @pidgeon-ai/cli
pidgeon id init acme.com
pidgeon id issue support-agent
The TXT record under _post proves the domain is yours and names the key. The post CNAME makes post.acme.com serve the key directory and the agent's card to anyone.
The library that signs every request, @pidgeon-ai/id, wraps fetch: signedFetch(identity, { agent: 'support-agent@acme.com' }) returns a fetch that adds Signature-Agent, From, Content-Digest, Signature-Input and Signature to each call. Because a call to an MCP server over Streamable HTTP is an HTTP request, the same fetch handed to an MCP client transport signs every tool call too. The library is Apache-2.0 and coming soon to npm; the pidgeon id commands are in the CLI today.
Pidgeon and Web Bot Auth
Pidgeon does not replace Web Bot Auth. It is one more way to find out who holds the key. A request a Pidgeon agent signs is a Web Bot Auth request: same profile, same key directory, same keyid. A site that checks those signatures already reads it unchanged.
What Pidgeon adds sits beside the signature, not inside it:
- An address. Web Bot Auth names the operator behind a key. The covered
Fromnames one agent among many under that key. - A card. From the address, anyone can fetch a signed card saying whether it is an agent or a service, who it acts for, which domain operates it and whether that domain was proved.
- One identity across channels. The key that signs the request also signs the agent's email, so the agent a site sees is the agent a support desk sees in its inbox.
Limits
- A signature says who, not what is allowed.
verifiedmeans the domain's key signed this request and nothing covered changed. Whether the agent may place the order, read the record or call the tool is your site's decision, made by your own authorisation, as it is today. - Coverage matters. A signature over
@authorityalone verifies for any request to your host. Pidgeon's answer then saysbody: not_checked; insist on the components you need. - A directory that does not answer gives
unsignedwith the warningenvelope_unverifiable, rather than a guess. - An address in a Pidgeon account signs its mail with a key Pidgeon holds for its domain, so it cannot sign HTTP requests this way. Request signing is for domains whose operator holds the key, through
pidgeon id. - Sending the request to Pidgeon means sending us its headers and body. Nothing sent is stored or logged, and each IP can check 300 an hour.
Start
Read checking a message for the verdicts, which are the same for a request, and agents for what a card says. To give your own agents keys, start at the quickstart. The draft that defines all of it is on the protocol page.