Developer documentation

Verify AI agents before they access your API

Integrate cryptographic verification, trust policies, signing keys, MCP authorization, and analytics.

Introduction

AgentBouncer overview

Verify signed AI-agent requests and apply project-specific access policies.

AgentBouncer verifies HTTP Message Signatures sent by AI agents, resolves the signer public key, evaluates your project policy, and returns a final access decision.

The most important integration rule

Use allowed as the final authorization decision. verified reports the cryptographic result, while allowed reports the effective project-policy decision.

verifiedallowedMeaning
truetrueThe signature is valid and the request is allowed.
truefalseThe signature is valid, but the project policy denied access.
falsetrueThe request was not verified but was allowed by MONITOR_ONLY or another permissive setting.
falsefalseThe request failed verification and access was denied.
  • Verify RFC 9421 HTTP Message Signatures.
  • Resolve provider and project-owned public keys.
  • Apply trust tiers, trust scores, actions, tools, and custom rules.
  • Protect MCP tools, APIs, checkout operations, and autonomous workflows.
  • Record verification events for analytics and incident investigation.
Introduction

Quickstart

Create a project, copy an API key, and protect an endpoint.

  1. 1

    Create a project

    Open the AgentBouncer dashboard, create a project, and enter the origin or host of the service you want to protect.

  2. 2

    Start in Monitor only

    Use MONITOR_ONLY during the first integration phase. Requests continue to run while AgentBouncer records what would have been blocked.

  3. 3

    Copy the project API key

    The API key authenticates your backend when it calls the AgentBouncer verification API. It is not an agent signing key.

  4. 4

    Send the original request URL and signature headers

    The URL must match the URL that the agent signed. Preserve Signature, Signature-Input, and Signature-Agent.

  5. 5

    Authorize with allowed

    Reject the request only when allowed is false.

Verify a request with REST
bash
curl -X POST https://agentbouncer.io/api/v1/verify \
        -H "Authorization: Bearer ab_live_YOUR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "url": "https://merchant.example/api/mcp/weather",
          "method": "POST",
          "headers": {
            "signature": "sig1=:BASE64_SIGNATURE:",
            "signature-input": "sig1=(\"@authority\" \"signature-agent\");created=1760000000;expires=1760000300;keyid=\"KEY_ID\";tag=\"web-bot-auth\"",
            "signature-agent": "\"https://agent.example\"",
            "content-type": "application/json"
          },
          "expectedTag": "web-bot-auth",
          "action": "tools:call",
          "tool": "weather"
        }'
Apply the final decision
typescript
const verification = await verifyWithAgentBouncer(request);
      
      if (!verification.allowed) {
        return Response.json(
          {
            ok: false,
            error: "agent_access_denied",
            verification,
          },
          {
            status: verification.verified ? 403 : 401,
          }
        );
      }
      
      // Continue to the protected operation.
      return runProtectedTool();
Concepts

Core concepts

Understand projects, API keys, signing keys, providers, and policies.

ConceptPurpose
ProjectA protected application, API, MCP server, origin, or trust boundary.
Project API keyAuthenticates your backend when calling /api/v1/verify.
Project signing keyIdentifies and signs requests from one of your own agents.
ProviderA registered external AI-agent provider with a signature identity and public-key directory.
Provider keyA public key discovered in the provider HTTP Message Signatures directory.
Verification eventAn immutable record of the cryptographic result, policy decision, signer, risk, and request metadata.
PolicyRules that convert verification and trust information into allowed=true or allowed=false.

API keys and signing keys are different

The project API key authorizes calls from your server to AgentBouncer. A signing key is held by an agent and signs the original HTTP request.

Dashboard

Create and manage projects

Configure the trust boundary for an application or MCP server.

  1. 1

    Create the project

    Choose a descriptive name such as Production MCP, Checkout API, or Internal automation.

  2. 2

    Set the origin

    Use the public origin that agents call. Reverse-proxy and internal deployment URLs should not replace the externally signed URL.

  3. 3

    Choose a policy

    Start with MONITOR_ONLY, review verification events, and then move to BLOCK_UNKNOWN or a stricter policy.

  4. 4

    Create project signing keys if needed

    Use project keys for internal agents, development clients, and integrations that are owned by the project.

  5. 5

    Review events

    Inspect verified, allowed, reason, signer type, provider, project key, action, tool, risk, and policy details.

