---
title: Admit
description: A site verifies an agent's edge signature against its company's public key directory and learns which company sent it, never what it may spend. Coming with the SDK.
canonical: https://tollrelay.com/docs/admit
lastmod: 2026-10-17
---

# Admit

A site verifies an agent's edge signature against its company's public key directory and learns which company sent it, never what it may spend. Coming with the SDK.

## The two signatures

Coming: the pass names the organisation's origin once the origin is served. The SDK sends the `wba` signature for a pass that names one, so until then it sends the `trlay` signature only.

Every request an agent sends carries two RFC 9421 signatures from one device key, under two labels in the same `Signature-Input` and `Signature` headers. A verifier reads one label and ignores the other.

|  | `trlay` | `wba` |
| --- | --- | --- |
| For | The seller's check and TollRelay | Any site at the edge |
| `tag` | `trlay-agent` | `web-bot-auth` |
| Covered | `@method`, `@authority`, `@path`, `@query`, `content-digest`, `trlay-requester` when present | `@authority`, `@path`, `signature-agent;key="wba"`, `content-digest` when a body is sent |
| Window | `created` plus 30 seconds | `created` plus 30 seconds |
| `nonce` | 16 random bytes, refused on repeat by TollRelay | 16 random bytes; the edge may not check it, so the window is the bound |
| `keyid` | The key's fingerprint | The same fingerprint |

The `Signature-Agent` header is `wba="https://<handle>.agents.trlay.dev"`, the origin of the organisation's key directory. The `keyid` is the JWK SHA-256 thumbprint of the device key, so the same fingerprint names the key in both signatures, the directory and the agent's DID document.

## The key directory

Coming: the origin and the directory are not served yet, so the address above does not answer today.

```http
GET https://<handle>.agents.trlay.dev/.well-known/http-message-signatures-directory
```

Each organisation has one public origin, `<handle>.agents.trlay.dev`. It serves a JWK Set of the organisation's active agent keys at the standard's well-known path. Each key carries `kty`, `crv`, `x`, `kid` (the fingerprint), `use` and `alg`, and never a private member.

The directory lists one company's keys, so it stays small. Responses carry `Cache-Control: public, max-age=300` and an `ETag`. A removed key leaves the directory within five minutes plus the verifier's own cache.

The directory is served unsigned in the beta. A verifier fetches it over TLS from the organisation's origin. A verifier that insists on a signed directory will not accept it yet.

A site that already runs a Web Bot Auth verifier can admit these agents with no TollRelay code: the signature, the directory and the key id are the standard's.

A sample entry. The values are fixed when the directory ships.

```http
Content-Type: application/http-message-signatures-directory+json
Cache-Control: public, max-age=300

{ "keys": [ { "kty": "OKP", "crv": "Ed25519", "x": "…", "kid": "<fingerprint>", "use": "sig", "alg": "…" } ] }
```

## admit()

Coming: the trust list names each company's verification state once the API publishes it. Until then `verified` is `false` for every agent.

`admit(request, options)` is for a site that sells nothing. It answers who is asking, never how much they may spend.

```ts
import { admit } from "@tollrelay/sdk";

const answer = await admit(request);
if (!answer.ok) return refuse(answer.reason);
// answer: { ok: true, handle, fingerprint, verified }
```

It does these, in order:

1. Reads the `wba` member of the signature headers.
2. Requires the `Signature-Agent` origin to end in `.agents.trlay.dev`, or in the suffixes the site passes as `options.suffixes` (`.agents.trlay.sh` for staging).
3. Fetches the directory, cached under its cache headers.
4. Verifies the signature over the site's own `@authority` and the signature window. A site behind a proxy that rewrites the host passes its own as `options.authority`.
5. Reads the organisation's verification state from the trust list.

It answers `{ ok: true, handle, fingerprint, verified }` or `{ ok: false, reason }`, where the reason is one of `no_signature`, `malformed`, `origin_not_allowed`, `directory_unavailable`, `unknown_key`, `invalid` or `expired`. It returns no problem code, because it answers the site and not the agent. `verified` is `true` only when the trust list says the company is verified.

It never reads a pass, a limit or a spend, and it never calls TollRelay's check. Admitting an agent says the company is known. It does not say what the agent may buy.

## The pass, shown when asked

The first request to a host carries the `wba` signature only. The pass and the requester pseudonym travel to a host that asks for them.

- A host asks with a `401` carrying `WWW-Authenticate: Bearer realm="tollrelay"`, which `gate()` already sends, or with an x402 `402` carrying the identity extension.
- The SDK retries once with the pass and the requester header, and remembers the host for the life of the client.
- The option `showPass: "always"` sends both on every request, as the SDK did before 0.9.0.
- A pass that carries `edge: false` sends no `wba` signature.
- Coming: an owner or admin will be able to switch the edge signature off for one agent, and a sandbox pass will always carry `edge: false`.

## The agent lane

Coming: the sandbox desk is not live yet.

A site that wants agents but not all of them mounts `gate()` on a route that is set apart for them. The route has its own terms, its own rate limits and a price per call on x402. The agent lane lets agents in without blocking them, and the price and the limits are the site's to set.

The sandbox desk is the worked example. It will show an agent lane running end to end, with a sandbox pass.
