Stop an AI agent from changing the wrong customer's data
The ticket is right. The customer is wrong.
Your support agent is handling customer A. It reads a pasted ticket link for customer B and updates that ticket's priority. The API accepts the request because the agent has a valid app key. You find the mistake later in a log. The request was authenticated, but it was outside the job you gave the agent.
An API key identifies an app. It does not describe which customer that app is helping in this session. A rate limit can slow the mistake down. It cannot choose the right customer. Logs help you investigate after a write. They do not stop that write. A reminder in the prompt still leaves the agent able to make the call.
Give this session a smaller permission
Create a Gate project and register an app with its allowed routes. Keep the admin key on your backend. For each job, your backend grants a run. Bind the customer from your authenticated application context, rather than trusting a customer name chosen by the agent. The run is an additional check on top of the app's route list.
curl -X POST https://gate.odilelabs.com/api/projects/PROJECT/runs \
-H "Authorization: Bearer $ADMIN" -H 'content-type: application/json' \
-d '{"app":"APP_ID","scope":{"customer":"A"},"ttl_seconds":600,
"actions":[{"rule":"PATCH /customers/{customer}/tickets/:id","max":2},
"GET /customers/{customer}/tickets/:id"]}'
The response contains a token and a run description. Pass only the app key, gateway URL and run token to the agent. The literal customer value replaces the named placeholder. The ticket identifier remains variable, so this run can update A's tickets within its limit.
import { Gate } from 'https://gate.odilelabs.com/sdk/gate.mjs';
const gate = new Gate({ gateway, appKey, runToken: token });
const result = await gate.request('PATCH', '/customers/A/tickets/1', {
body: { priority: 'high' }
});
console.log(result.status, result.outcome, result.idempotencyKey);
console.log(await gate.allowed());
Check a refusal before connecting your API
In Demo Desk, the allowed update returns 200. Changing the path to customer B returns 403 with action_not_in_run. Deleting a ticket or exporting the desk also fails when those actions are absent from the run, even if the app's route list allows them. Expiry and revocation end the permission. The SDK supplies an idempotency key for the write; keep it for an explicit retry.
Keep the checks behind Gate
Gate checks method, path and query, not body contents. A body that names a different customer still needs validation by your API. Gate does not replace ownership checks or row-level security. It only protects calls that go through Gate. Give the agent no direct upstream credentials, and require runs for the app's writes if every write must be scoped. The run does not automatically become mandatory for calls without its token.
For background on limiting agent permissions, see OWASP's excessive agency guidance. The Gate Actions reference explains run requirements and the dashboard.
Try it
Try it: run the free sandbox example before pointing Gate at your own API.
curl -s https://gate.odilelabs.com/actions-quickstart.sh | sh