Recommended project boundaries

Use separate projects for production and development. Consider separate projects when applications have materially different trust or authorization requirements.

Dashboard

Project API keys

Authenticate your server when calling the AgentBouncer API.

Project API keys use the ab_live_ prefix and must only be stored on your backend. Never expose them in browser JavaScript, public repositories, or client-side environment variables.

.env.local
bash
AGENTBOUNCER_API_KEY=ab_live_YOUR_API_KEY
              AGENTBOUNCER_PUBLIC_ORIGIN=https://merchant.example
              AGENTBOUNCER_VERIFY_URL=https://agentbouncer.io/api/v1/verify
  • Store production keys in your hosting provider secret manager.
  • Rotate a key if it was logged or committed accidentally.
  • Do not send the API key to the incoming AI agent.
  • Do not confuse the AgentBouncer API key with a private signing JWK.
Integration

REST API integration

Call AgentBouncer from any language or backend framework.

POST /api/v1/verify
http
POST /api/v1/verify HTTP/1.1
      Host: agentbouncer.io
      Authorization: Bearer ab_live_YOUR_API_KEY
      Content-Type: application/json
      
      {
        "url": "https://merchant.example/api/mcp/weather",
        "method": "POST",
        "headers": {
          "signature": "sig1=:...:",
          "signature-input": "sig1=(...);created=...;expires=...;keyid=\"...\";tag=\"web-bot-auth\"",
          "signature-agent": "\"https://agent.example\""
        },
        "expectedTag": "web-bot-auth",
        "action": "tools:call",
        "tool": "weather",
        "userAgent": "ExampleAgent/1.0"
      }
FieldRequiredDescription
urlYesThe exact external URL whose request was signed.
methodRecommendedThe original HTTP method. Defaults to GET.
headersRecommendedOriginal request headers used for signature verification. Signature fields may alternatively be supplied through signature, signatureInput, and signatureAgent.
signatureConditionalThe Signature header value. Required if it is not included in headers.
signatureInputConditionalThe Signature-Input header value. Required if it is not included in headers.
signatureAgentNoThe Signature-Agent structured-field value. It may alternatively be included in headers.
expectedTagNoThe intent tag expected by the protected endpoint.
actionNoA semantic operation such as tools:call, products:read, checkout, or orders:write.
toolNoThe MCP tool or application capability being accessed.
userAgentNoThe original requester User-Agent for analytics.

Preserve the signed URL

Do not replace the public target URL with the AgentBouncer verification endpoint URL, an internal container URL, or a reverse-proxy URL.

Integration

JavaScript and TypeScript SDK

Verify signed requests and apply the final AgentBouncer policy decision.

Install
bash
npm install @agentbouncer/sdk
Create a client
typescript
import {
            createAgentBouncer,
          } from "@agentbouncer/sdk";
          
          export const agentBouncer =
            createAgentBouncer({
              apiKey:
                process.env.AGENTBOUNCER_API_KEY!,
              publicOrigin:
                process.env.AGENTBOUNCER_PUBLIC_ORIGIN,
              verifyUrl:
                process.env.AGENTBOUNCER_VERIFY_URL,
            });

Class constructor

The SDK also exports AgentBouncer. new AgentBouncer(options) and createAgentBouncer(options) create the same client.

Configure the public origin behind a proxy

If request.url contains an internal deployment or reverse-proxy URL, configure publicOrigin with the externally visible origin that agents sign. You can alternatively pass targetUrl to each verify() call.

verify()
typescript
const verification =
            await agentBouncer.verify({
              request,
              expectedTag: "web-bot-auth",
              action: "tools:call",
              tool: "weather",
            });
          
          if (!verification.allowed) {
            return new Response(
              "Agent access denied",
              {
                status: verification.verified
                  ? 403
                  : 401,
              }
            );
          }
          
          // Continue to the protected operation.
