A quarter after the AI gateway launched, the provider invoice is larger than the gateway's own usage numbers can explain. Someone traces the difference to personal keys on corporate cards, a team calling the provider directly from a batch job, and an evaluation pipeline that was moved off the gateway because, in the team lead's words, it kept blocking things and nobody could say why.
The usual response is to treat that as a compliance failure and enforce harder. Treat it as a product failure instead. Every developer who routed around the control paid a cost its designers did not see. Fix the cost and most of the bypass disappears; enforcement then handles a remainder that is much smaller and much easier to justify. Enforce first, and the same workloads move onto laptops and personal accounts, where nothing can see them. The checklist below runs in the order to do the work, and each item comes with a way to test it.
1. Interview the teams that left before changing anything
Bypass has a small number of causes, and each has a specific design fix. Interview three teams that left the governed path and sort what they tell you into this table.
| Friction | What the developer experiences | What they do instead | Design fix |
|---|---|---|---|
| Setup cost | A ticket and several days of waiting for an API key | Create a personal key in the provider console | Self-serve keys in minutes, scoped to a project and an environment |
| A different API | An internal SDK wrapper with its own request shape that lags the provider's features | Call the provider SDK directly | Keep the provider's API and change only the base URL |
| Latency everywhere | Every call, including trivial ones, waits on the heaviest check | Turn the control off in development, then forget to turn it back on | Cheap checks for low-consequence calls; spend time where consequence is |
| Opaque denials | A bare 403 that says the request was blocked | Retry, rephrase the prompt, or switch paths | A denial with the rule, the reason, a decision ID and a next step |
| No exception path | A feature blocked for weeks waiting on a review | Ship a shadow deployment on a separate key | Self-serve, time-limited exceptions with a named owner |
| Feature gaps | Streaming, tool calls or a new model work directly but not through the control | Use the direct path for the features that matter | Treat feature parity as a requirement and publish the gaps |
Two rows do most of the damage. Setup cost decides which path a new project starts on, and projects rarely move later. Opaque denials decide whether a team stays: a developer who hits three unexplained blocks in a week stops trusting the path, even if each block was correct. Test: every reason you heard maps to a row. A reason that does not is a friction you have not designed for yet.
2. Make a key take minutes, and keep provider credentials central
Issue keys self-serve per project or per agent, tagged with an environment such as development or production, so policy can treat a test run differently from production traffic. Hold provider credentials centrally and never hand them to developers. If the only working provider key lives behind the control, a personal key is not faster; it is a second account to set up and expense. Test: measure minutes from a new project's key request to its first successful call. It should be short enough that nobody looks for a shortcut.
3. Keep the provider's API and change only the base URL
Make the governed path a configuration change, not a code change. Most model SDKs already let you point the client at a different endpoint. The OpenAI Python library, for example, accepts a base_url argument and also reads the OPENAI_BASE_URL environment variable [3]. If your control speaks the same API, a team adopts it by setting one variable and swapping one key, and every feature the provider ships keeps working unless you deliberately block it.
Send tool traffic through the same front door. If agents reach MCP servers and internal tools through the endpoint they use for model calls, there is one thing to configure, one key to rotate and one place where policy applies. Put the governed base URL into every project template, internal starter kit and CI image, so a new service is on the governed path before anyone makes a choice. Test: streaming, tool calls and the newest model work through the control exactly as they do directly. Treat feature parity as a requirement and publish any gap.
4. Spend inspection where one call can do damage
A chat completion that summarizes an internal document and a tool call that deletes a production table are different risks. A control that charges both the same latency will be resented on the first and under-powered on the second. Sort calls by consequence, meaning the worst realistic outcome of one call judged by reversibility and reach, and spend inspection where it changes outcomes.
Reads and reversible changes take a short path with cheap checks and run; irreversible, outbound, financial and administrative calls take the full path and may ask a person or be blocked. Most developer traffic never waits on the expensive path.
Environment matters as much as action. In development, an agent deleting a row in a disposable test database should not wait for a person; in production, the same call should. Outbound messages, payments and administrative changes are the exception, because they reach the outside world or change who can do what. They should ask everywhere, including development, where a test message to a real customer is still a real message.
The fast path is not a weaker path. Structural checks, such as detecting real credentials or card numbers in a prompt, are cheap and run on every call; what the fast path skips is expensive work that only matters when a call can do damage. Test: run the same delete in development and production and confirm only production asks, then send a test message from development and confirm it asks too.
5. Make every denial say which rule, why, and what next
Most developers meet your control for the first time when it says no, so the denial is the control's user interface. A bare 403 that says request blocked is the worst one available. It does not say what is wrong, whether it is the developer's fault, or what to do next, so they rephrase, retry, and eventually find another path.
HTTP already has a standard for this. RFC 9457 defines problem details: a machine-readable error body with a type URI that identifies the problem, a short human-readable title, a detail field specific to this occurrence, and an instance URI for the occurrence itself, and it allows problem types to add their own extension members [1]. Use it, and add the fields developers need to act.
The bare denial on the left gives the developer nothing to do but retry or leave. The structured denial on the right names the rule and the reason, says whether a retry helps, and links to the exception request.
- Name the rule and the layer that decided, for example an organization-wide data rule versus a rule specific to this agent, so the developer knows whom to talk to.
- Say what was found and where, without echoing the sensitive value: a value that looks like a card number in the third message, not the number itself.
- Say whether a retry can succeed. Policy denials will not change on retry; rate limits will. RFC 6585 defines 429 Too Many Requests for rate limiting and allows a Retry-After header saying how long to wait [2]. Keep the two distinct so client retry logic does the right thing.
- Return a decision ID that support and security can look up, so a developer can report a false positive in one message.
- Link to the next step: the documentation for the rule, the exception request, or the approval that is now pending.
Test: show three developers a real denial and ask what they would do next. If the answer is retry or rephrase, the denial has failed.
6. Put every new rule through monitor mode and a replay
Before a new rule blocks anything, run it in monitor mode and replay it against recent traffic. Count which teams it would have blocked and read a sample. A rule that would have denied a hundred legitimate calls a day is not ready, however sound its threat model. Test: no rule reaches enforcement without a replay result attached to its change.
7. Let teams grant themselves exceptions that expire
Some denials will be correct in general and wrong for one team this week. If the only remedy is a security review with a two-week queue, the team will find another path. Make exceptions self-serve within limits:
- An exception is named, scoped to one agent or project, and carries a written reason and the requester's name.
- It has a default expiry measured in days and a hard maximum, so it cannot quietly become permanent.
- Low-consequence exceptions take effect immediately and are reviewed afterward; exceptions that touch outbound, financial or administrative actions need an approver first.
- The agent's owner and the security channel are notified automatically, and every decision made under the exception records that the exception allowed it.
- An exception renewed repeatedly is evidence that the rule is wrong, not that the team is careless. Review renewals monthly.
8. Measure bypass from outside the control
The headline metric is governed share: the fraction of AI usage that goes through the control. The difficulty is the denominator, because bypassed traffic is by definition the traffic you do not see. Build it from several sources. AI provider organizations list keys and usage per key. Expense systems show subscriptions bought on cards. Egress logs and DNS show which hosts call provider API domains. Identity providers show OAuth grants to AI applications. Repository scanning finds committed keys; GitHub secret scanning, for example, lists both OpenAI and Anthropic API keys among its supported patterns [4].
| Metric | How to compute it | Healthy direction |
|---|---|---|
| Governed share of spend | Spend through the control divided by total provider spend across all organizations and expensed accounts | Rising toward nearly all |
| Keys outside the control | Provider keys not issued through the control, from provider org listings and repository scans | Falling, with each one assigned an owner |
| Time to first governed call | Minutes from a new project's key request to its first successful call | Short enough that nobody looks for a shortcut |
| Denials overturned | Share of denials later reversed as false positives or granted an exception | Low and falling; a spike means a bad rule |
| Time from denial to resolution | Minutes from a denial to a successful call or a granted exception | Falling |
| Exception renewals | Exceptions renewed more than once for the same rule | Each renewal reviewed as a rule defect |
When you find a bypass, contact the team before you revoke anything. Ask what they were trying to do and what stopped them on the governed path. Most answers map to a row in the friction table, and fixing that row helps every team rather than one.
Items 2 to 7 in one gateway
With Intertrace, teams change their base URL and use a runtime key, and the same front door covers model calls, MCP traffic and agent-to-agent messages. The gateway assigns each call a consequence tier from the tool name and its arguments, so reads and reversible changes run while outbound messages, payments and administrative changes ask a person. Each decision records its rule and layer. Exceptions are named, scoped to one agent and expire after seven days by default and ninety at most, and a proposed rule can be replayed against the last thirty days of real decisions before it blocks anything. The interviews and the side doors stay yours.
9. Close the side doors last
Hollin, a fictional online retailer with forty product teams, started where many companies do (numbers illustrative): keys took four days, a custom SDK lacked streaming, denials were generic, twenty-three provider keys had never been issued through the gateway, and a little over half of AI spend went through it. The platform team fixed the friction first, then gave every owner of an outside key thirty days' notice and a migration offer before revoking; by the end of the quarter governed share was above ninety percent, time to first call was under ten minutes, and the most common exception had exposed a rule that flagged internal order IDs as card numbers.
That is the right order. Once the governed path is the easier one, restrict egress to provider API domains from production networks, revoke provider keys that were not issued through the control, turn on push protection for provider keys in your repositories, and decline expensed AI subscriptions that duplicate a governed service. Announce each step with a date and a migration offer. Done last, each one removes an inferior option instead of the only one that worked.