# API: team members, roles and access tokens

_API · 6 min read · Updated 2026-10-22 · https://lanachat.ai/guides/api-team/_

Your **team** is the people who work in the workspace. Each **member** has a **role** (built-in or custom) that sets what they can do, the creators they work on, and a personal access key to sign in. **Access tokens** are the keys of the API and of the MCP connection. All these endpoints need an **owner** token: in the examples, `$LANACHAT_TOKEN` holds one.

## Endpoints

| Method | Path | Description | Permission | Status |
|---|---|---|---|---|
| GET | `/members` | List members (`status=active,disabled`, `role_id=`, `q=`) | `owner` | Planned |
| POST | `/members` | Add a member with a role and creators | `owner` | Planned |
| GET | `/members/{id}` | One member, last activity and overrides | `owner` | Planned |
| PATCH | `/members/{id}` | Change role, creators, overrides; disable or enable | `owner` | Planned |
| DELETE | `/members/{id}` | Delete a member (their past messages keep their name) | `owner` | Planned |
| POST | `/members/{id}/access-key` | Create a personal access key (shown once) | `owner` | Planned |
| POST | `/members/{id}/invitation` | E-mail an access key to the member | `owner` | Planned |
| DELETE | `/members/{id}/access-key` | Revoke the member's key: they are signed out | `owner` | Planned |
| GET | `/roles` | Built-in and custom roles with their permission table | `owner` | Planned |
| POST | `/roles` | Create a custom role | `owner` | Planned |
| PATCH | `/roles/{id}` | Rename a role or change its permissions | `owner` | Planned |
| DELETE | `/roles/{id}` | Delete a custom role no member has | `owner` | Planned |
| GET | `/tokens` | API and MCP access tokens (`include_revoked=true`) | `owner` | Planned |
| POST | `/tokens` | Create a token with its permissions (shown once) | `owner` | Planned |
| PATCH | `/tokens/{id}` | Rename a token or change its permissions | `owner` | Planned |
| POST | `/tokens/{id}/rotate` | Replace the secret; the old one stops at once | `owner` | Planned |
| DELETE | `/tokens/{id}` | Revoke a token | `owner` | Planned |

## The member object

```json
{
  "id": "mem_4",
  "name": "Ana",
  "email": "ana@mia-agency.com",
  "status": "active",
  "role_id": "role_chatter",
  "creator_ids": ["cr_lea", "cr_mia"],
  "overrides": { "billing.read": false, "fans.export": true },
  "access_key": { "active": true, "created_at": "2026-09-20T09:00:00Z" },
  "last_active_at": "2026-10-06T13:40:00Z"
}
```

| Field | Type | Description |
|---|---|---|
| `id` | string | Starts with `mem_` |
| `name` | string | Their name in the Console |
| `email` | string or null | Where the invitation goes |
| `status` | string | `active` or `disabled` |
| `role_id` | string | Their role |
| `creator_ids` | array of strings | The creators they work on |
| `overrides` | object | Permissions set for this member only, on top of the role |
| `access_key` | object | Whether they have an `active` personal access key, and since when; never the key itself |
| `last_active_at` | string or null | Their last activity |

A disabled member cannot sign in and keeps their history, role and creators.

## The role object

```json
{
  "id": "role_chatter",
  "name": "Chatter",
  "builtin": true,
  "permissions": { "live.read": true, "live.reply": true, "fans.read": true, "fans.export": false, "billing.read": false },
  "members": 6
}
```

| Field | Type | Description |
|---|---|---|
| `id` | string | The role |
| `name` | string | Its name |
| `builtin` | boolean | `true` for Owner, Manager and Chatter |
| `permissions` | object | One key per row of the permission table, `true` or `false` |
| `members` | integer | How many members have it |

Permission keys follow the rows of the permission table in **Team Management › Roles**, grouped by area: Dashboard, Creators, AI Brain, Conversations, Vault, Scripts, Playground, Telegram and Employees. **Owner** always has every permission and cannot be edited. **Manager** and **Chatter** are built in: you can change their permissions but not rename or delete them.