require()
typescript
import {
            AgentBouncerDeniedError,
            AgentBouncerError,
          } from "@agentbouncer/sdk";
          
          try {
            const verification =
              await agentBouncer.require({
                request,
                expectedTag: "web-bot-auth",
                action: "tools:call",
                tool: "weather",
              });
          
            return runWeatherTool({
              agent: verification.agent,
            });
          } catch (error) {
            if (
              error instanceof
              AgentBouncerDeniedError
            ) {
              return new Response(
                "Agent access denied",
                {
                  status:
                    error.verification.verified
                      ? 403
                      : 401,
                }
              );
            }
          
            if (
              error instanceof AgentBouncerError
            ) {
              return new Response(
                "Agent verification unavailable",
                {
                  status: 503,
                }
              );
            }
          
            throw error;
          }

SDK method semantics

verify() returns the complete verification result. require() throws AgentBouncerDeniedError when allowed is false. Network, timeout, API authentication, and malformed-response failures throw AgentBouncerError.

Integration

Next.js App Router

Protect a Next.js route while preserving the external request URL.

src/lib/agentbouncer-client.ts
typescript
import {
            createAgentBouncer,
          } from "@agentbouncer/sdk";
          
          const apiKey =
            process.env.AGENTBOUNCER_API_KEY;
          
          const publicOrigin =
            process.env.AGENTBOUNCER_PUBLIC_ORIGIN;
          
          if (!apiKey) {
            throw new Error(
              "Missing AGENTBOUNCER_API_KEY"
            );
          }
          
          if (!publicOrigin) {
            throw new Error(
              "Missing AGENTBOUNCER_PUBLIC_ORIGIN"
            );
          }
          
          export const agentBouncer =
            createAgentBouncer({
              apiKey,
              publicOrigin,
              verifyUrl:
                process.env.AGENTBOUNCER_VERIFY_URL,
            });
src/app/api/mcp/weather/route.ts
typescript
import {
            AgentBouncerDeniedError,
            AgentBouncerError,
          } from "@agentbouncer/sdk";
          import {
            NextRequest,
            NextResponse,
          } from "next/server";
          import {
            agentBouncer,
          } from "@/lib/agentbouncer-client";
          
          export const runtime = "nodejs";
          export const dynamic = "force-dynamic";
          
          export async function POST(
            req: NextRequest
          ) {
            const requestId = crypto.randomUUID();
          
            try {
              const verification =
                await agentBouncer.require({
                  request: req,
                  expectedTag: "web-bot-auth",
                  action: "tools:call",
                  tool: "weather",
                });
          
              const body = await req
                .json()
                .catch(() => ({}));
          
              const city =
                typeof body.city === "string"
                  ? body.city
                  : "Berlin";
          
              return NextResponse.json({
                ok: true,
                city,
                weather: {
                  temperatureC: 21,
                  conditions: "Clear",
                },
                agent: verification.agent,
                requestId,
              });
            } catch (error) {
              if (
                error instanceof
                AgentBouncerDeniedError
              ) {
                return NextResponse.json(
                  {
                    ok: false,
                    error: "mcp_access_denied",
                    reason:
                      error.verification.reason,
                    requestId,
                  },
                  {
                    status:
                      error.verification.verified
                        ? 403
                        : 401,
                  }
                );
              }
          
              console.error(
                "AgentBouncer verification failed",
                {
                  requestId,
                  error,
                }
              );
          
              return NextResponse.json(
                {
                  ok: false,
                  error:
                    "agentbouncer_unavailable",
                  message:
                    error instanceof
                    AgentBouncerError
                      ? error.message
                      : "Unable to verify the request.",
                  requestId,
                },
                {
                  status: 503,
                }
              );
            }
          }

Do not use the old gate

Do not use if (!verification.verified || !verification.allowed). That incorrectly blocks requests allowed by MONITOR_ONLY or allowUnsigned.

Integration

Next.js without the SDK

Send a minimal verification payload directly to the REST API.

