API: team members, roles and access tokens
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 #
{
"id": "mem_4",
"name": "Ana",
"email": "[email protected]",
"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 #
{
"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 #
{
"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 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 |
curl "https://api.lanachat.ai/v1/members?status=active&role_id=role_chatter" \
-H "Authorization: Bearer $LANACHAT_TOKEN"
{
"data": [
{ "id": "mem_4", "name": "Ana", "email": "[email protected]", "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 |
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": "[email protected]", "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
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 |
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
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
curl -X POST https://api.lanachat.ai/v1/members/mem_4/access-key \
-H "Authorization: Bearer $LANACHAT_TOKEN" \
-H "Idempotency-Key: $(uuidgen)"
{ "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
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
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
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 |
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 |
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
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 |
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 it gets |
expires_in_days | integer | No | When it stops |
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}'
{ "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 |
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
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
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.