Permission grants and revocation.
The consent screen is where your users set the agent's blast radius, and it is almost always inherited wholesale from OAuth — a one-time decision about a permanent, coarse, application-wide scope, designed for software that does not improvise. The surprising consequence is that the screen which decides how much access people give you is not the grant screen at all: it is the revocation screen. People over-grant when taking access back is invisible, and under-grant when it looks irreversible, and the cheapest way to raise the access an agent actually gets is to make un-granting a one-click action whose consequences are shown in advance.
The screen you inherited was designed for a different actor.
OAuth consent was built for an application whose behaviour is fixed at build time. You approve calendar.events once, and what the app does with it is the same next Tuesday as it is today. Every convention on that screen follows from that assumption: permanent grants, application-wide scope, a list of capability names, no expiry.
An agent breaks each of those assumptions individually.
- Behaviour is not fixed. The same grant is exercised differently every run, because a model chose the plan. Approving the capability is no longer approving the behaviour.
- Scope is coarser than the task. The task is "reschedule this one meeting"; the available scope is "your calendar". The user is asked to authorise the larger thing to get the smaller one.
- Nobody is watching at the moment of use. A grant approved in a foreground session is exercised at 3am by a scheduled run. The mental model formed at grant time — "I'll be here when it does this" — is wrong within a week.
This is not an argument for asking more often. Per-action approval has its own playbook and its own failure mode — see approval and confirmation UX, where the enemy is habituation. This page is about the standing grant that sits underneath every one of those prompts and is reviewed by nobody.
Show the radius, not the scope names.
A scope list is a manifest of API permissions and it is unreadable as a risk statement. drive.readonly tells a user nothing; "can read all 12,431 files in your Drive, including 340 shared with people outside your company" tells them what they are deciding. You almost always have the data to resolve the second sentence at grant time — one count query — and almost nobody spends it.
- Resolve scopes into counts of real objects at the moment of the ask. Numbers make an abstraction concrete in a way no wording change ever will.
- Lead with what is irreversible. Sort the list by consequence, not by API surface: things that leave the building (send, post, pay, share) above things that change state, above reads. Most consent screens sort alphabetically, which is the same as sorting randomly.
- Name the ceiling, not just the capability. "Can spend up to $50 per day without asking" is a far better disclosure than "can make purchases", and it doubles as the control — see cost and quota UX.
- Do not pad the list. Every scope you request that the product does not visibly use trains the user to skim, and the skim is what carries them past the one that mattered.
Grant to the task, and let expiry do the narrowing.
Users cannot predict which resources an agent will need, so asking them to pre-select a narrow scope produces either a wrong guess or a resigned "everything". Time is the dimension they can reason about, and it needs no foresight: a grant good for this task, or this week, bounds the damage without requiring anyone to enumerate resources in advance.
- Offer duration as a first-class choice next to scope — this run, today, thirty days, always — with a default that is not "always". Defaults here set your entire risk posture, because almost nobody changes them.
- Escalate in place instead of asking big up front. Start with read, and when the run needs to write, ask then, with the specific object named. The ask lands when the user has context, which is the only time it is answerable.
- Expire on disuse, not just on a clock. A grant unused for ninety days should lapse with a notice. This quietly removes most standing access without a single decision from the user.
- Make renewal cheap and visible. If re-granting is a five-screen re-onboarding, users pick "always" the first time and you have lost the mechanism. One click to extend, from the notification.
The underlying property is covered in blast radius: expiry bounds damage without requiring anyone to know in advance what the task will touch.
Revocation is the load-bearing screen, and it is usually the worst one.
Here is the inversion worth designing around: how much access a user grants is set mostly by how confident they are that they can take it back. When revocation is buried in a settings page, phrased as "disconnect", and gives no hint of what will break, the rational move is to grant as little as possible and route around your product. When it is one click, reachable from the place the agent works, and shows the consequence before the click, people grant more — because the decision stopped being permanent.
- Put revocation where the agent is, not only in account settings. The moment a user wants it is the moment the agent did something surprising, and that moment happens in the activity view.
- Preview the consequence. "Revoking calendar access will stop 3 scheduled rules and cancel 1 run in progress" converts a scary irreversible-feeling action into an informed one.
- Offer pause before revoke. Most people reaching for the button want the agent to stop now, not to dismantle a week of setup. A pause that is instant and obvious absorbs nearly all of that traffic and preserves the configuration.
- Say what happens to the data. Revoking access is not deleting what was already read; be explicit about which it is, and offer the other one next to it.
- Confirm it actually happened. Show the token as revoked, with a timestamp, and stop the in-flight run visibly. A silent success reads as a failure and gets repeated.
If you ship one thing from this page, ship a revoke control in the agent's own surface that previews what will break and offers pause as the adjacent option. It is a small piece of work and it moves the grant behaviour upstream of it more than any wording change to the consent screen will.
Give the user a standing-access inventory, in their language.
Every user of a long-lived agent eventually accumulates grants nobody remembers making: an integration approved during a trial, a scope escalated for one task in March. The provider-side registry of this is an operations concern — see agent inventory and registry — but the user needs their own view, and it needs to be about consequences rather than tokens.
- One page, everything the agent can reach right now, grouped by system rather than by scope string, with the date granted and the date last used.
- Surface the unused ones first. "Not used in 4 months" next to a revoke button is the highest-yield row on the page, and it makes the review a two-minute job rather than an audit.
- Show delegation. If sub-agents or connected tools inherit the grant, say so plainly — users assume the permission stops at the thing they installed, and the surprise when it does not is expensive. Related: delegated access and consent records.
- Link each grant to what it did. A grant row that expands into the actions taken under it turns an abstract permission into an evidence trail, and it is the same data your transparency surfaces already collect.
Design the failure: what a running agent does when access disappears.
Access is revoked mid-run more often than teams plan for — by the user, by an admin, by an expiring token, by an upstream policy change. The default behaviour is the worst one: the tool call fails, the model reads a 403 as a problem to route around, and it either retries, invents a workaround, or reports success on a task it did not complete.
- Make permission errors terminal, not retryable. A 403 is not a transient failure and must not enter the retry path. It should stop the run and produce a specific state, not an apology in prose.
- Never let the model narrate around it. Surface the loss as a first-class UI state — "paused: calendar access was revoked" with resume and re-grant next to it — rather than as a sentence in the transcript that the user may never read.
- Hold the partial work. Preserve what completed and what did not, so re-granting resumes rather than restarts. This is what makes users willing to revoke in the first place; see undo and reversibility.
- Do not re-ask automatically. An agent that immediately pops a fresh consent prompt after a revocation reads as adversarial, and it is the fastest way to get the whole integration removed.
Treat the grant, the inventory and the revoke as one product surface rather than three screens owned by different teams, and instrument it: track the share of users on non-default durations, the share of grants unused for ninety days, and the revoke-to-reconnect rate. That last number is the honest one — a high reconnect rate means people wanted a pause and could not find one, and a revoke rate near zero on a long-lived integration means your inventory is not being read rather than that everything is fine. Related: progressive autonomy for how grants should widen over time, and agent identity and permissions for what is happening underneath.