Minimal header collection
typescript
import type {
                NextRequest,
              } from "next/server";
              import {
                pickVerificationHeaders,
              } from "./pick-verification-headers";
              
              function resolvePublicTargetUrl(
                req: NextRequest
              ) {
                const publicOrigin =
                  process.env
                    .AGENTBOUNCER_PUBLIC_ORIGIN;
              
                if (!publicOrigin) {
                  return req.nextUrl.toString();
                }
              
                return new URL(
                  `${req.nextUrl.pathname}${req.nextUrl.search}`,
                  publicOrigin
                ).toString();
              }
              
              export async function verifyAgentRequest(
                req: NextRequest,
                options?: {
                  expectedTag?: string;
                  action?: string;
                  tool?: string;
                }
              ) {
                const targetUrl =
                  resolvePublicTargetUrl(req);
              
                const response = await fetch(
                  process.env.AGENTBOUNCER_VERIFY_URL ??
                    "https://agentbouncer.io/api/v1/verify",
                  {
                    method: "POST",
                    headers: {
                      Authorization:
                        `Bearer ${process.env.AGENTBOUNCER_API_KEY}`,
                      "Content-Type": "application/json",
                      Accept: "application/json",
                    },
                    body: JSON.stringify({
                      url: targetUrl,
                      method: req.method,
                      headers:
                        pickVerificationHeaders(req),
                      expectedTag:
                        options?.expectedTag ?? null,
                      action:
                        options?.action ?? null,
                      tool:
                        options?.tool ?? null,
                      userAgent:
                        req.headers.get("user-agent"),
                    }),
                    cache: "no-store",
                  }
                );
              
                const result = await response
                  .json()
                  .catch(() => null);
              
                if (!response.ok) {
                  throw new Error(
                    result?.detail ||
                      result?.reason ||
                      `AgentBouncer returned HTTP ${response.status}.`
                  );
                }
              
                if (
                  !result ||
                  typeof result.verified !== "boolean" ||
                  typeof result.allowed !== "boolean"
                ) {
                  throw new Error(
                    "AgentBouncer returned an invalid response."
                  );
                }
              
                return result;
              }
Call the verification endpoint
typescript
import type { NextRequest } from "next/server";
      import { pickVerificationHeaders } from "./pick-verification-headers";
      
      export async function verifyAgentRequest(
        req: NextRequest,
        options?: {
          expectedTag?: string;
          action?: string;
          tool?: string;
        }
      ) {
        const targetUrl = req.nextUrl.toString();
      
        const response = await fetch(
          process.env.AGENTBOUNCER_VERIFY_URL ??
            "https://agentbouncer.io/api/v1/verify",
          {
            method: "POST",
            headers: {
              Authorization:
                `Bearer ${process.env.AGENTBOUNCER_API_KEY}`,
              "Content-Type": "application/json",
            },
            body: JSON.stringify({
              url: targetUrl,
              method: req.method,
              headers: pickVerificationHeaders(req),
              expectedTag: options?.expectedTag ?? null,
              action: options?.action ?? null,
              tool: options?.tool ?? null,
              userAgent: req.headers.get("user-agent"),
            }),
            cache: "no-store",
          }
        );
      
        const result = await response.json();
      
        if (!response.ok && response.status === 401) {
          throw new Error(
            "AgentBouncer API authentication failed."
          );
        }
      
        return result;
      }
Integration

Express integration

Protect Express routes using the REST verification endpoint.

