# Confluence Permissions — Agent Guidance

Covers `src/tools/permissions.ts` (24 tools, all `admin_`-prefixed) plus
`admin_get_space_permissions` (`src/tools/admin.ts`) — global (instance-wide) and per-space
permission read/grant/revoke/set. Confirmed live 2026-08-20; see
`docs/plans/2026-08-20-full-api-coverage.md` batch 7 for the full record.

## Every grant/revoke pair shares one shape

`admin_grant_*`/`admin_revoke_*` (anonymous/group/unlicensed/user, both global and space-scoped)
all take an `operations` array of `{targetType, operationKey}` objects — e.g.
`{targetType: "space", operationKey: "read"}`. The get/set tools follow the same subject taxonomy.
There's no per-tool special-casing; if you understand one pair, you understand all of them.

## Granting global permissions requires `application`/`use` to exist first

Confirmed live: granting *any* global permission to a subject with no existing global grants fails
with `"'USECONFLUENCE' permission has to exist before adding any other permissions for a subject"`
unless `{targetType: "application", operationKey: "use"}` is included in the same call (or already
granted). This is a real Confluence business rule, not a bug in these tools — if a grant call fails
with that message, add the `use` operation to the request.

## Revoking a space's last administrator is blocked, on purpose

Confluence refuses to leave a space with zero `administer` grants — `admin_revoke_space_user_permissions`/
`admin_revoke_space_group_permissions` correctly 400 with `"not allowed to remove all the
Administration Permissions"` if the target is the only remaining admin. If a revoke fails this way,
grant `administer` to someone else on that space first.

## Space-scoped user-permission tools need `userKey`, not username

`admin_get_space_user_permissions`/`admin_grant_space_user_permissions`/
`admin_revoke_space_user_permissions` take the user's opaque internal `userKey` (from `get_user` or
`get_current_user`), not their username — confirmed live, passing a username 404s. This is
inconsistent with the **global** equivalents (`admin_get_user_global_permissions` etc.), which
accept plain username. Don't assume the two are interchangeable.

## What's flagged as genuinely unverified, and why

`admin_set_global_permissions` and the anonymous/group/unlicensed grant-revoke pairs at global
scope are implemented and unit-tested but not live-verified against this project's real debug
instance — deliberately, since a mistake there changes real instance-wide access with no scoped,
disposable blast radius. `admin_grant_user_global_permissions`/`admin_revoke_user_global_permissions`
*were* live-verified (a safe round-trip against the acting user's own direct grant, confirmed
restored to its empty baseline afterward) — that proves the shared request/response code every
other pair in this file uses. If one of the unverified ones misbehaves, the fix is almost certainly
not in this file (the code path is proven); look for an instance-specific permission quirk instead.
