The three calls
Register the organisation, issue a pass, set what it may spend. Everything after that is a header on requests you already make.
The quickest way in is npx @tollrelay/cli init --region eu --handle acme, which registers your organisation, sends you a sign-in link and gives your first agent its pass, so all that's left is setting its limit. If you'd rather make the calls yourself, they're below in the order you'll make them.
Register the organisation
POST https://pr-281-api.trlay.sh/identity/orgs
{ "name": "Acme Research", "handle": "acme", "region": "eu", "email": "you@acme.example" }One call, one verification. The organisation is the thing sellers trust; passes hang off it. The region is eu or us. It is chosen here and cannot be changed later.
Issue a pass
POST https://pr-281-api.trlay.sh/identity/machines
{ "org": "org_eu_…", "label": "procurement-agent", "public_key": { "kty": "OKP", "crv": "Ed25519", "x": "…" }, "alg": "ed25519", "tier": "file" }One per agent, under the organisation. The agent makes its own key where it runs and sends only the public half; trlay pass create does both in one step, and the private half never leaves the machine. Name it what your team calls the agent; receipts show it as acme/procurement-agent.
Set a spending limit
PUT https://pr-281-api.trlay.sh/mandates/mid_eu_…
{ "usd_per_day": "50", "usd_per_call_max": "1", "sellers_allow": ["data.example.com", "gpu.example.com"] }Per day, per call, per seller. The API calls this a mandate. The pass carries it, so sellers can see the limit before they charge.
A limit can also say where it may be used from. requires: { key_tier: "hardware" } allows only a key held in approved hardware, and "service" refuses a key held in a file. A request signed by a lower tier is refused with 403 device_not_allowed and a receipt. The dashboard offers it as Only from approved hardware and Not from a file key.
Sign it off
POST https://pr-281-api.trlay.sh/mandates/mid_eu_…/versions
{ "constraints": [{ "type": "com.tollrelay.usd_per_day", "max_usd": "50" }] }Propose a version, and a person who approves reads the constraints in words and signs or refuses on the dashboard. No agent key reaches that screen. A version that creates or widens what the agent may spend needs two signatures once the organisation is verified and has two or more people who can sign (an owner or an admin), and never the agent's creator alone, unless they are the only person at the company who can sign.
Issuing the signed credential from a completed signature is coming.
Attach it to every request
The SDK signs and adds one header, X-Trlay-Identity. If the seller charges, the SDK checks the spending limit first, as a courtesy. The seller's check and the wallet enforce it.
Pushing that limit into the wallet you already use is coming.
Approvals
A call that trips a rule on the spending limit, a first payment to a new seller or a cumulative cap, waits for a person instead of failing.
POST https://pr-281-api.trlay.sh/approvals
{ "mid": "mid_eu_…", "mandate_version": 3, "closed_mandate": "…", "seller": "data.example.com", "amount_usd": "1.500", "rail": "x402" }A person reviews it on the dashboard and approves or refuses with a signed decision. The agent reads the item back for the outcome before it tries the call again.
Add a device
GET https://pr-281-api.trlay.sh/identity/machines/mid_eu_…/keysAn agent holds one key per device it runs on. A new device registers its public key and the key is pending. A pending key mints no pass and passes no check until an owner or admin approves it on the dashboard or with trlay keys approve, or the device policy has.
The tier says where the key is held: hardware, proven by the device maker's attestation, service for a key service, or file. A key that names hardware without attestation is recorded as file.
The agent's DID document lists every active key as a verification method with the id #<fingerprint>.
Set the device policy
POST https://pr-281-api.trlay.sh/org/device-policy
{ "rules": [{ "platform": "android-key-attestation", "roots": ["google-key-attestation-ca1"], "packages": ["com.acme.procurement"], "signing_certs": ["<sha-256 of the app signing certificate>"] }] }An owner sets one policy for the organisation. A hardware key whose verified attestation matches a rule is active on registration, approved by policy. Android key attestation is the first platform.
A rule needs the package and the app signing certificate, because any app can claim a package name. With no policy, every key waits for a person.
Revoke a device
POST https://pr-281-api.trlay.sh/identity/machines/mid_eu_…/keys/<fingerprint>/revokeAn owner or admin revokes a key on the dashboard or with trlay keys revoke. The check refuses it at once, and a revoked key never becomes active again. Revoking an agent refuses every one of its keys.
The pass names its place on the status list in status, as idx and uri. The list shows a revoked key within five minutes, so a seller that checks offline stops accepting it by then.
Read the history
Every checked call lands in your history: seller, amount, receipt. Export it for finance.
Device names the key that signed each entry: where it is held (hardware, a key service or a file) and its fingerprint. An entry from before devices were recorded leaves it empty. The CSV adds signed_by and key_tier as its last columns.
Asked by is a pseudonym of the person who asked the agent to buy, which only you can turn into a name, with trlay record --resolve.
A signed proof of each order for an audit, per region, is coming.
On the other side, a seller runs a check to read the pass and the spending limit that remains. See Verify.
Where your agents run
One line gives an agent its pass wherever it runs. The SDK and the framework packages sign the agent's requests with its key and carry its pass, and take the options signedFetch takes: the signer, the pass and the mandate.
| Where | What to add |
|---|---|
| Your own code | @tollrelay/sdksignedFetch({ signer, pass }) |
| Vercel AI SDK | @tollrelay/vercel-aitollrelayFetch({ signer, pass }) |
| LangChain JS | @tollrelay/langchaintollrelayTool({ name, schema, identity, func }) |
| OpenAI Agents SDK | @tollrelay/openai-agentstollrelayTool({ name, parameters, identity, execute }) |
| Cloudflare Agents SDK | @tollrelay/cloudflare-agentstollrelayFetch({ signer, pass }) |
| An MCP client that sends an API key | https://pr-281-api.trlay.sh/mcpAuthorization: Bearer <organisation API key> |
| A terminal | @tollrelay/clinpx @tollrelay/cli init --region eu --handle acme |