typescript
import type {
        NextFunction,
        Request,
        Response,
      } from "express";
      
      export async function requireVerifiedAgent(
        req: Request,
        res: Response,
        next: NextFunction
      ) {
        try {
          const protocol =
            req.headers["x-forwarded-proto"] || req.protocol;
      
          const host =
            req.headers["x-forwarded-host"] ||
            req.headers.host;
      
          const targetUrl =
            `${protocol}://${host}${req.originalUrl}`;
      
          const verifyResponse = await fetch(
            "https://agentbouncer.io/api/v1/verify",
            {
              method: "POST",
              headers: {
                Authorization:
                  `Bearer ${process.env.AGENTBOUNCER_API_KEY}`,
                "Content-Type": "application/json",
              },
              body: JSON.stringify({
                url: targetUrl,
                method: req.method,
                headers: {
                  signature: req.headers.signature,
                  "signature-input":
                    req.headers["signature-input"],
                  "signature-agent":
                    req.headers["signature-agent"],
                  "content-type":
                    req.headers["content-type"],
                  "content-digest":
                    req.headers["content-digest"],
                },
                expectedTag: "web-bot-auth",
                action: "api:call",
              }),
            }
          );
      
          const verification = await verifyResponse.json();
      
          if (!verification.allowed) {
            res.status(verification.verified ? 403 : 401).json({
              ok: false,
              error: "agent_access_denied",
              verification,
            });
      
            return;
          }
      
          res.locals.agentVerification = verification;
          next();
        } catch (error) {
          next(error);
        }
      }
Integration

Protect MCP tools

Apply AgentBouncer decisions before executing MCP operations.

Set action to tools:call and tool to the MCP tool name. This makes verification events and custom policy rules specific to the operation being requested.

MCP authorization pattern
typescript
const verification = await agentBouncer.verify({
        request,
        expectedTag: "web-bot-auth",
        action: "tools:call",
        tool: toolName,
      });
      
      if (!verification.allowed) {
        return {
          isError: true,
          content: [
            {
              type: "text",
              text: "This agent is not allowed to call the tool.",
            },
          ],
        };
      }
      
      switch (toolName) {
        case "weather":
          return getWeather(toolArguments);
      
        case "catalog":
          return searchCatalog(toolArguments);
      
        case "checkout":
          return createCheckout(toolArguments);
      
        default:
          throw new Error("Unknown MCP tool");
      }
OperationSuggested actionSuggested tool
Read an MCP resourceresources:readresource name
List toolstools:list*
Call a tooltools:calltool name
Read productsproducts:readcatalog
Create checkoutcheckout:createcheckout
Submit an orderorders:createorders
Agent identities

Project signing keys

Create identities for agents owned directly by your project.

  1. 1

    Issue a project key

    Open the project Agent keys tab and create a descriptive key such as Production MCP agent or Internal CRM.

  2. 2

    Save the private JWK

    The private key is shown once. Store it in the agent secret manager.

  3. 3

    Keep the public identity

    AgentBouncer stores the public JWK, key ID, signature-agent identity, scopes, and policy configuration.

  4. 4

    Sign requests

    Use the private JWK to produce Signature and Signature-Input headers for the exact target URL.

  5. 5

    Revoke compromised keys

    Delete or revoke a project key immediately if its private JWK is exposed.

Example environment variables
bash
AGENT_KEY_ID=YOUR_KEY_ID
      AGENT_SIGNATURE_AGENT=https://agentbouncer.io/project-agents/PROJECT_ID/KEY_ID
      AGENT_PRIVATE_JWK='{"kty":"OKP","crv":"Ed25519","x":"...","d":"..."}'
Agent identities

Register an agent provider

Publish a provider identity and public-key directory.

  1. 1

    Create the provider

    Choose a stable slug, provider name, website, signature-agent origin, and directory URL.

  2. 2

    Publish the public JWKS

    Expose public keys at the configured HTTP Message Signatures directory.

  3. 3

    Refresh keys

    Ask AgentBouncer to load and validate the directory.

  4. 4

    Verify the provider domain

    Publish the AgentBouncer DNS TXT record or verification file.

  5. 5

    Sign production requests

    Use a private key corresponding to an active public key in the directory.

Example JWKS document
json
{
        "keys": [
          {
            "kty": "OKP",
            "crv": "Ed25519",
            "kid": "provider-key-2026-01",
            "alg": "EdDSA",
            "use": "sig",
            "x": "PUBLIC_KEY_MATERIAL"
          }
        ]
      }
Recommended public location
text
https://agent.example/.well-known/http-message-signatures-directory/jwks.json

Never publish the private JWK

Only public JWK properties belong in the provider directory. The private d property must remain secret.

