API Access Control for AI Agents
An OAuth scope describes a surface of what's possible. An agent needs a per-operation allow list enforced at execution, not a scope it can use to argue its way into anything the scope covers.
Key takeaways
- -An OAuth scope such as repo or chat:write describes everything that category of operation could ever do. It does not say which specific operations an agent is actually supposed to call.
- -API access control for an agent means a per-operation allow list: the agent can run exactly the methods explicitly added, and nothing else is reachable, regardless of what its scope would technically permit.
- -Scopes and roles still matter. They set the outer boundary a human or security team signs off on. The allow list is the inner boundary enforced on every call the agent actually makes.
- -Access control answers whether a call can run at all. Governance, covered separately, also covers what happens once it's allowed to run: argument checks, approval, retries, and audit.
- -In Swytchcode, access control is a specific command, swy add method, applied one canonical method at a time, so a provider connection never implicitly grants everything that provider's API can do.
API access control for an AI agent is a per-operation allow list enforced every time the agent tries to call something, not an OAuth scope checked once at connection time. A scope describes a surface: everything a category of operation could ever do. An agent needs a narrower boundary than that, because the question is never whether the agent's token could technically perform an action. It is whether that specific action is one the agent is actually supposed to be able to run.
Why a scope isn't access control
Grant a GitHub token the repo scope and it can read issues, but it can also delete branches, change repository settings, and push directly to main. Grant a Slack app chat:write and it can post a status update, or it can post to any channel it's a member of, at any volume, at any time. The scope is the same either way. For a human developer with a token in their own terminal, the gap between what a scope allows and what they actually intend to do is closed by judgment. An agent does not have judgment in that sense. It has whatever the scope technically permits, plus whatever its own reasoning decides to try.
What per-operation access control actually looks like
Access control at the operation level means naming the exact methods an agent can call, not the provider it can talk to. Connecting Stripe is an authentication event. Allowing stripe.refund.create is a separate, explicit decision, and an agent that has only been allowed stripe.charge.get1 cannot issue a refund even though the same Stripe credential is sitting right there, authenticated and ready.
- Default deny. Nothing is reachable until it's explicitly added. This is the opposite of how OAuth scopes default, where everything inside the granted scope is reachable until something actively narrows it.
- Canonical, specific methods. gmail.user.messages.get1, not "Gmail access." The allow list names the operation, not the application.
- Enforced on every call, not once. A scope is checked at the OAuth handshake. An allow list is checked each time the agent tries to run something, for the life of the agent, including calls it attempts a thousand runs after the connection was first made.
- Reviewable like code. The list of allowed methods lives in a file that goes through the same review a pull request does, rather than a setting buried in an admin console somewhere a security team has to go looking for it.
Scopes and roles still matter, they just aren't the whole answer
None of this replaces OAuth scopes, SSO, or RBAC. Those set the outer boundary: which provider the agent can authenticate to at all, and under which human or service identity. Per-operation access control is the inner boundary, enforced inside that outer one. A security review usually wants to see both: the scope that was granted, and the specific list of operations actually allowed within it.
Three access control models compared
| Model | What it checks | When it's checked | What it misses |
|---|---|---|---|
| OAuth scope only | Which category of operation the token can perform | Once, at authentication | Everything inside the scope is reachable for the life of the token |
| Role-based access (RBAC) | Which role the calling identity holds | At authentication, sometimes cached | Doesn't see the specific call or its arguments once the role is assigned |
| Per-operation allow list | The exact canonical method being called | On every call, at execution | Nothing, by design, since it's a default-deny list rather than a default-allow scope |
What this looks like set up with Swytchcode
Connecting a provider and allowing a method are two separate steps. Finding and allowing a method one at a time looks like this:
# Find the exact method before allowing anything
swy discover "issue a stripe refund" --project stripe
# Allow exactly that method, nothing else in the Stripe API
swy add method stripe.refund.create
# Confirm what's actually reachable right now
swy list methods stripe --allowedNothing else in the Stripe API becomes callable just because the credential is connected. An agent that tries stripe.customer.delete without that method explicitly added gets blocked before the call ever leaves, not after.
FAQ
Isn't a narrow OAuth scope enough if we only request the minimum we need?
Minimal scopes help, but most providers don't offer a scope granular enough to separate "read a charge" from "issue a refund" or "post to one channel" from "post to any channel." Per-operation access control fills that gap regardless of how fine the provider's own scopes get.
Does access control replace credential security?
No. Access control decides whether a call can run. Keeping the credential itself out of the model's context is a separate control, covered in the credential vault guide linked below. Both matter, and neither substitutes for the other.
What happens if an agent tries a method that wasn't allowed?
The call is blocked before it reaches the provider. The method is not reachable, so there's nothing to deny at the API level, the agent's attempt simply fails locally with no request sent.
Related reading
- API governance tools for AI agents: swytchcode.com/content/api-governance-tools-ai-agents
- What a credential vault needs to do for AI agents: swytchcode.com/content/credential-vault-ai-agents
- Your API, your OAuth app, your rules: swytchcode.com/blogs/your-api-your-oauth-app-your-rules
More content
How to Log Every API Call an AI Agent Makes
Log the call where it is sent, not where it is described. Record the operation, the actor, the idempotency key, the policy decision and the outcome. Never record the credential. If the agent writes its own log, the calls that were blocked quietly disappear.
How to Connect GitHub to an AI Agent (2026)
Most of what you want an agent to do on GitHub needs no push access at all. Working that out first is the difference between a useful teammate and a token that can rewrite your main branch.
Composio Pricing Compared with Nango, Arcade, Pipedream, and Swytchcode
Composio is free for 100,000 tool calls a month, then $29. Nango is $50 pay-as-you-go. Arcade Team is $25 plus a cent a call. Pipedream Startup is $99 billed annually. Swytchcode is $149 for a million. The headline prices are the least useful part, so here is the arithmetic.
