AI agents

Tier Claims in the Token Versus a Lookup per Call

Antonio Evans9 min read

A wide flat vector scene split into two parallel lanes by a thin vertical divider.
Key points

Key points

  1. 1A tier claim inside the caller's token is only as trustworthy as the token's remaining lifetime, so bound exp to the window you accept for a stale downgrade.
  2. 2A fresh lookup on every tool call removes revocation lag but adds a per-call dependency you must budget for in latency and availability.
  3. 3Every refusal should log the token's issue time, the policy decision, the policy version and a correlation ID so an incident review can name the evidence.
  4. 4The pattern most teams settle on is a short per-caller cache with an explicit invalidation push on tier change, plus a fresh check on high-impact tools.

What the two designs actually decide

The question looks like a performance argument and is really an evidence argument. Both designs answer "may this caller run this tool right now?" The difference is what backs the answer when someone asks why it was allowed, or why it was not.

In the token-claim design, the permission travels with the caller. The authorization server mints a token carrying a tier (or role, or plan) and the API trusts the signature. This is the classic claim-as-assertion approach: a claim is a trusted statement about a user that an API can rely on without a round trip, and the usual advice is to design scopes and claims together so the claim set matches the API's authorization rules (Curity on claims). The cost is baked in: the token is a snapshot, and the snapshot ages.

In the lookup design, the permission lives in a policy engine or database and is re-read at decision time. An agent framework can consult a policy before every tool dispatch and map the result to allow, deny or requires-approval, with the policy input carrying the tool name, the arguments and even the message history for the run (AI SDK policy tool approvals). This gives you live truth, at the price of a hop on every call.

Subagents sharpen the problem. A spawned worker is a new execution principal, not a copy of its parent, and inheriting the parent's token by default is a privilege carryover that lets a read-only reporting worker hold write scope (Permit on subagent MCP permissions). Whatever you choose, the decision must be attached to the specific child, tool and arguments.

Cache windows set how long a stale tier survives

Forget the token-versus-lookup framing for a moment and name the real variable: the maximum interval during which a downgraded caller can still act on the old tier. Every design has one. Write it down before you pick.

If the tier is a token claim, that interval is the token's remaining lifetime. Keycloak's access token lifespan defaults to five minutes, with a realm-wide SSO session idle default of 30 minutes and a session maximum of 10 hours (Skycloak on JWT lifecycle). Short access tokens in the 5 to 15 minute range are the standard advice precisely because expiration is the one revocation mechanism that always works when validation never phones home.

Do the arithmetic. A five-minute access token with a support tier that lets the user finish the current job means the worst case for a downgrade is roughly five minutes, not zero. If your support desk tells a customer "your access changes immediately," and the token lives five minutes, the desk is wrong by up to five minutes. That gap is not a bug to be fixed by wishful thinking; it is a number to be chosen and written into the runbook.

A per-call lookup has a staleness window of one policy cache TTL, which you also choose. Okta's guidance for least privilege asks that policies be re-checked on sensitive actions and revocation paths and that claims be treated as advisory when the underlying permission can change during the token's life (Okta on least privilege for agents).

DesignStaleness windowRevocation lagPer-call dependencyBest fit
Tier in token, no lookupRemaining token lifetimeUp to full token lifetime plus clock skewNone after issuanceSlow-changing tiers, low-impact tools
Lookup on every callOne in-flight checkNear zeroPolicy engine or database on the hot pathMoney movement, deletes, data export
Lookup cached per caller, short TTLCache TTLCache TTL, cut short by invalidation pushPeriodic onlyMost tier gates on read-oriented tools
Hybrid: claim for coarse gate, lookup for high impactToken lifetime for low impact, TTL for high impactNear zero for high-impact toolsSelectiveMixed tool catalogs, the common landing point

The hybrid row deserves an example. Suppose a reading agent can call search_repo and read_file. The tier claim gates both, no lookup, so p99 stays flat. The same agent can also call git push, which the policy hard-denies without human review (AI SDK policy tool approvals). No amount of token freshness makes that a safe claim-only decision, because the action is irreversible. The rule that falls out of this: the cheaper the decision is to reverse, the longer you can trust a claim.

Revocation lag after a downgrade

