From sandbox to production API access: a promotion path for AI agents
How to promote an AI agent from sandbox to production API access: staged profiles, evidence from gateway logs, and an audit trail for every access grant.
- ai-agents
- mcp
- governance
- access-control
- audit
No sane platform team deploys a new service straight to production. Yet that is exactly how most AI agents get their API credentials: someone builds an agent on a Tuesday, it works in a demo on Wednesday, and by Thursday it holds a token that reaches production data. There is no intermediate state. The agent goes from not existing to holding production API access in one step, because the infrastructure has no way to express anything in between.
Human consumers never get treated this way. A new B2B partner starts in a sandbox, proves their integration, and gets promoted to production keys through a process someone signs off on. A new employee starts with limited permissions and gains more as trust builds. Agents skip all of it, not because anyone decided they should, but because the agent was wired up through a hand-rolled MCP server or a borrowed service account, and that path has exactly one setting: connected.
This post describes a concrete promotion path for AI agent production API access, built on gateway primitives: a sandbox profile the agent starts in, an evidence gate based on what the gateway actually logged, and a promotion step that is itself recorded in the audit trail. Every screen, field, and log structure below is real Zerq configuration.
Why "we tested it in staging" is not a promotion process
Most teams, asked how an agent earned production access, point at a staging environment. The agent ran against staging for a while, nothing broke, so it got production credentials. That answer has three holes.
First, testing the agent's code is not the same as constraining the agent's access. A staging run tells you what the agent did on the days you watched it. It does not limit what the agent can do once it holds a production token. Agents are non-deterministic consumers: the same agent that made polite GET requests for two weeks can issue a burst of DELETE calls after a prompt change, a model upgrade, or an injected instruction in retrieved content. A promotion process has to end in enforced constraints, not in a green checkmark from last month.
Second, in the common architectures there is nothing to promote. When agents reach APIs through a standalone MCP server holding a shared service credential, "sandbox" and "production" are properties of which backend URL got pasted into a config file. Kong and AWS API Gateway can model per-consumer keys, but the promotion from one tier to another is a redeploy or a ticket to whoever owns the gateway config, and the approval lives in the ticket system while the enforcement lives somewhere else. Nothing connects the decision to the runtime state, so six months later nobody can show an auditor why this agent has the access it has.
Third, the evidence is missing. To decide an agent is ready for production you need its actual request history: what it discovered, what it called, what got denied, whether it ever hit a rate limit. Backend logs record traffic from the MCP server's IP, not from the agent as a distinct caller, so the promotion decision gets made on anecdote.
A promotion path for AI agent production API access
Zerq treats an agent as a first-class API consumer with its own client identity, and treats sandbox and production as profiles on that client: separate runtime access contracts, each with its own credential, allowed methods, IP restrictions, and active toggle. Because agents connect through Gateway MCP using the same enforcement path as REST consumers, every call the agent makes lands in the same request log, tagged with its client and profile IDs. Promotion becomes a small, auditable configuration change instead of a leap of faith.
Here is the path end to end.
Step 1: create the agent's client with a sandbox profile
- In the management UI, open Clients & Profiles → Clients and click New client. Name it for the agent, not the team:
ai-claims-triage, status Enabled. One agent type, one client. Never share a client between agents, or containment and evidence both blur. - Open the client, click Add Profile, and create the starting profile:
- Name:
sandbox - Active: on
- Auth Type: Token. The bearer token is generated on save and shown once; copy it into your secrets manager immediately.
- Allowed Methods:
GETonly. Anything else returns405 Method Not Allowedat the gateway, whatever the agent decides to try. - IP Restrictions: the CIDR range where the agent infrastructure runs, for example
10.40.8.0/24. Requests from any other address are rejected with403before authentication is even attempted, so a leaked sandbox token replayed from elsewhere gets nowhere.
- Name:
- Attach a conservative policy to the client: Policies → New Policy, rate limit
100requests per1minterval, quota10,000per1d. Note that a policy applies to all of the client's active profiles combined, which matters at promotion time below.
Step 2: scope the sandbox profile to sandbox collections
Grant the client access only to the collections that front sandbox backends. Open each sandbox collection, for example claims-api-sandbox, and add ai-claims-triage under the Access tab. Do not grant the production collections yet.
This scoping controls discovery as well as execution. When the agent calls the list_collections MCP tool, it sees only collections its client has been granted; production APIs are not merely blocked, they are invisible. An agent cannot be talked into calling an endpoint it cannot discover and has no route to.
Step 3: connect the agent to the sandbox profile
The agent's MCP configuration carries the identity that makes the whole scheme enforceable. For Claude Desktop, Cursor, or any Streamable HTTP MCP client:
{
"mcpServers": {
"zerq-claims-sandbox": {
"type": "streamableHttp",
"url": "https://api.example.com/mcp",
"headers": {
"Authorization": "Bearer <sandbox-token>",
"X-Client-ID": "ai-claims-triage",
"X-Profile-ID": "sandbox"
}
}
}
}
The X-Client-ID header names the agent; X-Profile-ID pins it to the sandbox contract. Every MCP tool invocation, from list_endpoints to execute_endpoint, is authenticated, method-checked, IP-checked, rate-limited, and logged under this identity.
Step 4: let the agent run, and let the gateway collect the evidence
Now the agent does its job against sandbox data for a defined burn-in period, two to four weeks is typical, while the gateway records everything. This is where the promotion decision gets its facts. Open Logs → Request Logs and filter by Client ID = ai-claims-triage; the request log holds every call with method, path, matched collection and proxy, status code, latency, full request and response bodies, and the profile ID that made it.
The denials are as informative as the successes. Suppose the agent attempts a write during burn-in:
// tools/call → execute_endpoint
{
"name": "execute_endpoint",
"arguments": {
"collectionId": "claims-api-sandbox",
"proxyId": "update-claim-status",
"pathParams": { "claimId": "CLM-20441" },
"body": { "status": "approved" }
}
}
The gateway rejects it with 405 Method Not Allowed because the sandbox profile permits only GET, and the attempt is logged. That log line is not a failure of the burn-in; it is the burn-in working. It tells you precisely which additional method the agent's task genuinely requires, so the production grant can be scoped to reality instead of to a guess. A 429 with body "error": "rate_limit_exceeded" tells you the same thing about throughput: the agent's real request rate, not the number someone estimated in a design doc.
Step 5: pass the promotion gate
Before anyone creates a production profile, the request logs have to answer a fixed set of questions. This is the gate:
- Did the agent stay inside its intended endpoints? Filter the burn-in period by client ID and review the distinct paths called. Anything surprising is a conversation with the agent's owners, not a footnote.
- What was denied, and why? Review every
403and405. Each one is either a scope the production profile legitimately needs, or evidence the agent attempts things it should not, which is exactly what you want to learn before production. - What throughput does it actually need? Take the peak sustained rate from the logs and set the production policy from it, with headroom, rather than copying a tier name.
- Did error handling behave? Filter status
4xxand5xxand confirm the agent backed off rather than retry-storming. - Who owns it? A named human owner, an on-call route, and a documented purpose. No owner, no promotion.
If any answer fails, the agent stays in sandbox and nothing about that is an emergency, because the sandbox is a fully working environment, not a waiting room.
Step 6: the promotion itself
Promotion is three small configuration changes, each one recorded:
- On the client
ai-claims-triage, click Add Profile and createproduction. Give it a stronger auth posture than the sandbox: mTLS if your agent infrastructure can hold a client certificate, or a token with a rotation expiry set in hours so the credential is short-lived by construction. Set Allowed Methods to exactly what the burn-in evidence justified, for exampleGET, POSTand nothing else, and pin IP Restrictions to the production agent subnet. - Grant the client access to the production collections it needs, and only those, via each collection's Access tab.
- Update the client's policy to the production numbers derived in the gate. Because the policy applies across the client's active profiles combined, this swap is the moment the client's throughput budget changes, which is another reason the gate reviews real rates first.
Switch the agent's MCP config to the new credential and X-Profile-ID: production. The profile detail page shows the token state at a glance, with a green Valid badge, an amber Expiring Soon within 24 hours of expiry, and a red Expired, so short-lived production credentials do not become silent outages.
Keep the sandbox profile. It costs nothing, and every future change to the agent, a new model, a new prompt, a new task, goes through the same burn-in loop against sandbox collections before its production scope widens. Promotion is not a one-time event; it is the standing process by which this agent's access ever changes. And if production behaviour ever looks wrong, toggling the production profile to inactive stops it from the next request while leaving the sandbox path intact for investigation.
What the audit trail records
Every step above is an administrative action, and each one lands in the audit log as structured JSON in your own MongoDB, in your own environment. The production profile creation, for instance, is recorded with:
{
"timestamp": "2026-09-07T09:41:22Z",
"actorId": "u-priya.n", // who made the grant, from the OIDC token
"actorType": "user", // user, service, or system
"action": "CREATE", // CREATE, UPDATE, DELETE, or READ
"resourceType": "profile", // what kind of thing changed
"resourceId": "prof-8c21f0", // the production profile's ID
"httpMethod": "POST", // the management API call behind the click
"requestBody": { "name": "production", "authType": "mtls", "allowedMethods": ["GET", "POST"] },
"responseStatus": 201,
"ipAddress": "10.2.0.14",
"requestId": "req-77b2..."
}
For a compliance team this closes the loop that ticket-based promotion never closes. The question "who gave this agent production access, when, and with what scope" is answered by filtering the audit log for Resource type profile and Action CREATE, and the request body shows the exact scope granted. The same trail captures the policy update and every collection access grant. Reviewers use the dedicated Auditor role, which can read everything and change nothing, so the people who verify promotions are structurally separated from the people who perform them. See how the pieces fit in the platform architecture: configuration, request logs, and audit data all stay inside your boundary.
What this looks like in practice
An insurer built a claims-triage agent to pre-sort incoming claims by reading claim records and policy data. The first version ran through a locally spun-up MCP server holding a service account token with full API access; it worked, and that was precisely the problem, because nobody could say what "worked" excluded. Security froze the rollout.
The platform team moved the agent behind Zerq. Client ai-claims-triage, sandbox profile with GET only, access to the claims-api-sandbox collection, 100 requests per minute. Three weeks of burn-in produced a clean path profile, one recurring 405 on a claim-status update endpoint, and a peak sustained rate near 40 requests per minute. The gate review concluded the agent legitimately needed one write endpoint, so the production profile got GET, POST, mTLS against the insurer's internal CA, access to two production collections, and a policy of 100 requests per minute with a daily quota.
The rollout that security had frozen was approved in a week. Not because the agent changed, but because the answer to "what can it do, and how do we know" changed from a shrug to a profile page and a log query. When the team later swapped the underlying model, the new version went back through the same sandbox loop and was promoted again in days, with the whole history sitting in the audit trail for the next regulator conversation.
What you get
A promotion path instead of a binary switch: agents start in sandbox by default and earn production scope. Enforcement instead of intention: methods, IPs, rate limits, and collection visibility are gateway-enforced contracts, not code review comments. Evidence instead of anecdote: the burn-in record is the gateway's own request log, per agent, per profile. An audit trail instead of a ticket archive: every grant, scope change, and policy update is recorded with actor, timestamp, and exact payload. And one platform doing all of it for REST consumers and AI agents alike, running entirely in your infrastructure.
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.