# Current user bootstrap payload

> Source: https://elaichi.ai/docs/api-reference/current-user/user/getcurrentuser/

`GET /user/me`

Resource: **User** · API: **Current user**

## Response body

- **`id`** _(string)_
  User id (`usr_…`).
- **`email`** _(string)_
- **`name`** _(string,null)_
- **`avatar_url`** _(string,null)_
- **`identities`** _(array<object>)_
  Login identities linked to this account.
  - **`provider`** _(string)_
    e.g. `google`, `email_code`, `invite`, or an SSO connection.
  - **`connected_at`** _(string)_
- **`is_active`** _(boolean)_
  Inactive accounts cannot authenticate at all.
- **`is_staff`** _(boolean)_
  Platform staff flag. Unrelated to organization roles.
- **`last_login_at`** _(string,null)_
- **`created_at`** _(string)_
- **`updated_at`** _(string)_
- **`mfa_enabled`** _(boolean)_
  True when TOTP is active on this account.
- **`mfa_setup_required`** _(boolean)_
  True when THIS session would be refused (`403 mfa_setup_required`) in at least one organization the user belongs to, because the organization requires a second factor and the session has not shown one. Always `false` for an API token. A client routes the person to second-factor setup on this rather than working out which organizations require one.
- **`mfa_setup_required_org_ids`** _(array<string>)_
  The organizations behind `mfa_setup_required`: a bounded page (at most 50), with the organization named by the `current_org_id` query parameter first when it is one of them. Not the whole set — `mfa_setup_required_org_count` is the true number; an organization not listed still refuses with `403 mfa_setup_required` when entered.
- **`mfa_setup_required_org_count`** _(integer)_
  How many organizations refuse this session for lack of a second factor.
- **`can_disable_mfa`** _(boolean)_
  `false` while an organization the user belongs to requires a second factor and they hold no passkey — turning the authenticator app off (`DELETE /user/me/mfa/totp`) is refused with `409 mfa_required_by_organization`. The reason is in `mfa_disable_blocked_reason`.
- **`mfa_disable_blocked_reason`** _(string,null)_
  A sentence saying why `can_disable_mfa` is `false`, or `null`.
- **`is_root_org_staff`** _(boolean)_
  Platform staff who are also a member of the root organization — gates the staff console.
