Last Updated: Jun 16, 2026

Fine-Grained API Authorization for AI Agents: Scopes, Time, and IP Conditions

Fine-grained API authorization for AI agents goes beyond scopes, adding time, IP, country, and client-type conditions. Learn how to layer them with Cedar policies.

Fine-grained API authorization for AI agents controls not just which API and scope an agent can use, but the exact conditions under which it can use them: the time of day, the source network, the country of origin, and whether the caller is an agent at all. Scopes set the coarse boundary, and context conditions make it precise.

The gap between coarse and fine-grained authorization is where most real access requirements live. "This agent can read billing" is a scope. "This agent can read billing, but only from our servers, during business hours, in the US" is fine-grained authorization, and a scope list cannot express it. This guide covers the request attributes you can authorize on and how to layer them. It builds on what Cedar policies are and the broader how to control what an AI agent can access.

Coarse versus fine-grained authorization

Coarse-grained authorization answers which API and which scope. It is the same decision every time: the agent either has billing.read or it does not. Scopes are essential and you should always set them tightly, but they are static. They carry no awareness of when, where, or how a request is made.

Fine-grained authorization adds that awareness. It evaluates the full context of each request and can permit or refuse based on conditions a scope cannot capture. The two work together: scopes are the baseline boundary, and conditions narrow it for sensitive cases. You do not replace one with the other.

The request attributes you can authorize on

At the moment an agent requests a token, the authorization server knows a lot about the request, and any of it can feed a policy decision. The attributes that matter most for agents are these:

The client type, so you can treat an agent differently from a regular application. The requested scopes and API scopes, the coarse permissions being asked for. The requested audiences and the resource's own audience, identifying which API the token is for. The country the request resolves to, and the source IP address. And the time of day. A fine-grained policy composes any of these into a single readable rule.

Scope as the baseline

Start every agent with least-privilege scopes. An agent that reads invoices gets a read scope and nothing else. This is the first and cheapest cut, and it does most of the work. In a Cedar policy the scope check looks like this:

context.requested_api_scopes.contains(Scope::"https://api.example.com/billing#billing.read")

Everything below is a condition you add on top of a tight scope, not a replacement for it.

Time-of-day conditions

Restrict sensitive operations to the hours they should happen. MonoCloud expresses time as time_of_day_seconds, the number of seconds since midnight, so 9am is 32400 and 11pm is 82800. A window looks like this:

context.time_of_day_seconds >= 32400 && context.time_of_day_seconds <= 82800

This is useful for agents that should only act during business hours, or batch agents restricted to off-peak windows.

IP and network conditions

Limit an agent to the networks it should run from, such as your own servers or a known egress range. You check the source IP against an allowlist:

[ip("1.1.1.1"), ip("1.1.1.2"), ip("1.1.1.3")].contains(context.location.ip_address)

If an agent only ever calls from your infrastructure, an IP condition turns a leaked credential used from anywhere else into a denied request.

Country and client-type conditions

For data-residency or compliance needs, restrict by the country a request resolves to with context.location.country == Country::"US". And to make a rule apply only to agents rather than ordinary apps, gate on principal.client_type == ClientType::"agent". The client-type check is what lets you write policies specifically for autonomous callers without affecting your normal user-facing applications.

Combining conditions into one policy

The real power is composing these. The policy below permits the support agent to read billing only when it is an agent, asks for the read scope, comes from the US, runs during business hours, and originates from an approved IP:

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.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")].contains(context.location.ip_address) };

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. Because the model is default-deny, any request that fails even one condition simply gets no token.

When to keep it simple

Fine-grained does not mean conditioning everything. Each condition you add is something that can legitimately fail and block a valid request, so apply conditions where the risk warrants it and leave low-risk paths on scopes alone. A read-only reporting agent might need only a tight scope. An agent that can move money deserves time, network, and country conditions plus a short-lived token. Match the control to the consequence.

In MonoCloud, the coarse path is a basic rule where you pick the agent and select scopes through structured fields, and the fine-grained path is an advanced rule written in Cedar. Both are evaluated on every token request. To put this to work, read how to control what an AI agent can access for the full setup, or start building on MonoCloud for free.