Resolve the authenticated actor's effective POS scopes
Returns the pos.* scopes the CALLING credential currently holds. This is the discovery entry point: call it FIRST, once, before planning any other work, and use the answer to decide what to attempt.
HOW TO USE THE RESULT. Every documented operation carries an x-required-permissions array. If an entry of that array is missing from effectivePermissions, the router gate refuses the call with 403 before the handler runs — do not attempt it, and do not retry it with a different body, because nothing about the request can change the outcome. Match the scope EXACTLY (pos.team.view does not satisfy pos.team.manage) and match it PER OPERATION, not per resource: most routers gate reads on a .view scope and writes on a .manage one, so holding pos.customers.view lets you list customers and NOT edit them.
NECESSARY, NOT SUFFICIENT (solya-pos#950). Holding the advertised scope only gets you past the router gate. Several use-cases additionally assert a kernel permission that the scope does not imply, so a call can pass the gate and still return a bare 403 from inside the handler — known cases include the team writes (pos.team.manage) and recording a customer consent decision (pos.customers.manage, solya-pos#949). Treat a 403 on an operation whose scope you DO hold as a server-side grant gap to report, not as something to work around. Some operations also demand a second, human authority on top of any scope — see POST /v1/auth/elevate.
READING THE PAYLOAD. The answer is wrapped: { data: { effectivePermissions } }. That envelope is specific to this operation — nearly every other operation returns its payload raw — so read data.effectivePermissions, not the root. The array is sorted and de-duplicated, which makes it stable enough to cache for the lifetime of the credential and to diff between two tokens. An EMPTY array is a valid, successful answer: the caller is authenticated but holds no POS scope, so every scope-gated operation will 403 and only this endpoint and the unauthenticated reads (GET /v1/permissions/manifest, GET /v1/fiscal-keys) will work.
SCOPE AND LIMITS. This describes the CALLER and nobody else — there is no way to ask what another actor holds, and the role label on a team-directory row is a display string that answers no authorization question. It needs no scope of its own (any authenticated actor may introspect itself) and it is a pure read: it grants nothing, refreshes nothing and does not extend the session. Because it is the cheapest authenticated call in the API, it doubles as the credential check — a 401 means the bearer token is missing, malformed or expired, and re-authentication is the only fix. One code path serves both credential kinds: a Keycloak access token’s scope claim and an opaque POS session’s server-derived scopes resolve to the same vocabulary.
Authorizations
Authorization: Bearer <token>. Accepts EITHER a Keycloak access token (scopes-in-token) OR an opaque POS session token; both resolve to the same pos.* scope vocabulary the route guards enforce.
Response
The caller's effective POS scopes, sorted and de-duplicated, under a data envelope.
The caller's effective POS scopes, sorted and de-duplicated, under a data envelope.

