What it can reach
The honest boundary.
Your role decides which tools exist
The server acts as you, in the workspace you approved the client for. There is no separate service account and no wider access than your own. The tool list a client is given is built from the permissions you hold in that workspace AND the ones you gave the client when you approved it, so a read-only client does not list sendEmail, createRule or sendWithTemplate at all.
That is a stronger thing than refusing the call. A tool that is absent from the list is not a tool the model reasons about, does not eat context, and cannot be attempted on somebody’s behalf and then apologised for. It also means an empty-looking client is usually a permission you do not hold, or did not give it, rather than a feature this server is missing, which is exactly what whoAmI exists to tell you.
Registration and invocation are two gates, and only the second one holds. The list is built once, when the session opens, and your role or the client’s approval can change while it is open, so the list is stale by construction and the protocol has no way to withdraw a tool mid-session. Every gated tool therefore re-reads your role and the client’s approval before it runs. Registration is the courtesy; invocation is the boundary.
A refusal says which half is missing, as in Refused (missing_permission): your role in this mailbox is Viewer, which does not include “Send email” or Refused (not_granted_to_app): this app was not given “Send email” when you connected it, so an agent can explain it to the person it is working for and stop trying, rather than retrying an opaque error until somebody gives up. A role changed by an admin, or an approval changed in Connected apps, lands the same way, on the very next call.
Addresses are the other axis. A role says what you may do; the addresses you reach, narrowed by any domains or addresses you picked for the client, say which mailboxes it may do it to, and both have to agree before a message goes out. You reach the addresses and domains you were granted, or every address in the workspace, including ones added later, when your role holds addresses:all.
The approval: what you gave this client
Approving a client records what you chose for it: one workspace, a set of permissions, optionally some domains or addresses, and optionally an end date. The approval is read again on every call, so a change in Account → Connected apps, a narrower role, an address taken away or the end date passing all apply to the client’s next request. getConnections lists only the approved workspace, and setActiveConnection cannot leave it. A client that was connected before approvals existed has none, so it is refused until it asks again.
The ceiling is still YOU. A permission your role does not hold cannot be given to a client, and one your role loses later leaves the client too. The exceptions are account:read and account:write: they cover only your own profile, notification settings, workspaces, connected apps, invitations, signed-in devices, security log and passkeys, so no workspace role limits them. Billing can be given only by the workspace owner. Within that ceiling the choice is per client: read only, read and send, everything your role allows, or any set of permissions you pick. A client limited to some addresses cannot manage API keys or webhooks, because those reach the whole workspace.
The vocabulary is shared: a role’s permissions, an API key’s scopes and a client’s approval are drawn from one alphabet. A client may ask for permissions by name in the OAuth scope parameter, and the consent screen starts from what it asked for. The same access token also works on the REST API, under the same approval.