Authorization

Policy modes

Choose how verified identity and trust data affect access.

ModeBehavior
MONITOR_ONLYRecords verification and policy failures but allows the request.
BLOCK_UNKNOWNBlocks unsigned requests and unknown identities.
ALLOW_TIER_1_ONLYAllows only the highest-trust provider tier.
ALLOW_TIER_1_AND_2Allows trusted Tier 1 and Tier 2 providers.
CUSTOMEvaluates provider, tier, project-key, action, and tool rules. Matching DENY rules take precedence over matching ALLOW rules; otherwise defaultEffect is applied.

Recommended rollout

Begin with MONITOR_ONLY, inspect real traffic, define actions and tools, test project keys, and only then enable blocking.

Authorization

Custom policies

Create fine-grained allow and deny rules.

Example custom policy
json
{
        "version": 1,
        "defaultEffect": "DENY",
        "rules": [
          {
            "id": "rule_allow_tier_1_read",
            "name": "Allow Tier 1 catalog reads",
            "enabled": true,
            "effect": "ALLOW",
            "subject": {
              "type": "PROVIDER_TIER",
              "tiers": ["TIER_1"]
            },
            "actions": ["products:read"],
            "tools": ["catalog"]
          },
          {
            "id": "rule_deny_external_checkout",
            "name": "Deny external checkout",
            "enabled": true,
            "effect": "DENY",
            "subject": {
              "type": "ANY_PROVIDER"
            },
            "actions": ["checkout:create"],
            "tools": ["checkout"]
          }
        ]
      }
  • DENY rules take precedence over ALLOW rules.
  • An empty actions array matches any action.
  • An empty tools array matches any tool.
  • The wildcard * matches any action or tool.
  • If no rule matches, defaultEffect is applied.
  • Use PROJECT_KEY to target selected internal agent keys.
API reference

Verification response

Understand the fields returned by the verification API.

json
{
        "verified": true,
        "allowed": true,
        "reason": null,
        "checks": {
          "signature": "valid",
          "timestamp": "valid",
          "replay": "unknown",
          "provider": "known",
          "policy": "matched"
        },
        "agent": {
          "type": "PROJECT_KEY",
          "provider": null,
          "providerSlug": null,
          "projectKey": {
            "id": "key_id",
            "name": "Production MCP agent",
            "scopes": []
          },
          "signatureAgent": "https://agentbouncer.io/project-agents/...",
          "keyid": "..."
        },
        "request": {
          "method": "POST",
          "path": "/api/mcp/weather",
          "tag": "web-bot-auth",
          "expectedTag": "web-bot-auth",
          "action": "tools:call",
          "tool": "weather"
        },
        "risk": {
          "score": 5,
          "level": "low"
        },
        "policy": {
          "mode": "CUSTOM",
          "wouldBlockReason": null,
          "matchedRule": {
            "id": "rule_allow_weather",
            "name": "Allow weather agent",
            "effect": "ALLOW"
          },
          "defaultEffectApplied": false
        }
      }

Replay detection

A valid signature does not by itself prove that the request has not been replayed. Until replay storage or nonce validation is enabled, checks.replay is reported as unknown.

FieldMeaning
verifiedWhether the cryptographic identity verification succeeded.
allowedThe final authorization decision after applying project policy.
reasonThe effective denial reason, or null when access is allowed.
checksIndividual signature, timestamp, replay, provider, project-key, and policy results. unknown means that a check was not performed or sufficient information was not available.
agentResolved provider or project-key identity.
riskCalculated risk score and level.
policyPolicy mode, matching rule, and hypothetical block reason.
API reference

Reasons and errors

Common verification and policy decision reasons.