- **`organizations`** _(array<object>)_
  One entry per active membership. Organizations where membership has lapsed are omitted.
  - **`organization`** _(object)_
    - **`id`** _(string)_
      Organization id (`org_…`) — the value for `X-Organization-Id`.
    - **`name`** _(string)_
    - **`slug`** _(string)_
      Immutable after creation.
    - **`plan`** _(string)_
      The stored plan: `gold`, `black`, or `none` when locked. This is not the same as the effective entitlement — read `GET /organization/{id}/entitlements` before gating a feature on it.
    - **`region`** _(string,null)_
      Data location fixed at creation: `us`/`eu` pin a hard jurisdiction, `apac` is a placement hint.
    - **`trial_ends_at`** _(string,null)_
    - **`trial_indefinite_at`** _(string,null)_
      When staff granted an indefinite trial, or null. Unlocked with no end date: no countdown, no expiry email, billing still reachable.
    - **`settings`** _(object)_
      Free-form org settings. `PATCH /organization/{id}` REPLACES this object wholesale.
    - **`logo`** _(string,null)_
      Public URL of the logo image, or null.
    - **`can_delete`** _(boolean)_
      Whether the CALLER may delete this organization — the `org:delete` permission, which only the Org Owner role holds, and never for the platform root organization. Server-computed: branch on this rather than inspecting roles. Always `false` where the response has no member context to compute it from — the org switcher list `GET /organization` (one member-context lookup per row would be a per-row round trip), the staff console, and the invite-accept response. It never overstates: trust it when true, and read `GET /user/me` or `GET /organization/{id}` (both of which compute it) when you need it for a list row.
    - **`can_manage`** _(boolean)_
      Whether the CALLER may change this organization's own settings — the `org:manage` permission that `PATCH /organization/{id}` requires, and the organization neither staff-suspended nor in its deletion grace period. Server-computed, and `false` wherever `can_delete` is for lack of a member context; it never overstates.
    - **`allow_staff_impersonation`** _(boolean)_
      Whether Elaichi staff may impersonate this organization's members for support. `true` by default. When `false`, every request an impersonated session makes against this organization is refused with `403 staff_impersonation_blocked`, reads included, and the organization is left out of that session's `GET /user/me` and `GET /organization`. Changed with `PATCH /organization/{id}` (`org:manage`).
    - **`mfa_required`** _(boolean)_
      Whether members must hold a second factor to act in this organization. `false` by default, for existing and new organizations alike. When `true`, a signed-in session that has not shown a second factor — no TOTP challenge at login, not a passkey sign-in, no factor enrolled since — is refused on every organization-scoped route, reads included, with `403 mfa_setup_required` (`error.details.has_second_factor` says whether the person already has one and only needs to sign in with it). Exempt: org API tokens, Elaichi staff impersonation sessions, and a session signed in through THIS organization's own SSO connection. MCP connections made before it was switched on, or from a session without a factor, are refused too until the person connects again. Turning it OFF only lifts that requirement: a member who set up an authenticator app of their own is still challenged for it at every non-passkey sign-in, because the factor belongs to the member and no organization setting can switch it off. Changed with `PATCH /organization/{id}` (`org:manage`, interactive session only).
    - **`can_require_mfa`** _(boolean)_
      Whether the CALLER may turn `mfa_required` ON right now: `can_manage`, from an interactive session that would itself pass the check it is about to create. `false` — with the reason in `can_require_mfa_reason` — when switching it on would lock the caller out of this organization. The PATCH refuses that regardless with `409 mfa_setup_required`. Turning it OFF is not governed by this field; it needs a step-up.
    - **`can_require_mfa_reason`** _(string,null)_
      A sentence saying why `can_require_mfa` is `false` for a caller who can otherwise manage the organization. `null` otherwise.
    - **`deletion_scheduled_at`** _(string,null)_
      Set when a deletion has been scheduled (`DELETE /organization/{id}`). The organization is read-only until `purge_after`. Null otherwise.
    - **`purge_after`** _(string,null)_
      When a scheduled deletion becomes permanent — 30 days after `deletion_scheduled_at`. Null unless scheduled.
    - **`can_restore`** _(boolean)_
      Whether the CALLER may cancel a scheduled deletion: the `org:manage` permission, and only while `purge_after` is still ahead. Server-computed, like `can_delete`, and `false` for the same reasons — no member context (the org switcher list, the staff console) — plus once the window has closed. Read `purge_after` to tell "too late" from "not yours to undo".
    - **`can_authorize_apps`** _(boolean)_
      Whether the CALLER may connect an app (MCP OAuth consent) to this organization right now: `false` while it is suspended, scheduled for deletion, or has no active plan. Server-computed by the same gate `POST /oauth/authorize-request/{id}/approve` calls; present only on member-scoped responses. `authorize_apps_blocked_reason` says which.
    - **`authorize_apps_blocked_reason`** _(string,null)_
      Why `can_authorize_apps` is `false`, in the gate's own order: `blocked`, then `pending_deletion` (even when the subscription is also gone, since a soft delete cancels it), then `no_plan`. `null` when connecting is open.
      Allowed: `blocked`, `pending_deletion`, `no_plan`, `null`
    - **`created_at`** _(string)_
    - **`updated_at`** _(string)_
  - **`member`** _(object)_
    Membership facts only — roles and teams are the sibling fields below, not nested here.
    - **`user_id`** _(string)_
      User id (`usr_…`).
    - **`status`** _(string)_
    - **`via`** _(string,null)_
      How they joined, e.g. invite or verified domain.
    - **`joined_at`** _(string)_
  - **`roles`** _(array<object>)_
    Role summaries held in this organization.
    - **`id`** _(string)_
      Role id (`role_…`).
    - **`name`** _(string)_
    - **`is_system`** _(boolean)_
    - **`seat_class`** _(string)_
      Allowed: `billable`, `free_admin`
  - **`teams`** _(array<object>)_
    - **`id`** _(string)_
      Team id (`team_…`).
    - **`name`** _(string)_
  - **`permissions`** _(array<string>)_
    Effective permission names, unioned across roles. This is the authoritative list for deciding what to show; the server re-checks it on every request.

## Code examples

### curl

```bash
curl -X GET 'https://api.elaichi.ai/user/me' \
  -H 'Authorization: Bearer $ELAICHI_API_TOKEN' \
  -H 'Content-Type: application/json'
```

### JavaScript

```javascript
const response = await fetch('https://api.elaichi.ai/user/me', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer ' + process.env.ELAICHI_API_TOKEN,
    'Content-Type': 'application/json',
  },
});

const data = await response.json();
console.log(data);
```

### Python

```python
import os
import requests

url = "https://api.elaichi.ai/user/me"
headers = {
    "Authorization": f"Bearer {os.environ['ELAICHI_API_TOKEN']}",
    "Content-Type": "application/json",
}

response = requests.get(url, headers=headers)
print(response.json())
```