## The access token object

```json
{
  "id": "tok_31",
  "name": "Reporting script",
  "permissions": ["can_fan_read", "can_sales_read"],
  "status": "active",
  "created_at": "2026-10-06T15:10:00Z",
  "expires_at": "2027-01-04T15:10:00Z"
}
```

| Field | Type | Description |
|---|---|---|
| `id` | string | Starts with `tok_` |
| `name` | string | Up to 100 characters, required |
| `permissions` | array of strings | The [permissions](https://lanachat.ai/guides/api-overview/) it has |
| `status` | string | Active, expired or revoked |
| `created_at`, `expires_at` | string | ISO 8601, UTC |
| `token` | string | The secret, only in the answer that creates or rotates it |

## Members

### List members

`GET /members` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `status` | string | No | `active` or `disabled`; both separated by a comma |
| `role_id` | string | No | Only members with this role |
| `q` | string | No | Part of a name |
| `limit`, `cursor` | number, string | No | [Pagination](https://lanachat.ai/guides/api-overview/) |

```bash
curl "https://api.lanachat.ai/v1/members?status=active&role_id=role_chatter" \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
```

```json
{
  "data": [
    { "id": "mem_4", "name": "Ana", "email": "ana@mia-agency.com", "status": "active", "role_id": "role_chatter", "creator_ids": ["cr_lea", "cr_mia"], "overrides": {}, "access_key": { "active": true, "created_at": "2026-09-20T09:00:00Z" }, "last_active_at": "2026-10-06T13:40:00Z" }
  ],
  "next_cursor": null
}
```

### Add a member

`POST /members` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `name` | string | Yes | Their name |
| `email` | string | No | Needed to send an invitation |
| `role_id` | string | Yes | A built-in or custom role |
| `creator_ids` | array of strings | No | The creators they work on |

```bash
curl -X POST https://api.lanachat.ai/v1/members \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name": "Ana", "email": "ana@mia-agency.com", "role_id": "role_chatter", "creator_ids": ["cr_lea"]}'
```

The answer is the new member (`201`), active, without an access key yet.

### Get a member

`GET /members/{id}` · `owner`

```bash
curl https://api.lanachat.ai/v1/members/mem_4 \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
```

The answer is the member object. An unknown id answers `404`.

### Update a member

`PATCH /members/{id}` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `role_id` | string | No | New role |
| `creator_ids` | array of strings | No | Replaces their list of creators |
| `overrides` | object | No | Permissions for this member only |
| `status` | string | No | `active` or `disabled` |

```bash
curl -X PATCH https://api.lanachat.ai/v1/members/mem_4 \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"creator_ids": ["cr_lea", "cr_mia"], "overrides": {"fans.export": true}}'
```

The answer is the updated member. `status: "disabled"` cuts access at once; the role and creators are kept.

### Delete a member

`DELETE /members/{id}` · `owner`

```bash
curl -X DELETE https://api.lanachat.ai/v1/members/mem_4 \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
```

The member is removed and their creators are unassigned. Their past messages keep their name. To keep them for later, disable them instead.

## Personal access keys

Team members don't use a password: they sign in with a personal access key. Keys start with `emp_`.

### Create an access key

`POST /members/{id}/access-key` · `owner`

```bash
curl -X POST https://api.lanachat.ai/v1/members/mem_4/access-key \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
```

```json
{ "member_id": "mem_4", "sign_in_url": "https://app.lanachat.ai/login/?mode=employee", "access_key": "emp_6Yq2…", "shown_once": true }
```

Send both the sign-in address and the key to the person through a channel you trust. A disabled member answers `409`: enable them first.

### E-mail an access key

`POST /members/{id}/invitation` · `owner`

```bash
curl -X POST https://api.lanachat.ai/v1/members/mem_4/invitation \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
```

The key is e-mailed to the address on their card. A member without an e-mail address, or a disabled one, answers `409`.

### Revoke an access key

`DELETE /members/{id}/access-key` · `owner`

```bash
curl -X DELETE https://api.lanachat.ai/v1/members/mem_4/access-key \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
```

The key stops at once and the member is signed out. Create a new key to let them back in.

## Roles

### List roles

`GET /roles` · `owner`

```bash
curl https://api.lanachat.ai/v1/roles \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
```

A list of role objects: Owner, Manager, Chatter and your custom roles.

### Create a custom role

`POST /roles` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `name` | string | Yes | The role's name |
| `permissions` | object | No | The rows to turn on; the others are off |

```bash
curl -X POST https://api.lanachat.ai/v1/roles \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name": "Night chatter", "permissions": {"live.read": true, "live.reply": true, "fans.read": true}}'
```

The answer is the new role (`201`) with `builtin: false`.

### Update a role

`PATCH /roles/{id}` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `name` | string | No | New name (custom roles only) |
| `permissions` | object | No | The rows to change; the others are kept |

```bash
curl -X PATCH https://api.lanachat.ai/v1/roles/role_chatter \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"permissions": {"fans.export": false}}'
```

The change applies at once to every member with that role, except their own overrides. Editing Owner, or renaming Manager or Chatter, answers `409`.

### Delete a custom role

`DELETE /roles/{id}` · `owner`

```bash
curl -X DELETE https://api.lanachat.ai/v1/roles/role_night \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
```

Deleting a role that members still have answers `409`: give them another role first. Built-in roles cannot be deleted.

## Access tokens

### List tokens

`GET /tokens` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `include_revoked` | boolean | No | `true` also lists revoked tokens |

```bash
curl "https://api.lanachat.ai/v1/tokens?include_revoked=true" \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
```

A list of access token objects, without their secret.

### Create a token

`POST /tokens` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `name` | string | Yes | What it is for, up to 100 characters |
| `permissions` | array of strings | Yes | The [permissions](https://lanachat.ai/guides/api-overview/) it gets |
| `expires_in_days` | integer | No | When it stops |

```bash
curl -X POST https://api.lanachat.ai/v1/tokens \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name": "Reporting script", "permissions": ["can_fan_read", "can_sales_read"], "expires_in_days": 90}'
```

```json
{ "id": "tok_31", "name": "Reporting script", "token": "lc_live_8f2c1d0e9b7a4c6f", "permissions": ["can_fan_read", "can_sales_read"], "expires_at": "2027-01-04T15:10:00Z" }
```

`token` is in this answer only. A token can never give more than the owner token that created it. A missing name answers `400`.

### Update a token

`PATCH /tokens/{id}` · `owner`

| Parameter | Type | Required | Description |
|---|---|---|---|
| `name` | string | No | New name |
| `permissions` | array of strings | No | Replaces its permissions |

```bash
curl -X PATCH https://api.lanachat.ai/v1/tokens/tok_31 \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"permissions": ["can_sales_read"]}'
```

The answer is the token, without its secret. Turning `can_engine` off also removes publishing, Advanced AI and experiment-report permissions. A revoked token answers `409`.

### Rotate a token

`POST /tokens/{id}/rotate` · `owner`

```bash
curl -X POST https://api.lanachat.ai/v1/tokens/tok_31/rotate \
  -H "Authorization: Bearer $LANACHAT_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)"
```

The answer is the token with a new `token` secret, shown once. The old secret stops at once, so update your script right after. A revoked token answers `409`.

### Revoke a token

`DELETE /tokens/{id}` · `owner`

```bash
curl -X DELETE https://api.lanachat.ai/v1/tokens/tok_31 \
  -H "Authorization: Bearer $LANACHAT_TOKEN"
```

The token stops at once. A token already revoked answers `409`.

> Warning: A token works like a password. Revoke it as soon as you stop using it or think it leaked.

## Related

- [LanaChat API: quick start, authentication and errors](https://lanachat.ai/guides/api-overview/)
- [Team Management: employees and roles](https://lanachat.ai/guides/team-and-roles/)
- [Custom roles and the permission table](https://lanachat.ai/guides/custom-roles/)
- [Personal access keys](https://lanachat.ai/guides/personal-access-keys/)