ReasonDescription
unauthorizedMissing or invalid AgentBouncer project API key.
invalid_requestThe verification payload is missing a required field.
invalid_urlurl is not a valid absolute target URL.
no_signatureSignature or Signature-Input is missing.
no_keyidSignature-Input does not contain keyid.
unknown_keyidNo active project or provider key was found.
not_yet_validThe signature creation time is in the future.
expiredThe signature has expired.
window_too_longThe created-to-expires signature window exceeds the permitted maximum.
bad_signatureCryptographic verification failed.
unknown_providerThe key provider is not recognized.
provider_blockedThe provider is suspended, banned, fraudulent, or revoked.
provider_not_trustedThe project requires a trusted provider.
unsupported_tagThe signature intent tag is unsupported.
tag_mismatchThe actual signature tag does not match expectedTag.
tier_too_lowThe provider does not satisfy the required tier.
trust_score_too_lowThe provider trust score is below the project threshold.
project_keys_disabledProject-owned signing keys are disabled.
custom_policy_deniedA matching custom DENY rule rejected the request.
custom_policy_no_matchNo custom rule matched and the default effect is DENY.
Operations

Verification events

Monitor real traffic and investigate authorization decisions.

  • Filter events by day, week, month, all time, or custom dates.
  • Compare verified requests with allowed requests.
  • Use allowed, not verified, when labeling an event as accepted or rejected.
  • Inspect provider tier, status, trust score, abuse score, risk, action, and tool.
  • Review wouldBlockReason for requests allowed by MONITOR_ONLY.
  • Export event data as CSV for offline analysis.
verifiedallowedRecommended UI label
truetrueAllowed
truefalseDenied by policy
falsetrueAllowed in monitor mode
falsefalseVerification failed
Operations

Security guidance

Deploy AgentBouncer without leaking secrets or weakening verification.

  • Keep AgentBouncer API keys on the server.
  • Keep private signing JWKs in a secret manager.
  • Publish only public JWK properties.
  • Use HTTPS for target URLs, signature-agent identities, and key directories.
  • Preserve the original externally visible URL and HTTP method.
  • Do not forward Authorization, Cookie, Set-Cookie, x-api-key, or proxy credentials in verification payloads.
  • Exclude reverse-proxy and observability headers unless they are intentionally signed.
  • Use short signature validity windows.
  • Revoke compromised project and provider keys.
  • Start policy changes in MONITOR_ONLY before enforcing them.

Request body integrity

AgentBouncer verifies the HTTP Message Signature over the covered request components supplied to the verification API. Applications that rely on a signed Content-Digest must also ensure that the digest matches the body received by the protected endpoint.

Operations

Troubleshooting

Diagnose signature, URL, key-directory, and policy problems.

ProblemWhat to check
Invalid Signature headerEnsure Signature, Signature-Input, and Signature-Agent are each included exactly once.
bad_signatureCompare the signed URL, method, authority, covered components, public key, and content digest.
unknown_keyidConfirm the project key is active or refresh the provider public-key directory.
tag_mismatchCompare expectedTag with the tag in Signature-Input.
custom_policy_no_matchCheck subject type, project key ID, provider tier, action, tool, and defaultEffect.
MONITOR_ONLY still blocksMake sure your application checks only verification.allowed.
Wrong @authorityUse the external host that the agent signed, not an internal deployment host.
Correct authorization gate
typescript
if (!verification.allowed) {
        return Response.json(
          {
            ok: false,
            error: "agent_access_denied",
            verification,
          },
          {
            status: verification.verified ? 403 : 401,
          }
        );
      }
Incorrect authorization gate
typescript
// Incorrect: blocks MONITOR_ONLY requests.
      if (!verification.verified || !verification.allowed) {
        return new Response("Denied", {
          status: 403,
        });
      }
Operations

Production checklist

Review the integration before enabling blocking policies.

  • The AgentBouncer API key is stored only on the server.
  • The production project uses the correct public origin.
  • The application sends the exact signed target URL.
  • Signature headers are forwarded exactly once.
  • Sensitive and infrastructure headers are excluded.
  • The authorization gate checks allowed.
  • Actions and MCP tool names are populated.
  • MONITOR_ONLY traffic has been reviewed.
  • Known providers and project keys resolve correctly.
  • Custom policies have an intentional default effect.
  • Private signing keys are stored in a secret manager.
  • Key revocation and rotation procedures are documented.
  • Verification event exports have been tested.