---
title: "Who am I"
method: GET
path: "/whoami"
tags: ["API keys"]
---

# Who am I

`GET /whoami`

## Request

- Base URL: `https://app.mandala.computer/api/v1`
- URL: `https://app.mandala.computer/api/v1/whoami`
- Auth: HTTP bearer

The credential this request was made with: the person holding it, the account and role it acts with, the workspace it is confined to (`null` for an account-wide key), and the key itself.

The role is the holder’s role now, not when the key was minted. A suspended account may still call this, and `account.status` says so.

A workspace-scoped key is not told its holder’s email address or name, nor the account’s name, because such a key may be in an end customer’s hands: each is `null`. Its `account.plan` is `null` too; `GET /account` reports the plan and its ceilings.

Requires the `viewer` role or higher.

## Response `200`

The credential and its holder.

- Whoami
  - `user` object, required
    - `id` string, required: `usr-` and sixteen hex characters.
    - `email` string, nullable, required: The address its holder signs in with. `null` for a workspace-scoped key, which may be in an end customer’s hands.
    - `name` string, nullable, required: Their display name, or `null` when they have not set one. Always `null` for a workspace-scoped key, for the same reason as `email`.
  - `account` object, required
    - `id` string, required: `acc-` and sixteen hex characters.
    - `name` string, nullable, required: The account’s name, or `null` when it has none. Always `null` for a workspace-scoped key: signup names an account after its owner, so it is usually a person’s name.
    - `plan` string, nullable, required: The id of the plan the account is on, such as `solo`, `studio` or `fleet` (the plan sold as Swarm), without its display name. `null` for a workspace-scoped key. `GET /account` gives the plan with its display name in `plan.label`, and its ceilings.
    - `status` string, required: `active`, or `suspended` when the account may not use resources.
  - `role` string, required: `owner`, `member` or `viewer`: the holder’s role on the account now, which is what the key acts with.
  - `workspace` Workspace, required
    - `id` string, required: `wsp-` and twelve hex characters. What a computer’s `workspace_id` names.
    - `name` string, required: Its name, unique within the account. Up to 40 characters.
    - `created_at` string, required: RFC 3339 timestamp.
  - `key` ApiKey, required
    - `id` string, required: `key-` and twelve hex characters. What an audit entry names as `apikey:<id>`.
    - `name` string, nullable, required: Its label, up to 60 characters, or `null` when it was minted without one. For a hosted-MCP (OAuth) credential, which only `GET /whoami` answers, `Connected app: ` followed by the connected app’s name, up to 100 characters in all.
    - `prefix` string, required: The first twelve characters of the key and an ellipsis, for recognising it. Not a secret. `oauth` for a hosted-MCP (OAuth) credential, as `GET /whoami` answers it.
    - `created_at` string, required: RFC 3339 timestamp.
    - `last_used_at` string, nullable, required: RFC 3339 timestamp of its last authenticated request, or `null` until it has one.
    - `workspace_id` string, nullable, required: The workspace it is confined to, or `null` for the whole account.
    - `workspace_name` string, nullable, required: That workspace’s name, or `null` for an account-wide key.
    - `manage_keys` boolean, required: Whether this key may use `/api-keys`. With it a key lists its holder’s keys; creating and revoking also need the holder to be a member or owner, so a viewer’s key can only list. Set only from the dashboard; no key can set it.
    - `minted_by_key_id` string, nullable, required: The id of the API key that minted this one over the API, or `null` for a key minted from the dashboard. Kept after that key is revoked.

## Other responses

- `401`: Platform authentication required or refused: reason is missing, invalid, or revoked, with WWW-Authenticate: Bearer (invalid_token for invalid/revoked).
- `403`: The credential is recognized but its holder lacks the required role or membership, or the holder/account is suspended, or the holder has not verified their email address. An ordinary denial for too low a role may carry no `reason`. The email one carries none either, and its message begins `Verify your email before`: it answers a create, a clone of a computer or a snapshot, a new API key and a new SSH key, and is cleared by verifying the address in the dashboard. `reason: "revoked"` marks authority that is gone: a suspended account or a lost membership (refused as the request arrives, or if it happens while the request is in flight), or a role that was lowered while the request was in flight. The message identifies the refusal; replacing a valid key alone does not restore authority, and sending the same call again does not help. A suspended account may still make this read: a `403` here is about the credential or the person holding it, never the account’s suspension.
- `405`: This public path exists but does not accept that method. Allow lists only its public methods, with HEAD for GET and metadata OPTIONS.
- `429`: You are asking faster than your plan allows. `Retry-After` says how many seconds to wait, and every metered response — anything past authentication and the re-check that your credential’s authority still holds — carries `RateLimit-Remaining`, so you do not have to be refused to find out. A `401`, a `403` from that re-check, an `OPTIONS` answer and an unexpected `500` carry none. The budget is spent in proportion to what a call costs rather than per request. What a call costs, in budget units, by kind (each operation’s exact figure is its `x-rate-cost` in the OpenAPI document): 1 to read one thing; 5 for a listing that spans the fleet, and 5 for a call into a running guest such as a screenshot or an exec; 10 for a power change and 10 to move file bytes; 25 for `GET /usage`, `GET /credits`, creating a public URL, and creating, rotating or testing a webhook; 50 to capture a snapshot; 100 to create, clone or restore a computer, and 100 to start an agent run, whose actions are then charged as they happen. The account-wide budget, as what an idle caller may spend at once and what refills each minute: No plan 1200 at once and 1200 a minute; Solo 4800 at once and 4800 a minute; Studio 12000 at once and 12000 a minute; Swarm 21600 at once and 21600 a minute. A single API key gets half of either. A `401` for a missing or unusable credential costs nothing: it is refused before anything is metered. A few refusals at the door cost one unit: a `403` because the role is too low or the route refuses an API key confined to a workspace, a `404` for a path that matches nothing, a `405`, and an `Idempotency-Key` answered from its stored result or still in progress. Everything else is charged in full when it is admitted, before it is carried out, so a refusal after that has spent the whole cost: a `400`, `402` or `409` from the route, a `403` its handler sends (on most routes, a suspended account included), and a model provider’s `401` on an agent run. The budget belongs to the account and is shared across everything on it, and any single API key may spend at most half of it. `RateLimit-Limit` is whichever of those two is binding on the request that carried it — usually the per-key half, and the account-wide figure when other keys have already spent more of it than this one has. All three `RateLimit-*` headers describe the same one, so `limit` minus `remaining` is what has been spent against it. A few `429`s are not about this budget, say so in their message, and can carry no `Retry-After`: a model provider’s rate limit relayed on an agent run. The cap on forks of one snapshot waiting for their secrets sends its own bounded expiry-based `Retry-After` with `reason: "contention"` (see `POST /snapshots/{id}/clone`).
- `500`: The application could not finish this: an unexpected exception, a build that broke, or a storage fault. Application refusals carry the request correlation ID; unexpected internal details are not returned.

---

[API](https://skmtc.dev/mandala/apis/mandala-computer-api.md) · [All operations](https://skmtc.dev/mandala/apis/mandala-computer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mandala/apis/mandala-computer-api/revisions/2afbb87fa703?raw)
