Authentication
The API uses session tokens or API keys. You get a session by signing in and send it as a cookie (browsers) or a bearer token; servers and scripts can use an API key instead.
Sign in
POST /auth/login
Content-Type: application/json
{ "email": "you@example.com", "password": "your-password" }The response is { "userId": "…", "accountType": "client", "tenantId": "…" } and sets an HttpOnly cookie named lex_session (SameSite=Lax, Secure in production). Sessions last 7 days by default. POST /auth/logout ends the session (204).
New users can create an account with POST /auth/register (email, password, optional displayName and workspaceName). Every client account gets its own personal workspace.
Account types
Lex has two separate account systems you can integrate with. Each app signs in only its own type, and the same email can have one account of each type with different passwords.
| Account type | App | x-lex-client | Session cookie | Sign-up |
|---|---|---|---|---|
client | Lex app | any other value (e.g. my-app) | lex_session | POST /auth/register |
contributor | Contributor platform (Mini Program developers) | contributor-dashboard | lex_contributor_session | POST /auth/register |
The x-lex-client header on /auth/login and /auth/register picks the account type. Every endpoint is for one account type: client endpoints answer 403 FORBIDDEN ("A Lex client account is required.") to contributor sessions, and /contributor/* refuses client sessions.
From a browser
Send the cookie with every request and add the client header to every request that changes something (POST, PUT, PATCH, DELETE):
const API = 'https://api.lexcetha.com/api/v1';
async function api(path: string, init: RequestInit = {}) {
const res = await fetch(`${API}${path}`, {
...init,
credentials: 'include',
headers: { 'content-type': 'application/json', 'x-lex-client': 'my-app', ...init.headers },
});
if (!res.ok) throw (await res.json()).error;
return res.status === 204 ? undefined : res.json();
}The x-lex-client header protects cookie sessions against cross-site request forgery: a plain HTML form on another site can't add it, so it can't change anything. Requests without it get 403 FORBIDDEN ("Missing client header").
Allowed origins
Browsers can only call the API from origins on its CORS allowlist. Lex's own apps are on it. To call the API from your own web app, ask the platform team to add your origin.
API keys (recommended for servers)
For a server, a backend job or CI, use an API key instead of a password.
- In the contributor platform (platform.lexcetha.com), switch the sidebar to API Integration.
- Link your Lexcetha workspace: sign in with your Lexcetha app account. Keys act as that account, in its workspace.
- Create a key with only the scopes it needs, and an optional expiry. The key (
lx_…) is shown once; Lexcetha stores only its hash.
Send it as a bearer token:
curl https://api.lexcetha.com/api/v1/bots \
-H "Authorization: Bearer lx_…"| Scope | Endpoints |
|---|---|
bots:read / bots:write | /bots, /desktop-sessions, /skills |
chat:read / chat:write | /agent-runs, /chats, /conversations, /attachments, /speech, /transcriptions, /realtime-calls |
approvals:read / approvals:write | /approvals |
connectors:read / connectors:write | /integrations, /connector-providers, /connector-connections, /catalog/connectors |
schedules:read / schedules:write | /schedules, /scheduled-runs |
documents:read / documents:write | /documents, /document-folders, /document-proposals |
catalog:read | /catalog, /plugins, /mini-programs |
usage:read | /usage |
read covers GET; write covers everything else and includes read. Any key may call GET /me and GET /tenants/current. Keys never reach sign-in, saved logins, memories, export or account deletion: those answer 403 FORBIDDEN, as does an endpoint outside the key's scopes ("This API key does not have the bots:write scope."). A revoked, expired or unknown key gets 401 UNAUTHENTICATED. Unlinking the workspace revokes every key. Actions through a key follow the same approvals as in the app.
From a server or script
Read the session token from the Set-Cookie header of /auth/login and send it as a bearer token. Bearer requests don't need the client header.
TOKEN=$(curl -s -D - -o /dev/null -X POST https://api.lexcetha.com/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"you@example.com","password":"your-password"}' \
| sed -n 's/^set-cookie: lex_session=\([^;]*\).*/\1/ip')
curl https://api.lexcetha.com/api/v1/bots -H "Authorization: Bearer $TOKEN"For a contributor account, add -H 'x-lex-client: contributor-dashboard' to the login request and read lex_contributor_session instead.
Keep tokens secret: anyone with the token acts as you until it expires or you sign out.
The current user and workspace
GET /me returns the user, their account type and, for client accounts, their workspace:
{
"id": "…", "accountType": "client", "email": "you@example.com", "displayName": "Ana",
"activeTenantId": "…", "role": "owner",
"memberships": [{ "tenantId": "…", "role": "owner", "tenant": { "id": "…", "name": "Ana's workspace", "slug": "ana-s-workspace-x1y2" } }]
}A client account owns exactly one workspace (role is owner). Workspaces have no other members: contributors use their own account type and never join a workspace. Contributor accounts return activeTenantId: null, role: null and no memberships.
Mini Programs built by contributors reach a workspace through the marketplace: GET /catalog/mini-programs lists every published Mini Program with this workspace's installation, and POST /mini-program-installations with { "miniProgramId": "…", "acceptedPermissions": [...] } installs one after accepting every permission it asks for.
