Delegus

ReceiptsRelease

Delegus now works in front of any web API

Until today, Delegus checked AI agents in front of MCP servers. That covers the tools agents call. It doesn't cover where a lot of the money moves: the payment, supplier and banking systems a company already runs, which speak ordinary web APIs.

Team Delegus

  • 3 min read

So now it works there too. Put Delegus in front of a web API, in any language, and every request is checked against the authority behind it before the API sees it. An allowed request goes through with its receipt attached. A refused one never reaches the API, and the agent gets the reason.

A gateway for any API#

The new gateway sits in front of your API. Your API needs no Delegus code at all; it could be Java, Go, Python or anything else.

text
DELEGUS_RP_KEY=dk_rp_… npx @delegus/http-gateway \
  --upstream http://api:8080 --public-base-url https://pay.example.com --route-map routes.json

The route map tells Delegus what a request means. For a payment endpoint, it says which field is the amount and which is the payee, so the check isn't just "may this agent call /payments" but "may this agent pay this payee this amount, right now":

json
{ "version": 1, "routes": [
  { "id": "payments.create", "method": "POST", "path": "/payments",
    "action": "commerce:purchase",
    "resource": "payee:{arg:/body/payee}",
    "amount": { "value": "/body/amount", "currency": "/body/currency", "unit": "minor" } }
] }

Each request ends one of three ways:

  • Allowed. It's forwarded with the exact bytes that were checked and a Delegus-Receipt-Id header.
  • Refused. The API never sees it; the caller gets a 403 with the reason and the receipt.
  • Missing proof. The caller gets a 401 telling it what to present.

If Delegus can't be reached, the gateway refuses. It fails closed.

If your API runs on Node, you can skip the extra hop and use the same check as middleware: delegus.http() for Express-style servers, or a Fastify hook. Both are in @delegus/sdk 0.3.1.

One payment, step by step#

Acme gives its procurement agent a permission, signed with Acme's own key: pay approved vendors, up to $25,000 a payment, until the end of the year. SupplierPay runs an ordinary payments API with the Delegus gateway in front of it.

  1. The agent asks. It sends POST /payments for $12,000 to VendorCo. With the request go two things: Acme's permission, and a proof the agent signed for this exact request.
  2. The gateway reads the request. The route map says this route is a purchase, the payee is vendorco, and the amount is $12,000.
  3. Delegus checks. Is the permission really signed by Acme, still in date, and not revoked? Did this agent sign this exact request, just now, for the first time? Is a purchase allowed, is VendorCo an approved payee, and is $12,000 within $25,000?
  4. Allowed. The gateway forwards the request to SupplierPay's API with a receipt id. The payment goes through.
  5. The agent tries $40,000. Refused, over its limit. SupplierPay's API never sees the request, and the agent gets the reason and a receipt.
  6. Acme takes the permission back. From the next request on, anywhere, the agent is refused.
  7. Months later, a dispute. Either side can show the signed receipt. Anyone can check it offline against Delegus's published key, without trusting Acme or SupplierPay.

Why this matters#

When an agent from one company asks another company's system to move money, the receiving side has a hard question. Did that company really authorize this agent to do this, at this amount, right now? Logs on the sender's side don't answer it. The receiver needs to check for itself.

We ran it end to end on a payment flow against a test bank we built. A treasury agent tried four payments. Three were refused before the bank was ever called: one over its limit, one to a payee its company never approved, one after its company took the permission back. One went through, carrying its receipt, and the bank's own log shows exactly that one call.

A treasury agent makes four payments against a test bank we built. Three are refused before the bank is ever called; one goes through, carrying its receipt. 38 seconds.

Also out today#

  • Claude Code as the agent. @delegus/agent-proxy runs on your machine, holds the agent's key and permission, and signs every tool call. Claude Code connects to it with one line: claude mcp add --transport http tools http://127.0.0.1:8787/mcp.
  • A safer MCP gateway. @delegus/mcp-gateway 0.3.1 takes one --endpoint, the exact address agents call and sign for. If the setup can't line up, it refuses at startup and says how to fix it, instead of refusing every call later.
  • Python agents can sign their own calls. delegus-mcp 0.3.1 adds mcp_headers for the agent side, on top of the server middleware it already had.

Try it#