Revocation is where token claims earn their bad reputation. A signed token is valid until it expires unless something actively invalidates it, and most stateless verifiers have no such mechanism. That means a downgrade from a paid tier to free does not stop the agent's writes until the token rolls over, and a user removed from a project keeps reading until the same moment.

Three levers shorten this, and you should know which ones you have:

  1. Short exp. The blunt instrument, and the one that always works. Five-minute access tokens make the worst case small enough to explain to a customer (Skycloak).
  2. Session invalidation. Identity providers can kill server-side sessions and refresh tokens immediately, since refresh tokens are tied to session state while access tokens are not. A not-before push or back-channel logout cuts sessions that are still alive.
  3. A freshness check on high-impact calls. This is the only one that makes a downgrade take effect on the next tool call rather than the next token issuance.

Microsoft's guidance frames revocation lag as a containment problem: test the revocation path, including disabling the agent, rotating credentials, invalidating tokens and removing stale permissions, because tokens that persist and shared keys make incident response slow (Microsoft on least privilege for AI agents). If you cannot answer "how do we invalidate a tier in under a minute" in one sentence, the lookup-on-every-call design is the safer starting point.

The subagent case is worse than the user case, because the child may have its own token that the parent cannot revoke. The reported behavior in the Claude Code issue tracker, where subagents spawned via the Task tool ran commands the parent's deny list should have blocked, is an inheritance failure rather than a token lifetime failure (issue 25000). The fix pattern is a token broker that mints child-bound, scope-minimized credentials tied to a delegated approval, not a longer-lived copy of the parent's (Permit).

What the refusal log can cite as evidence

This is the part the first search result usually skips, and it is the part that decides how angry the incident review gets. A refusal you cannot explain is indistinguishable from a bug, and a reviewer reading a log line that says only "denied" will assume the policy is broken.

A defensible refusal log has, at minimum:

  • the decision (deny or requires-approval), with the policy's stated reason, which the AI SDK surfaces back to the model or the approver (AI SDK)
  • the principal: agent identity, the "on behalf of" user, and the parent or subagent relationship
  • the tool name and argument digest, not the raw arguments if they may contain secrets
  • the tier or role that was claimed, and the tier the policy says the caller actually holds
  • the token's issue time and expiry, so a reviewer can see the decision rested on a claim issued 41 minutes earlier
  • the policy version or bundle hash, so "the rule changed last Tuesday" is a verifiable statement
  • a correlation ID linking the tool call, the model turn and the downstream resource access

Microsoft's control list asks for logs that capture agent identity, role, effective scope, action, resource, correlation ID and the on-behalf-of user, then goes further: test that these records exist before you need them (Microsoft). The NHI glossary entry on callable testing subagents makes the same point from the audit side, that the evidence should record which checks ran, which tools were accessed and which exceptions were raised (NHI glossary).

Here is a log line shape that satisfies the list without leaking data:

{
  "event": "tool_decision",
  "correlation_id": "run_8842:step_11:call_3",
  "decision": "deny",
  "reason": "writes above tier ceiling require approval",
  "principal": {"agent": "reporter-v2", "on_behalf_of": "user_5512", "parent": "orchestrator"},
  "tool": {"name": "refund.create", "args_digest": "sha256:9c1f..."},
  "entitlement": {"claimed_tier": "pro", "policy_tier": "free", "source": "token"},
  "token": {"issued_at": "2026-10-14T09:12:03Z", "expires_at": "2026-10-14T09:17:03Z"},
  "policy": {"bundle": "agent/call/decision", "version": "sha256:41ab..."}
}

The line that does the most work in a review is token.issued_at next to policy_tier. It converts the argument from "the policy is wrong" into "the claim was 41 minutes stale and the log shows it," which is a conversation about token lifetimes rather than about correctness.

Where enforcement sits matters as much as how fresh it is

Two places can refuse a call: a centralized gateway in front of the tools, or the tool server itself. A gateway is a single place for baseline authentication, authorization, throttling and request validation, while service-level policy handles the decisions that depend on the target object, tenant or data field (NHI on gateway versus service enforcement). The same split shows up in AI practice: Azure API Management's AI gateway handles model access, token limits and the MCP traffic contract as the governed boundary, while the application runtime keeps orchestration (Microsoft Tech Community on the AI Gateway tier).

