Why AI agents need permission passports, not API keys
API keys tell you who is calling. They don't tell you what that caller is allowed to do in this moment, for this action, against this resource. For autonomous agents, that distinction matters.
An API key is an authentication credential. It answers one question: who is calling? It does not answer whether the caller is permitted to take a specific action right now.
For a human developer making an API call, that's usually enough. The developer has context, can read error messages, and can be held accountable. An autonomous agent operating across dozens of services without supervision is a different category of caller entirely.
What an API key actually proves
An API key proves possession of a credential. If the key is valid, the service accepts the request. The key doesn't carry scope — it doesn't say this caller may purchase but not delete, or may spend up to $500 but not $5,000.
OAuth scopes are better, but they operate at the integration level, not the action level. A scope like commerce:write grants purchase capability for the entire integration — not just for agent Ollie, not just for this week, not just for amounts under $200.
As agents become the primary consumer of APIs, integration-level grants are too coarse. A single OAuth token shared across an agent's actions cannot express the difference between an agent being permitted to browse and an agent being permitted to buy.
The granularity problem
Consider an agent that does the following across a day:
- Reads your calendar to schedule a meeting.
- Books a flight based on the meeting location.
- Charges a corporate card for $1,200.
- Sends a contract on your behalf.
- Grants access to a shared drive folder.
Each of these actions has a different risk profile. You might trust the agent to read your calendar and book travel, but want to review contract sends and access grants before they happen. An API key that authorizes the agent doesn't capture any of that nuance.
What a permission passport adds
A permission passport is a set of scoped grants attached to a specific agent identity. Each scope defines what actions are allowed, what resources are in scope, what actions are explicitly blocked, and when the grant expires.
{
"resource": "commerce",
"allowedActions": ["browse", "add_to_cart"],
"blockedActions": ["purchase", "refund"],
"requiresApproval": false,
"expiresAt": "2026-05-30T00:00:00Z"
}The passport is readable by the agent before it acts — it can check what it's allowed to do without needing to attempt the action and get denied. And it's enforced at the decision boundary — every action is verified against the active passport before it runs.
The verification step
The verify call is what makes the passport meaningful. Without it, the passport is just documentation. With it, the passport is a permission boundary.
import { BehalfID } from "@behalfid/sdk";
const behalf = new BehalfID({ apiKey: process.env.BEHALFID_API_KEY });
const decision = await behalf.verify({
agentId: "agent_ollie",
action: "purchase",
vendor: "coachella.com",
amount: 742
});
if (!decision.allowed) {
throw new Error(decision.reason);
}
await charge(vendor, amount);The agent's API key authenticates the call. BehalfID checks the passport, evaluates the scope against the requested action, and returns a decision. The executor only runs after an explicit allow. The decision is logged with a stable requestId for audit.
This is the meaningful difference between API key authorization and passport-based permission enforcement: the scope is checked per action, per agent, at runtime — not once at integration setup and then forgotten.