Gateway overview
Put AgentGate in front of any HTTP app. Routes map requests to actions; GET /v1/gateway/check decides each request before it reaches your origin.
The gateway decides every protected request with the same engine as the browser widget: tokens, clearances, verified agent signatures, rate policy and the site's rules. Your proxy or middleware asks GET /v1/gateway/check before forwarding, and forwards only on a 204.
Use it when you want protection without touching each handler, for routes that browsers never call (APIs, webhooks, agents), or when the same routes serve people and agents.
1. Describe the site's routes
A site's routes map a request to an action, and the action says what evidence it needs. Set them in the console (site settings) or with the admin API:
{
"allowedOrigins": ["https://app.example.com"],
"actions": {
"signup": {"browserRequired": true},
"checkout": {"browserRequired": true, "clearance": true},
"catalog_read": {"browserRequired": false},
"agent_orders": {"browserRequired": false, "requireContentDigest": true}
},
"routes": [
{"method": "POST", "path": "/api/signup", "action": "signup"},
{"method": "POST", "path": "/checkout", "action": "checkout"},
{"method": "GET", "pathPrefix": "/api/catalog/", "action": "catalog_read"},
{"method": "POST", "path": "/api/orders", "action": "agent_orders"}
]
}- Browser routes (
browserRequired: true) need a single-use token from the browser SDK inX-AgentGate-Token, or, forclearance: trueactions, a valid clearance (X-AgentGate-ClearanceorAgentGate-Clearanceheader, or theagentgate_clearancecookie). Without one the answer is a challenge (401). - Machine routes (
browserRequired: false) never ask for browser evidence: rate policy, agent identity and rules only. Requests no route matches are evaluated the same way. - The action always comes from this configuration, never from the client. Paths are normalised like nginx:
/api/./signup,/api//signupand/api/%73ignupare all/api/signup. requireContentDigest: truemakes a signed agent's signature cover the request body'sContent-Digest(see Signed agents).
The page sends the token as a header:
const { token } = await AgentGate.execute({ action: "signup" });
await fetch("/api/signup", { method: "POST", headers: { "X-AgentGate-Token": token }, body });Note
nginx auth_request never sees the request body, so a hidden form field cannot reach the gateway. For classic form posts, validate the token in your backend with /v1/siteverify instead.
2. The check API
GET /v1/gateway/check, called by your gateway only (never by browsers):
| Request header | Meaning |
|---|---|
Authorization: Bearer ags_… | the site's backend secret (required) |
X-AgentGate-Site-Key | optional; must name the secret's site |
X-Original-Method, X-Original-Host, X-Original-URI, X-Original-IP | the original request (required) |
X-Original-Proto | http or https (default https) |
X-Original-Body-Digest | match or mismatch if the adapter checked Content-Digest against the body; omit when it never saw the body |
X-AgentGate-Token, X-AgentGate-Clearance, Cookie | the client's token and clearance |
Signature, Signature-Input, Signature-Agent, Content-Digest, User-Agent, … | the client's own headers, forwarded |
X-Original-* headers are believed only from a caller with a valid secret for the site; without one the answer is 403 invalid_credential and nothing is evaluated.
| Answer | When | Headers |
|---|---|---|
204 | allow; always in monitor mode | X-AgentGate-Decision: allow, X-AgentGate-Reason, X-AgentGate-Decision-ID, X-AgentGate-Agent (verified agent), X-AgentGate-Would-Decision (monitor), X-AgentGate-Digest-Check: required |
401 | challenge: get a (new) token | X-AgentGate-Reason, for example receipt_required, token_used, expired_clearance |
403 | block, or a bad secret or metadata (every mode) | X-AgentGate-Reason, for example rate_limited, agent_signature_invalid, policy, invalid_credential |
503 | dependency failure in enforce mode | X-AgentGate-Reason: service_unavailable, Retry-After: 5 |
Every reason is listed on Error codes. The endpoint never answers 429; the nginx configuration maps 403 rate_limited to 429 for the client and 403 payment_required to 402. In monitor mode a failing token verifier is recorded and the request allowed with reason service_unavailable, never silently.
Rate budgets per site: GATEWAY_IP_PER_MINUTE per client address and GATEWAY_AGENT_PER_MINUTE per verified agent without its own quota (both default 600). Exceeding one labels the request; the rules decide.
When a token is spent
A token is spent only by the request it admits:
- the check first validates it without spending it (signature, site, action and origin, expiry, not yet used), then evaluates the request;
- only an allow spends it, in one transaction with the decision event;
- a block or challenge after a valid token (a rate limit, a rule, a bad signature) leaves it unspent;
- if the transaction fails, enforce mode answers
503and the token stays unspent, so retrying the same operation with it is safe; - of several concurrent requests carrying one token, exactly one is allowed; the others get
401 token_used.
This is exactly-once admission at AgentGate, not exactly-once execution at your origin: if your origin fails after a 204, the token is spent and the client needs a new one.
Signed agents
Web Bot Auth signatures are verified at the gateway over @authority, @method, @path and the covered Signature-Agent member. Because auth_request never sees the body, content-digest is required only on actions with requireContentDigest: true. There the gateway checks that the signature covers it and answers X-AgentGate-Digest-Check: required, and your middleware verifies the digest against the body. A middleware that checked the body itself sends X-Original-Body-Digest instead.
3. Pick an adapter
- nginx:
auth_requestin front of any app, tested against a real nginx. - Go and Node middleware: inside your app, with body digest checks.
4. Go live
- Start in monitor mode and watch the would-have decisions for false positives.
- Switch the site to enforce.
- Rotate the backend secret with an overlap window: update your gateway's secret and reload before the old one expires.
Limits: rate budgets are process-local (one AgentGate instance per state directory), and content classifiers do not run on gateway checks because the gateway sees no body.