Skip to main content

Per-profile access control for AI agents at the API gateway layer

How to configure AI agent access control at the API gateway: dedicated clients, scoped collections, method restrictions, and per-agent rate limits in Zerq.

  • ai-agents
  • mcp
  • access-control
  • security
  • governance
Zerq team

AI agent access control at the API gateway layer is the gap most teams discover after the fact. An autonomous agent ships to production using the same API key the developer used for testing. The key was never scoped to specific collections, never restricted to read-only methods, and never given a dedicated rate limit. Six weeks later, the agent calls a payment endpoint it was never intended to reach — not because it was compromised, but because its credential permitted it.

The problem is architectural. When your access control model does not distinguish agent identity from application or human identity, the agent inherits whatever permissions the credential carried. That is usually too much. Agents make many more calls than humans and follow patterns their operators did not anticipate. A broad credential gives a broad blast radius.

This guide covers how Zerq handles AI agent access control at the gateway layer: the client and profile model, collection scoping, method restrictions, per-client rate limits, and the request log entries that let compliance teams see exactly what the agent called and when. There is a full step-by-step setup sequence and a go-live checklist you can apply to any agent rollout.

Why existing approaches fail

The most common improvised solution is a shared API key — one credential used by the mobile app, the CI pipeline, and the AI agent because it was already working and convenient. Revocation becomes impossible without breaking all three consumers simultaneously. Rate limits apply to the key, not to the agent specifically. The request logs carry the key ID but not an identity that distinguishes the agent from the human developer using the same key. A compliance question like "what did the order-processing agent call last quarter?" becomes unanswerable because the logs cannot make that distinction.

A second approach is to add a dedicated proxy or MCP server alongside the existing API gateway, specifically for agent traffic. This doubles the operational surface, creates two audit trails that need to be reconciled during an incident, and means agents bypass the rate limits and access controls already in place on the main gateway. Kong, Apigee, AWS API Gateway, and Azure API Management can all log gateway traffic — but none of them have a first-class model for AI agent identity. The log entry identifies a credential or an IP, not an agent type and deployment environment.

The underlying issue is not tooling. Agent identity, collection scope, method restrictions, and rate limits need to be first-class concepts in the access model from the start.

The client and profile model for AI agents

Zerq's security model is built on two concepts that apply identically to apps, services, and AI agents: a client (who is calling) and a profile (how they are allowed to call). Every request to the gateway carries X-Client-ID and X-Profile-ID headers. There is no anonymous traffic and no way to use the gateway without an identity that can be scoped and audited.

For AI agents, the model works like this:

  • One client per agent type and environment. A claims-processing agent in production is a different client than the same agent in staging. Different client IDs, different profiles, different rate limit policies.
  • Collections assigned per client. The client can only call endpoints within its assigned collections. When the agent calls list_collections via MCP, it receives exactly the collections it was assigned — nothing else is visible.
  • Method restrictions per profile. A profile can be restricted to GET only, blocking all writes at the gateway before the request reaches any backend.
  • Rate limit policies per client. A dedicated policy with a tighter request budget than human traffic, applied specifically to the agent's client.

The practical result is that the agent's blast radius is defined at configuration time, not discovered after an incident.

Setting up a least-privilege agent client

The following setup creates a production AI agent client with read-only access to two collections and a 100 req/min rate limit.

Step 1: create the agent client

  1. Go to Clients in the management UI sidebar.
  2. Click New Client.
  3. Set Name to a descriptive identity — for example, claims-processing-agent-prod.
  4. Under Collections, select only the collections the agent needs. In this example: claims-api-v2 and document-store-v1. Leave all other collections unchecked.
  5. Under Policy, select a policy with stricter limits than your human traffic. If one does not exist, create it first: go to Policies, click New Policy, and set Rate limit to 100 max requests per 1m interval and Quota to 50000 max requests per 30d. Agents typically need tighter burst limits than humans because they retry aggressively.
  6. Leave Developer Portal access disabled.
  7. Click Create.

Step 2: configure the profile

  1. Open the new client and go to Profiles.
  2. Click the default profile.
  3. Set Allowed Methods to GET only. Start read-only; add write methods only after reviewing which endpoints the agent explicitly needs to modify.
  4. Set IP Restrictions to the agent's egress IP or CIDR range if it runs in a fixed network location — for example, 10.8.0.0/24.
  5. Click Save.
  6. Copy the X-Client-ID, X-Profile-ID, and Authorization: Bearer token values from the profile detail page. These are shown in full only once.

Step 3: set agent credentials

In the agent's environment or configuration:

# Environment variables passed to the agent process
ZERQ_GATEWAY_URL=https://gateway.example.com
ZERQ_MCP_PATH=/mcp
ZERQ_CLIENT_ID=cl_abc123
ZERQ_PROFILE_ID=pr_xyz456
ZERQ_TOKEN=zerq_tok_xxxxxxxxxxxxxxxx

For MCP-compatible clients such as Claude or Cursor, the MCP server configuration block looks like this:

{
  "mcpServers": {
    "claims-gateway": {
      "url": "https://gateway.example.com/mcp",
      "headers": {
        "X-Client-ID": "cl_abc123",
        "X-Profile-ID": "pr_xyz456",
        "Authorization": "Bearer zerq_tok_xxxxxxxxxxxxxxxx"
      }
    }
  }
}

Step 4: run a go-live verification

Before sending production traffic, run one allowed call and one expected-deny call:

# Initialize an MCP session
curl -s -X POST https://gateway.example.com/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $ZERQ_TOKEN" \
  -H "X-Client-ID: $ZERQ_CLIENT_ID" \
  -H "X-Profile-ID: $ZERQ_PROFILE_ID" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"claims-agent","version":"1.0"}}}'
