# Get identity

> Read the authenticated caller and every account they can act under.

Canonical page: https://www.rundiffusion.com/docs/api/me
Endpoint: GET /api/v2/me
Authorization: OAuth device flow or Personal API Access Token or Company API Access Token

---

`me` returns who the current OAuth token belongs to and which accounts they can
bill work to. Call it to confirm a token works, and to discover the account IDs
you send in the account selection on account-scoped requests.

`GET /api/v2/me`

Authorization: OAuth device flow or Personal API Access Token or Company API Access Token

## Request

**Headers**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Authorization` | string | Yes | Bearer token. This endpoint accepts an OAuth device flow token, a Personal API Access Token, or a Company API Access Token. See [Authentication](/docs/api/authentication). |
| `X-Device-Id` | string | No | The connected-device ID, if your integration registered one. Sending it refreshes that device's last-used timestamp. Purely a hint: an unknown or malformed value never fails the request. |

No query parameters and no body.

cURL:

```bash
curl https://api2.rundiffusion.com/api/v2/me \
  -H "Authorization: Bearer $RUNDIFFUSION_TOKEN"
```

JavaScript:

```javascript
const me = await fetch('https://api2.rundiffusion.com/api/v2/me', {
  headers: { Authorization: `Bearer ${process.env.RUNDIFFUSION_TOKEN}` },
}).then(r => r.json());
```

Python:

```python
me = requests.get(
    "https://api2.rundiffusion.com/api/v2/me",
    headers={"Authorization": f"Bearer {os.environ['RUNDIFFUSION_TOKEN']}"},
).json()
```

## Response

`200 OK` with the caller and their accounts.

```json
{
  "user": {
    "uid": "k3PqV9…",
    "email": "sam@example.com",
    "email_verified": true,
    "sign_in_provider": "google.com",
    "created_at": "2025-11-03T16:42:08+00:00",
    "top_up": { "available": true, "tokens": 500 }
  },
  "accounts": [
    {
      "id": "personal",
      "kind": "PERSONAL",
      "label": "Personal account",
      "permissions": {
        "GENERATE": true,
        "BATCH_GENERATION": true,
        "TOKEN_LIMIT": false,
        "TOKEN_COOLDOWN": false,
        "VIEW_TRAIN": true,
        "SHARE_ASSETS": true,
        "MANAGE_ALL_GENERATIONS": false,
        "NUM_RESULTS_LIMIT": false,
        "BLOCK_TOOLS": false,
        "OPEN_SOURCE_APPS_ADMIN": true,
        "USE_OPEN_SOURCE_APPS": true,
        "MANAGE_TEAM_MEMBERS": false,
        "SHOW_TOKEN_BALANCE": true,
        "USE_PLUGINS": true
      },
      "permission_settings": {
        "token_limit_settings": null,
        "token_cooldown_settings": null,
        "num_results_limit_settings": null,
        "restrict_tools_settings": null
      },
      "plugin_kinds": ["*", "miro", "photoshop", "revit"]
    },
    {
      "id": "Tq8vNc…",
      "kind": "TEAM",
      "label": "Design",
      "company_name": "Acme",
      "team_label": "Team",
      "permissions": {
        "GENERATE": true,
        "BATCH_GENERATION": true,
        "TOKEN_LIMIT": true,
        "TOKEN_COOLDOWN": false,
        "VIEW_TRAIN": true,
        "SHARE_ASSETS": true,
        "MANAGE_ALL_GENERATIONS": false,
        "NUM_RESULTS_LIMIT": false,
        "BLOCK_TOOLS": false,
        "OPEN_SOURCE_APPS_ADMIN": false,
        "USE_OPEN_SOURCE_APPS": true,
        "MANAGE_TEAM_MEMBERS": false,
        "SHOW_TOKEN_BALANCE": true,
        "USE_PLUGINS": true
      },
      "permission_settings": {
        "token_limit_settings": { "max": 50, "apply_to_total_run": true },
        "token_cooldown_settings": null,
        "num_results_limit_settings": null,
        "restrict_tools_settings": null
      },
      "plugin_kinds": ["*", "miro", "photoshop", "revit"]
    }
  ],
  "newly_joined_teams": []
}
```

Account IDs are the literal string `personal` for the caller's individual account, and a
20-character ID for a team.

### Top level

| Name | Type | Description |
| --- | --- | --- |
| `user` | object | The authenticated caller. See [User object](#user-object) below. |
| `accounts` | array | Every account the caller can act under, personal first. An entry tells you what to send: a team's id goes in the team_id parameter, and the personal account is selected by sending no team_id at all. Never empty: a caller always has at least a personal account. |
| `newly_joined_teams` | array | Teams the caller became a member of while this request was served, as a pending invitation is accepted on sign-in. Each entry is { team_id, team_name }, and team_name is null on a team with no name. Almost always empty: it reports what changed on this call, not the teams you belong to, which is accounts. They are already in accounts by the time you read this. |

### User object

| Name | Type | Description |
| --- | --- | --- |
| `uid` | string | The caller's user ID, a 20-character opaque string. Example: k3PqV9…. |
| `email` | string | Email on the account. Example: sam@example.com. |
| `email_verified` | boolean | Whether that email has been verified. |
| `sign_in_provider` | string \| null | How the caller signed in. Example: google.com, or password for email and password. |
| `created` | string (ISO 8601) | When it was created, in UTC. |
| `top_up` | object | One-time token top-up availability, as { available, tokens }. Reads { "available": false, "tokens": 0 } on plans without the add-on. |

### Account object

| Name | Type | Description |
| --- | --- | --- |
| `id` | string | The account's id: the literal string personal for the caller's individual account, or a 20-character team ID to send as team_id. Example: Tq8vNc…. |
| `kind` | string | Which sort of account this is. Exactly one entry is PERSONAL. One of: `PERSONAL`, `TEAM` |
| `label` | string | Display name for the account. Example: Design. |
| `company_name` | string | The company the team belongs to. Present on TEAM entries only. Example: Acme. |
| `team_label` | string | The team's own label within that company. Present on TEAM entries only. Example: Team. |
| `permissions` | object | One boolean per SCREAMING_SNAKE permission key, mirroring what the account may do. Resolved by the server for every account kind, including PERSONAL, which gets the plan defaults, so a client never needs its own idea of what a solo user may do. |
| `permission_settings` | object | Parameters for whichever restriction permissions are active. All four keys are always present; each is null unless its permission is true in permissions. See the sections below for each shape. |
| `plugin_kinds` | array | Which plugin surfaces this account may use, already resolved against USE_PLUGINS. The * wildcard means every surface, including ones released later. Empty when USE_PLUGINS is false. Example: ["*", "miro", "photoshop", "revit"]. |

### Permission settings object

Four keys, one per restriction that carries parameters. Every key is present on
every account, and each is `null` unless two things are true: the matching
permission is `true`, and the role has parameters stored for it.

| Name | Type | Description |
| --- | --- | --- |
| `token_limit_settings` | object \| null | Parameters for TOKEN_LIMIT. See Token limit settings. |
| `token_cooldown_settings` | object \| null | Parameters for TOKEN_COOLDOWN. See Token cooldown settings. |
| `num_results_limit_settings` | object \| null | Parameters for NUM_RESULTS_LIMIT. See Num results limit settings. |
| `restrict_tools_settings` | object \| null | Parameters for BLOCK_TOOLS. See Restrict tools settings. |

#### Token limit settings

Caps what a single generation may cost.

| Name | Type | Description |
| --- | --- | --- |
| `max` | integer \| null | The ceiling, in whole tokens. A generation costing more than this is refused. Null when the role saved no ceiling, which means no limit. |
| `apply_to_total_run` | boolean | When true the ceiling applies to the whole run, so cost times the number of results. When false it applies per generation. |

```json
{ "max": 50, "apply_to_total_run": true }
```

#### Token cooldown settings

Caps how much the account may spend within a rolling window.

| Name | Type | Description |
| --- | --- | --- |
| `tokens` | integer \| null | The spend threshold for the window, in whole tokens. Null when the role saved no threshold, which means no limit. |
| `time_bucket` | string \| null | The window the threshold is measured over. Null when the role saved no window. One of: `1min`, `2min`, `5min`, `10min`, `15min`, `30min`, `1hr`, `2hr`, `3hr`, `12hr`, `1day`, `3day`, `7day`, `30day` |

```json
{ "tokens": 1000, "time_bucket": "1hr" }
```

#### Num results limit settings

| Name | Type | Description |
| --- | --- | --- |
| `max` | integer \| null | The largest num_results this account may request on a generation. Null when the role saved no cap, which means no limit. |

```json
{ "max": 4 }
```

#### Restrict tools settings

Which tools this account may not run. A tool is blocked when its ID is listed,
or when it carries a listed tool tag.

| Name | Type | Description |
| --- | --- | --- |
| `tool_tags` | object | Blocked tool tag IDs, as a map of ID to true. Match these against a tool's tool_tags array. Empty when nothing is blocked by tag. Example: { "Tg7mQx…": true }. |
| `tool_ids` | array | Tools blocked outright by ID, whatever their tags. Empty when none are. Example: ["Tl9xBv…"]. |

```json
{
  "tool_tags": { "Tg7mQx…": true },
  "tool_ids": ["Tl9xBv…"]
}
```

> **These are advisory, and the server still enforces**
>
> Read them to hide or disable what the account cannot use, so a caller is not
> offered a tool that will be refused.

### Every permission key

The complete set. The middle column is the wording an admin sees for that key on
the **Team Roles** page, so a support conversation about "Can generate" and your
code's `GENERATE` are talking about the same switch.

| Key | Shown in Team Roles as | Section | `true` means |
| --- | --- | --- | --- |
| `GENERATE` | Can generate | Generation Tools | May generate. |
| `BATCH_GENERATION` | Can use batch generation | Generation Tools | May run batch generations. |
| `VIEW_TRAIN` | Can train models | Generation Tools | May train models. |
| `SHARE_ASSETS` | Can manage Shared assets | Generation Tools | May change access settings on assets, including moving Private to Shared. |
| `MANAGE_ALL_GENERATIONS` | Can manage everyone's generations and boards | Generation Tools | May view, edit, and delete anyone's work on the team, Private included. |
| `USE_PLUGINS` | Allow plugin and API access | Generation Tools | May reach the API and the plugin surfaces listed in `plugin_kinds`. |
| `TOKEN_LIMIT` | Limit per generation token cost | Generation Tools | **Restricted.** A per-generation cap applies; read `token_limit_settings`. |
| `NUM_RESULTS_LIMIT` | Limit number of results per run | Generation Tools | **Restricted.** A cap on results per run applies; read `num_results_limit_settings`. |
| `BLOCK_TOOLS` | Block specific tools or an entire "Model Family" | Generation Tools | **Restricted.** Some tools are blocked; read `restrict_tools_settings`. |
| `TOKEN_COOLDOWN` | Implement a "Token Cooldown" | Team | **Restricted.** A spend-per-window cap applies; read `token_cooldown_settings`. |
| `SHOW_TOKEN_BALANCE` | Show Team token balance | Team | The team's token balance is visible to this member. |
| `MANAGE_TEAM_MEMBERS` | Can manage members | Team | May add and remove members. |
| `USE_OPEN_SOURCE_APPS` | Can use Open-Source Apps platform | Open-Source Apps | May use that platform. |
| `OPEN_SOURCE_APPS_ADMIN` | Can manage Open-Source Apps platform | Open-Source Apps | May administer that platform. |

The word *Team* in **Show Team token balance** is whatever the company calls a
team, and `team_label` on the account is that exact word. Substitute it to render
what the admin sees: on a company that uses *Studio* the row reads *Show Studio
token balance*.

> **On four keys, true means MORE restricted**
>
> `TOKEN_LIMIT`, `TOKEN_COOLDOWN`, `NUM_RESULTS_LIMIT`, and `BLOCK_TOOLS` are
> restrictions rather than capabilities, so their sense is inverted: `true` means a
> limit is being enforced, and `false` means none is. Treating them like the other
> ten, where `true` grants, gets you exactly backwards on the four that matter for
> clamping a request before you send it.
>
> These are also the four that populate `permission_settings`: the boolean says a
> limit applies, and the matching settings object says what it is.

> **Keys are snake_case, permission names are not**
>
> Every key in this response is `snake_case`, nested ones included. The only
> `SCREAMING_SNAKE` strings are the permission names themselves, because those are
> enum values rather than structure: the same string comes back as
> `error.details.required_permission` when a call is refused, so a denial and the
> flag you read on `/me` always match.

> **Pick the account explicitly**
>
> Anything billed or scoped to an account needs to know which one, and RunDiffusion
> never guesses. Read the account you want from `accounts`, then select it:
> a team's `id` goes in the `team_id` parameter, and omitting the parameter is
> the personal account. See
> [Authentication](/docs/api/authentication#selecting-an-account).

## Errors

| Name | Type | Description |
| --- | --- | --- |
| `401` | UNAUTHENTICATED | Missing, malformed, or expired token. This is the expected response when you call /me to test a credential that has gone stale, so it is a useful health check. |
| `429` | RATE_LIMITED | Too many requests. Back off and retry per the Retry-After header. See [Rate limits](/docs/api/rate-limits#identity). |

See [Errors](/docs/api/errors) for the full envelope and the code list.
