/api/external/v2/me/team-accounts/api/external/v2/account/teamMulti-Account Access
A single Lev user can belong to more than one account — for example, a broker who works across two brokerages, or a user who sits on both a sponsor account and a lender account. Every Lev API response is scoped to a single active account, so callers that need data across accounts must tell Lev which account a given request targets.
How scoping works
Every request on a multi-account user must carry the X-Active-Account header with the target account's slug. The backend resolves the slug against the user's active memberships and returns that account's data. Requests from single-account users, or from API keys (which are bound to a specific account at issuance and don't read this header), may omit it — the account is implied.
X-Active-Account: broker-magiciansGET /me is the one exception. It's the discovery endpoint — calling it without X-Active-Account is how clients bootstrap, and it returns the unscoped response with available_accounts[]. Every other endpoint enforces the rule.
If a multi-account user sends a non-/me request without X-Active-Account (and the caller is not an API key), the backend returns 400 Bad Request:
{
"error": "Active account required. Provide X-Active-Account header or use an API key."
}Discovering available accounts
Call GET /api/external/v2/me without the X-Active-Account header. The unscoped response returns the authenticated user plus an available_accounts array — every account the user has an active membership on — so clients can present a picker or pass a slug programmatically. The Current User section below documents both response branches.
Switching accounts
There are two ways to switch the account a request is scoped to:
- Per-request (JWT users) — change the
X-Active-Accountheader value on the next request. This is the lightweight path for clients that already hold the target slug. - Persisted (MCP, and anyone who wants sticky scoping) — call
PATCH /me/active-accountwith{ "slug": "<target>" }. The backend stores the preference on the user's record and auto-resolves subsequent requests against it until you switch again. The MCP server'sswitch_accounttool is a wrapper around this endpoint.
Current User
/api/external/v2/meGet the authenticated user's profile, account, and platform details
GET /me has two response branches depending on whether the request carries X-Active-Account. Multi-account users should call it unscoped first to discover their memberships, then supply X-Active-Account on every subsequent request.
Scoped response (200) — request carries X-Active-Account: <slug>:
{
"request_id": "...",
"timestamp": "2026-03-20T15:30:45Z",
"data": {
"user": {
"id": 1234,
"uuid": "550e8400-e29b-...",
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Smith",
"full_name": "Jane Smith",
"role": null,
"photo_url": null,
"last_login_at": "2026-03-20T10:00:00Z"
},
"account": {
"id": 56,
"name": "Acme CRE Advisors",
"account_type": "brokerage",
"org_id": 789
},
"profile": {
"id": 1001,
"title": "Managing Director",
"role": "admin"
},
"subscription": {
"type": "enterprise",
"status": "active",
"billing_cycle": "annual",
"end_date": "2027-01-01T00:00:00Z"
},
"platform": {
"api_tier": "standard",
"granted_scopes": ["deals:read", "deals:write", "contacts:read"],
"rate_limits": {
"requests_per_minute": 100,
"per_tool_per_minute": 20,
"concurrent_requests": 10,
"ai_actions_per_minute": 5,
"daily_read_cap": null,
"daily_write_caps": null
},
"api_keys": {
"current_count": 2,
"max_allowed": 10
}
}
}
}The platform section includes your API tier, granted scopes, current rate limits, and API key usage.
Unscoped response (200) — request omits X-Active-Account:
{
"request_id": "...",
"timestamp": "2026-03-20T15:30:45Z",
"data": {
"user": {
"id": 1234,
"uuid": "550e8400-e29b-...",
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Smith",
"full_name": "Jane Smith",
"role": null,
"photo_url": null,
"last_login_at": "2026-03-20T10:00:00Z"
},
"account": null,
"available_accounts": [
{
"id": 56,
"name": "Acme CRE Advisors",
"slug": "acme-cre",
"account_type": "brokerage"
},
{
"id": 72,
"name": "Redwood Capital Partners",
"slug": "redwood-capital",
"account_type": "lender"
}
],
"profile": null,
"subscription": null,
"platform": null
}
}account, profile, subscription, and platform are all null until an account is selected. available_accounts lists every account the authenticated user has an active membership on (capped at 50); use any entry's slug as the X-Active-Account value on subsequent requests.
The JSON examples on this page show the identity fields you'll use most. The full account object is below — every field here is returned on available_accounts, GET /me/accounts, and PATCH /me/active-account.
| Field | Type | Description |
|---|---|---|
id | integer | Account identifier |
name | string|null | Account display name. Same value as external_display_name |
slug | string | URL-safe identifier; pass as X-Active-Account |
account_type | string|null | Account classification. One of brokerage, borrower, lender, credit (or lev_internal for Lev-internal accounts, which external callers are unlikely to see). |
org_id | integer|null | Lender organization ID. Set on lender accounts; null elsewhere |
external_display_name | string|null | Public-facing account name |
additional_states | string[] | US state codes the account operates in beyond its primary address. Empty array when none are set |
logo_url | string|null | Account logo URL |
square_logo_url | string|null | Square-cropped logo URL |
website | string|null | Account website |
about | string|null | Free-text account description |
address | string|null | Street address |
city | string|null | City |
state | string|null | State |
zip | string|null | Postal code |
linkedin_url | string|null | LinkedIn profile URL |
unauthorizedAvailable Accounts
/api/external/v2/me/accountsList every account the authenticated user has an active membership on
A dedicated endpoint for fetching just the account list, without the rest of the /me payload. Convenient when the client already knows who the user is and only needs the account picker data.
Rate limits follow the account's tier, as shown by GET /me under platform.rate_limits.
Response (200):
{
"request_id": "...",
"timestamp": "2026-03-20T15:30:45Z",
"data": {
"accounts": [
{
"id": 56,
"name": "Acme CRE Advisors",
"slug": "acme-cre",
"account_type": "brokerage"
},
{
"id": 72,
"name": "Redwood Capital Partners",
"slug": "redwood-capital",
"account_type": "lender"
}
]
}
}Each account object carries the same fields documented under Current User's unscoped response (id, name, slug, account_type). Use any entry's slug as the X-Active-Account header value or pass it to PATCH /me/active-account to persist the selection.
This list is membership-based. Accounts linked to yours through Lev's team-account sharing appear under Team Accounts instead.
unauthorizedTeam Accounts
/api/external/v2/me/team-accountsList the accounts whose deals are shared with your active account
Some organizations link accounts together so a parent account can see the deals of the teams under it — a brokerage with regional offices, for example. Lev calls the linked accounts team accounts, and this endpoint returns the ones attached to the account the request is scoped to.
This is not the same list as Available Accounts. That endpoint returns accounts you hold a membership on; this one returns accounts your account is linked to, which you usually aren't a member of.
Requires the account:read scope. Multi-account users must send X-Active-Account — the response describes the account the request is scoped to, not the user.
Response (200):
{
"request_id": "...",
"timestamp": "2026-03-20T15:30:45Z",
"data": {
"team_accounts": [
{
"id": 84,
"name": "Acme CRE Advisors West",
"slug": "acme-cre-west",
"account_type": "brokerage"
},
{
"id": 91,
"name": "Acme CRE Advisors Southeast",
"slug": "acme-cre-southeast",
"account_type": "brokerage"
}
]
}
}| Field | Type | Description |
|---|---|---|
id | integer | Account identifier. Matches owner_account_id on a deal, so you can tell which team a deal belongs to |
name | string|null | Account display name |
slug | string|null | URL-safe identifier |
account_type | string|null | Account classification. Same values as Current User |
Team accounts carry identity fields only. There is no membership or profile data, because the caller has no membership on a team account — and for the same reason a team account's slug is not a usable X-Active-Account value. Scoping still requires a membership, so a team account slug you don't belong to is rejected like any other non-membership slug.
team_accounts is an empty array — never an error — when the account has no linked accounts. It is also empty for a caller who isn't an admin of the account: reading the team-account list is an admin-level permission, and a non-admin member gets the empty list rather than a 403.
bad_requestunauthorizedforbiddenSet Active Account
/api/external/v2/me/active-accountPersist the authenticated user's active-account preference on the server
Use this endpoint when you want account scoping to survive across requests without having to track and re-send X-Active-Account yourself. The backend stores the preference on the user's record and uses it as the default active account on every subsequent request (from any client authenticating with the same credentials). The MCP server's switch_account tool is a thin wrapper around this endpoint.
Rate limits follow the account's tier, as shown by GET /me under platform.rate_limits.
slugstringrequiredcurl -X PATCH "https://api.lev.com/api/external/v2/me/active-account" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "slug": "broker-magicians" }'Response (200):
{
"request_id": "...",
"timestamp": "2026-03-20T15:30:45Z",
"data": {
"active_account": {
"id": 56,
"name": "Broker Magicians",
"slug": "broker-magicians",
"account_type": "brokerage"
}
}
}Once set, GET /me (even unscoped) and every other request automatically scopes to this account. To switch again, call this endpoint with a new slug. To return to the "no preference" state, clients can switch back to the original account or call with another valid slug — there is no separate "clear" action.
bad_requestunauthorizednot_foundAccount Team
/api/external/v2/account/teamList team members in your account
Response (200):
{
"request_id": "...",
"timestamp": "2026-03-20T15:30:45Z",
"data": [
{
"id": 1234,
"first_name": "Jane",
"last_name": "Smith",
"email": "jane@example.com",
"role": "admin",
"title": "Managing Director",
"photo_url": null
}
]
}Each team member object contains:
| Field | Type | Description |
|---|---|---|
id | integer | User identifier |
first_name | string|null | First name |
last_name | string|null | Last name |
email | string|null | Email address |
role | string|null | Account role (admin, member, etc.) |
title | string|null | Job title |
photo_url | string|null | Profile photo URL |
unauthorized