# Response header: Mcp-Session-Id: sess_abc789

# Allowed: list available tools (scoped to assigned collections)
curl -i https://gateway.example.com/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $ZERQ_TOKEN" \
  -H "X-Client-ID: $ZERQ_CLIENT_ID" \
  -H "X-Profile-ID: $ZERQ_PROFILE_ID" \
  -H "Mcp-Session-Id: sess_abc789" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
# Expect: 200 — returns list_collections, list_endpoints, endpoint_details, execute_endpoint

# Expected deny: write operation blocked by method restriction
curl -i -X POST https://gateway.example.com/claims/v2/claims/approve \
  -H "Authorization: Bearer $ZERQ_TOKEN" \
  -H "X-Client-ID: $ZERQ_CLIENT_ID" \
  -H "X-Profile-ID: $ZERQ_PROFILE_ID"
# Expect: 405 Method Not Allowed

Confirm both responses appear in the request logs with the correct X-Client-ID and X-Profile-ID on each entry before going to production.

What MCP discovery looks like with scoped access

The Gateway MCP exposes four tools: list_collections, list_endpoints, endpoint_details, and execute_endpoint. The collections and endpoints returned by the first two are scoped to what the client's profile can access. An agent assigned to claims-api-v2 and document-store-v1 cannot discover or call endpoints in payments-api-v3, because that collection is not assigned to its client. The discovery layer is access-controlled, not just execution.

The execute_endpoint tool inherits the same method restriction as any direct REST call. If the profile allows only GET, a POST via execute_endpoint returns 405. The MCP transport does not bypass gateway policy — it runs through the same enforcement path as a direct REST request. This is the practical benefit of running MCP on the same gateway as your REST traffic: access controls are defined once and enforced consistently, regardless of how the request arrives.

When the agent authenticates for an MCP session, the same headers apply as for any gateway client:

curl -X POST https://gateway.example.com/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $ZERQ_TOKEN" \
  -H "X-Client-ID: cl_abc123" \
  -H "X-Profile-ID: pr_xyz456" \
  -d '{
    "jsonrpc": "2.0",
    "method": "initialize",
    "id": 1,
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {},
      "clientInfo": { "name": "claims-processing-agent", "version": "1.0" }
    }
  }'

The response includes an Mcp-Session-Id header. All subsequent tools/list and tools/call requests include that header alongside the same X-Client-ID and X-Profile-ID.

What the request log contains for every agent call

Every call the agent makes produces a structured request log entry in the same log store as all other gateway traffic. For a GET /claims/v2/claims?status=pending call from the agent, the log entry contains:

FieldValue
request_idreq_8f3a9c4d...
timestamp2026-06-22T09:14:23.441Z
methodGET
path/claims/v2/claims
status_code200
latency_ms84
client_idcl_abc123
profile_idpr_xyz456
collectionclaims-api-v2
client_ip10.8.0.5

Filter by client_id in the observability logs view to retrieve every request the agent made in a given window, across all endpoints. Filter by status_code 403 or 405 to see every blocked attempt. A rising deny-rate on an agent client ID is the signal that the agent is attempting calls outside its defined scope — either a code change that needs review or an attempted scope expansion.

Denied calls are logged with the same identity fields as successful ones. The 403 or 405 entry carries the agent's client_id and profile_id, the path it attempted, and the timestamp. This is the record a compliance auditor needs when asking "did the agent ever try to reach endpoints it was not permitted to call?"

A checklist for AI agent access control

Before any AI agent goes to production:

  • Dedicated client per agent type and environment (dev/staging/prod are separate clients, not separate profiles on the same client)
  • Collections assigned: only the collections the agent needs, verified by reviewing the list_collections response scoped to that client
  • Method restrictions set: GET only as the starting point; write methods added only after explicit review of which endpoints require them
  • Rate limit policy attached: 100 req/min and 50,000 req per 30 days is a reasonable baseline for a read-heavy agent; adjust based on measured call frequency in staging
  • IP restrictions configured if the agent runs in a fixed network location
  • Token rotation schedule documented: short-lived tokens preferred; rotation should not require the agent to be redeployed
  • At least one deny test logged before rollout: a 403 or 405 confirmed in the request logs with the agent's client ID on the entry
  • First-week request logs reviewed: confirm the actual access pattern matches the intended scope

Revisit this checklist whenever the agent gets a code update that could change which endpoints it calls.

What this looks like in practice

A financial services team running a policy-evaluation agent had the classic access creep problem. The agent had been given a credential matching their internal services, which meant it had write access to every endpoint those services used. The agent was read-only by design, but nothing enforced that at the gateway layer.

After creating a dedicated Zerq client with collection access limited to two read-only API collections and a method restriction of GET only on the profile, the team ran a week of production traffic. The observability dashboard showed GET requests to four endpoints, consistent volume, no denies. When a code change in the agent accidentally added a POST /policies/v1/approve call, it returned 405 at the gateway, was logged with the agent's client_id, and was caught before it reached any backend. The runbook now includes the checklist above, verified before every production deployment.

Closing

Zerq's client and profile model gives AI agents the same access governance as every other consumer: scoped collection discovery, method restrictions, per-client rate limits, and a request log entry with agent identity on every call. See the full capabilities overview for additional controls available at the profile layer, including mTLS authentication and scheduled credential rotation. The access model is the same whether the consumer is a mobile app, a human operator, or an autonomous agent running overnight.


Zerq is an enterprise API gateway built for regulated industries — one platform for API management, AI agent access, compliance audit, and developer portal, running entirely in your own infrastructure. See how it works or request a demo to walk through your specific requirements.