To control what an AI agent can access, give it its own identity instead of a shared key, grant it only the specific API scopes its task needs, and write policies that permit those requests while forbidding everything else. The authorization server checks the rules on every token request, so the agent only ever gets the access you approved.
That is the whole idea in one paragraph, and the rest of this guide turns it into concrete steps. Controlling agent access is harder than controlling a user's, because an agent acts on its own, at machine speed, and often for someone else. A static API key gives it everything at once with no record. Policy-based authorization replaces that with explicit, readable rules. This article is part of our AI agent identity series, and it builds on what Cedar policies are.
Do not let an agent reuse a human's API key or a secret shared across services. Register the agent as its own client so its requests are attributable to it and its access can be scoped and revoked independently. Mark its client type as agent, and use a credential it can prove rather than a copyable string: a machine-to-machine token, or a certificate through mutual TLS for stronger assurance.
The payoff shows up later. When the agent has its own identity, every later step (scoping, policy, audit) has something specific to attach to. When it borrows a human's key, none of that is possible.
List the exact APIs the agent must call and the narrowest scopes that let it do its job. An agent that summarizes invoices needs billing.read, not billing.write, and certainly not access to unrelated APIs. Scopes are the coarse boundary, the first cut at least privilege, so set them tightly before you add finer rules.
Write this down as a short list per agent. Most over-permissioned agents got that way because no one ever enumerated what the agent actually needed.
In MonoCloud you set this on the Access Rules tab of the API resource. A basic rule lets you pick the agent client and select the allowed scopes through structured fields. When you need conditions, write an advanced rule in Cedar. The rule is evaluated on every token request, and if no policy permits the request, no token is issued.
Here is a Cedar policy that permits an agent to receive a billing token only when it requests the right scopes:
permit (
principal == Client::"support-agent",
action,
resource == Api::"https://api.example.com/billing"
) when {
principal.client_type == ClientType::"agent" &&
context.requested_api_scopes.contains(Scope::"https://api.example.com/billing#billing.read") &&
context.requested_scopes.contains(Scope::"openid")
};
Cedar does not enforce this itself. It evaluates the rule and returns a decision, and MonoCloud's authorization server acts on it by issuing or refusing the token. The model is default-deny, so anything you have not explicitly permitted is already denied.
For anything risky, narrow the rule with the request context: the time of day, the source IP, or the country of origin. This is where policy beats a flat scope list, because none of these fit in a scope. The policy below permits a request only from the United States, between 9am and 11pm, and from an approved IP address:
permit (principal, action, resource)
when {
context.location.country == Country::"US" &&
context.time_of_day_seconds >= 32400 &&
context.time_of_day_seconds <= 82800 &&
[ip("1.1.1.1"), ip("1.1.1.2"), ip("1.1.1.3")].contains(context.location.ip_address)
};
Apply conditions where the risk warrants them, not everywhere, so you do not make the common path brittle.
Some actions an agent should never take, full stop. Express those as a forbid policy. Because forbid always wins over permit, a guardrail like this cannot be undone by a broad allow rule elsewhere, which makes it reliable. Swapping permit for forbid on a statement turns the same shape into a block.
This is how you encode hard limits, for example forbidding any write scope for an agent that is only ever meant to read.
A policy that permits a request can also shape the token it issues, through the rule's Tokens tab. For a riskier context, issue a shorter-lived token, switch to an opaque reference token that can be revoked centrally, or bind the token to the user's session so it ends at logout. The override applies only when that rule matches.
The point is to keep the blast radius of any single agent token proportional to the risk that created it. A short-lived, revocable token that leaks is a far smaller problem than a long-lived bearer token with broad scope.
Turn on audit logging so every token issued to the agent and every access decision is recorded. You cannot control what you cannot see, and an autonomous caller without records is a problem during any incident or review. Review the logs for tokens that should not have been issued and tighten the policy when you find them.
Observability is the last layer of agent identity, and it is what lets you trust the first six. We cover it further in the agent identity guide.
Say you run a support agent that should read billing data but never change it, and only from your own servers. You give it a dedicated client with client_type of agent, grant it just billing.read, write a permit policy requiring that scope plus an approved IP range, add a forbid policy for any write scope, set its tokens to a short lifetime, and turn on audit logs. The agent now does its job, cannot touch anything else, and leaves a trail. If its token ever leaks, it expires fast and only reads billing from your network anyway.
The recurring ones are easy to name. Sharing a human's key with an agent, which destroys attribution and over-grants access. Handing an agent broad scopes because it was faster than enumerating what it needed. Issuing long-lived bearer tokens that stay valid long after a task ends. And skipping audit logs, so no one notices a misconfigured agent until it has already made bad calls. Each is avoided by the steps above.
To put this into practice, read what Cedar policies are for the policy language itself, or start building on MonoCloud for free and write your first agent access rule in a few minutes.