For a tier decision this gives a clean division. The gateway validates the token, checks the coarse tier against a route table and rejects obviously out-of-plan calls early, cheaply and in one place. The tool server owns the final say on anything that touches a specific customer record, a refund or a delete, and that is where a fresh check earns its cost. Putting the fresh lookup only at the server keeps the hot path for reads fast and keeps the precision where the data lives.

The trap is treating the route table as full authorization. An allowlisted server can still expose a destructive operation to a subagent whose task prompt said draft a reply; the server was reachable, the intent was never checked (Permit). Coarse gateway checks plus per-call policy at the server is the layering that avoids it.

A decision rule, worked through

Use this as the rule to put in the design doc:

Carry the tier in the token only if you can bound the token's remaining lifetime to the interval you are willing to be wrong about a downgrade, and log the claim's issue time next to every refusal. Otherwise do a lookup per call, or a lookup cached per caller for a few seconds with an explicit invalidation on tier change.

Now run it on a concrete case. A support agent runs inside a customer's helpdesk with three tools: ticket.search, ticket.reply and billing.refund. The product tiers are free (search only), pro (search and reply) and enterprise (all three). Marketing promised that a downgrade stops replies "immediately."

The sales pitch and the engineering answer diverge, and the numbers show it. With a five-minute access token, "immediately" is up to five minutes for ticket.reply, which is probably acceptable if the reply can be recalled. For billing.refund it is not, because money left the account. So the rule produces a split: claim-only for search and reply, fresh lookup plus required approval for refund, with the claim still checked first to reject callers who are on free tier outright. The refusal log carries the claim's issue time so a reviewer can distinguish a stale claim from a mis-set tier. If the team cannot shorten the token or push invalidation, the answer for reply flips to cached lookup with a five-second TTL, and marketing gets told the real number.

The hybrid that most systems land on follows the same shape: a coarse claim gate that fails fast, a short per-caller cache for anything that changes, and a policy call on the destructive minority. AI SDK's policy callback fits this well because the decision can inspect the full message history and running totals, so "approve a second write in one run" or "deny a third irreversible action in this conversation" are expressible as policy rather than as bespoke code (AI SDK).

If you are building this gate and want a single endpoint to test it against before you wire your own policy engine, Quanta Inference exposes text, image, video, audio and embedding models through one OpenAI-compatible API key, which is enough to drive tool-calling loops end to end while you decide where the decision belongs.

FAQ

FAQ

Can I just put the tier in the token and be done?

Only if the token's remaining lifetime is shorter than the window you are willing to be wrong about a downgrade. A five-minute access token gives you a five-minute worst case, and that is fine for reversible actions. For a refund, a delete or a data export, the claim should be the first gate, not the last word.

How short should the cache be if I do a lookup per caller?

Start with a few seconds and prove the number with your invalidation path. The cache TTL is your staleness window, so it should be short enough that a support agent can explain the delay to a customer and long enough that you are not re-reading policy on every read. Push an explicit invalidation when a tier changes so the TTL is an upper bound, not the normal case.

What should a refusal log contain?

The decision and reason, the principal including any parent agent, the tool and a digest of its arguments, the claimed tier and the policy's tier, the token issue time and expiry, the policy version and a correlation ID. Microsoft's agent guidance asks for identity, role, effective scope, action, resource, correlation ID and on-behalf-of user in the audit trail (Microsoft).

Do subagents inherit their parent's tier?

They should not inherit authority by default, even when they share the user's context. Treat the child as a delegated principal with its own scoped, time-bounded credential minted for that task, because implicit inheritance is exactly how a read-only worker ends up with write scope (Permit).

Does the choice change the policy tooling I need?

It changes where the policy runs more than what it says. A policy engine that evaluates before every tool dispatch and returns allow, deny or requires-approval gives you both modes from one rule set, and a default-deny rule catches the calls that match nothing (AI SDK).

Sources

Sources

  1. Claims Best Practices
  2. Policy-Based Tool Approvals
  3. When AI Subagents Call MCP Tools, Who Owns ...
  4. JWT Token Lifecycle: Expiration, Refresh, and Revocation
  5. How to implement least privilege for AI agents
  6. Least privilege for AI agents (agentic identities + RBAC)
  7. BUG Sub-agents bypass permission deny rules and per ...
  8. What Is Callable testing sub-agent? Definition & Examples
  9. What is the difference between API gateway enforcement ...
  10. What the New API Management AI Gateway Tier Changes ...
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles