---
title: "Whoami"
method: GET
path: "/users/me/whoami"
tags: ["users"]
---

# Whoami

`GET /users/me/whoami`

## Request

- Base URL: `https://app.zeno-works.ch` (the document declares no server; this is the origin it was published from)
- URL: `https://app.zeno-works.ch/users/me/whoami`
- Auth: none declared

Which user this request acts as, and which company's books it is bound to (jwt or API key).

Zeno serves every tenant from one host, and an API key is opaque — nothing else
tells a connected agent whose books it is about to write. Call this FIRST when
working over MCP: ``company_scoped: true`` means this connection's data access
is confined to ``company_id`` — ``company_name`` names it (null here means the
bound company no longer exists), no other tenant is reachable, and the admin
endpoints are refused however privileged the owner is. ``is_admin`` is that
owner's bit: worth nothing outside this company, still full authority inside it,
so a scoped key with ``is_admin: true`` may write these books even when
``company_role`` is null. ``company_scoped: false`` means the credentials carry
the owner's full principal and ``memberships`` lists the companies it can act
in. Read-only and cheap — at most one lookup. ZET-171, ZET-176.

``credential_purpose`` says which kind of credential this is, and is what the
withholding rule keys on (SP-08): anything that is neither ``full`` nor ``session``
is confined, and gets ``memberships`` **withheld — null, not empty**.

Working over MCP, what you book or accept is recorded as MCP — not as this user
acting in person — and the app displays it that way.

``capabilities`` says what this caller may do, each verdict computed from the
predicate the gate itself evaluates (SP-12) — so an agent learns what it cannot do
before it collides rather than after. A credential confined **to a company** gets the
entries whose verdict is a fact about the *credential*; the one that would read the
owner's estate (``create_company``) is omitted, because its verdict would disclose a
sibling tenant. A credential confined to **no** company — an onboarding key — has no
sibling to disclose and does get it, which is the one operation such a key exists to
call. ``pending_admin_actions`` is present and empty until PRE-03/PRE-04 fill it.

## Headers

- `authorization` string, nullable

## Cookies

- `access_token` string, nullable

## Response `200`

Successful Response

- WhoamiOut: ``GET /users/me/whoami`` → which principal this request acts as, and which company's books it is bound to (ZET-171). Deliberately flat — no ``{"user": …}`` envelope: the reader is an agent orienting itself over MCP, not the SPA, and every field is an answer to "where am I", not a resource. Field semantics, stated precisely because agents act on them: - ``company_scoped`` — the request is confined to exactly one company (a company-scoped API key). ``company_id``/``company_name`` name that company; ``company_role`` is the key owner's per-membership role there (``None`` for a global admin who owns the key without being a member). - ``memberships`` is ``None`` — withheld, not empty — when scoped: a confined credential is not told about the owner's other tenants. Unscoped principals (sessions, full-access keys) get the owner's membership rows, the same data ``GET /users/me`` returns. - ``is_admin`` is the credential **owner's** global admin bit. Read it with the ZET-176 rule in mind: when ``company_scoped`` is true the request is refused on every instance-wide admin surface (``/users``, ``/impex``, ``/admin/*``, integration + storage-connection management, QST tariff writes) no matter what this field says — but the bit is *not* cosmetic, because it still bypasses the per-company role gates, so an ``is_admin: true`` scoped key can write the bound company's books even though ``company_role`` is null (a global admin owns the key without holding a membership row). The data layer is narrowed to the bound company either way. - ``credential_purpose`` — what kind of credential this is, and **the basis of the withholding rule**: ``capabilities`` and ``pending_admin_actions`` follow ``memberships`` and are withheld (``None``) for anything that is neither ``"full"`` nor ``"session"``. Keyed on purpose rather than on ``company_scoped`` deliberately — see :data:`CredentialPurpose`. - ``capabilities`` — what the caller may do, each verdict computed from the predicate the gate itself evaluates (SP-12). ``pending_admin_actions`` is still empty-but-present, awaiting PRE-03/PRE-04. Both live here rather than on a new endpoint because ``BASE_INSTRUCTIONS`` already mandates calling ``whoami`` first, so they cost zero extra round trips.
  - `user_id` string, required
  - `email` string, email, required
  - `auth_method` 'api_key' | 'session', required
  - `is_admin` boolean, required
  - `company_scoped` boolean, required
  - `company_id` string, nullable
  - `company_name` string, nullable
  - `company_role` 'owner' | 'accountant' | 'member', nullable
  - `memberships` MembershipOut[], nullable
    - `company_id` string, required
    - `role` 'owner' | 'accountant' | 'member', required
  - `credential_purpose` 'full' | 'company' | 'onboarding' | 'session', required
  - `capabilities` Capability[], nullable
    - `name` string, required
    - `allowed` boolean, required
    - `reason` string, nullable, required
    - `remedy` string, nullable, required
    - `operation` string, nullable, required
    - `needs_human` boolean, required
  - `pending_admin_actions` PendingAdminAction[], nullable
    - `kind` string, required
    - `count` integer, required
    - `how` string, nullable, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/zeno-works/apis/zeno-api.md) · [All operations](https://skmtc.dev/zeno-works/apis/zeno-api/llms.txt) · [OpenAPI document](https://skmtc.dev/zeno-works/apis/zeno-api/revisions/1eb93dde146c?raw)
