Appearance
REST API
Everything the admin panel does goes through a REST API at /api/v1 on your server, and you can use it too: to sync leads into a CRM, export conversations, manage bots from a script, or handle data requests.
Your server describes the API in full, with every request and response shape:
https://chat.example.com/api/docs: an interactive reference (OpenAPI UI);https://chat.example.com/api/openapi.json: the OpenAPI 3.1 document, for code generators.
(Replace https://chat.example.com with your server's address.) This page covers how to authenticate and lists the endpoints with who may call them.
Authentication
API tokens are for programs. An admin creates them in the admin panel (Settings, API tokens), choosing a name, the scopes and an optional expiry; the token (wft_...) is shown once. Send it as a bearer token:
sh
curl -H "Authorization: Bearer wft_xxxxxxxx_yyyyyyyy" https://chat.example.com/api/v1/leadsA bad, expired or revoked token gets 401; a token without the scope an endpoint needs gets 403. Revoking a token takes effect at once.
Sessions are for people: the admin panel signs in with POST /api/v1/auth/login, which sets an httpOnly cookie (it lasts 14 days, renewed as you use it). Requests that change something with a session cookie must also send the CSRF token from the login response (or GET /api/v1/me) in an X-CSRF-Token header. Some endpoints only work with a session, because they act as a person: creating API tokens, managing the team, and replying in conversations.
Scopes
A token can only call endpoints its scopes cover. Each scope also stands in for a role, and a token acts with the highest role its scopes imply (so endpoints that need the admin role also need an admin-level scope).
| Scope | Acts as | Endpoints |
|---|---|---|
bots:read | viewer | 16 |
bots:write | admin | 10 |
conversations:read | viewer | 4 |
conversations:write | agent | 3 |
leads:read | viewer | 2 |
leads:write | agent | 3 |
knowledge:write | admin | 5 |
analytics:read | viewer | 5 |
providers:write | admin | 4 |
tools:write | admin | 12 |
webhooks:write | admin | 8 |
privacy:write | admin | 3 |
settings:read | viewer | 1 |
Examples
sh
TOKEN=wft_xxxxxxxx_yyyyyyyy
API=https://chat.example.com/api/v1
# the newest conversations of one bot (conversations:read)
curl -H "Authorization: Bearer $TOKEN" "$API/conversations?botId=bot_01K...&limit=20"
# a transcript as Markdown (conversations:read)
curl -H "Authorization: Bearer $TOKEN" "$API/conversations/cnv_01K.../export?format=md"
# leads as CSV (leads:read)
curl -H "Authorization: Bearer $TOKEN" -o leads.csv "$API/leads.csv"
# change a bot's draft, then publish it (bots:write)
curl -X PATCH -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"patch": {"identity": {"instructions": "Help visitors with orders and returns."}}}' \
"$API/bots/bot_01K..."
curl -X POST -H "Authorization: Bearer $TOKEN" "$API/bots/bot_01K.../publish"Lists are newest first. To page, pass the nextBefore value of one answer as before in the next request.
Errors
Errors are JSON with a stable code:
json
{ "error": { "code": "invalid", "message": "identity.agentName: Too small: expected string to have >=1 characters" } }Common codes: bad_request and invalid (400 or 422, the message names the field), unauthorized (401), forbidden and csrf (403), not_found (404), conflict (409, e.g. a bot changed by someone else since you read it: send the revision you read when you patch), rate_limited (429, with Retry-After) and internal (500).
Live updates
The admin panel follows conversations as they happen over a WebSocket at /api/v1/live. It takes a signed-in session (not an API token); for your own systems, use webhooks. Each message is { "type": "...", "data": { ... } }: first hello ({ workspaceId }), then the workspace's events as they happen. An open socket also marks its user as available for hand-offs; the panel can send { "t": "presence", "presence": "away" } (or "online").
| Event | data (every one also has workspaceId) |
|---|---|
conversation.started | { botId: string; conversationId: string; visitorId: string } |
conversation.updated | { conversationId: string; patch: Record<string, unknown> } |
conversation.ended | { botId: string; conversationId: string; reason: string } |
message.created | { botId: string; conversationId: string; message: { id: string; seq: number; role: string; text: string; modality: string; createdAt: number; authorName?: string | null; visibility: string; attachments?: unknown[] } } |
message.delta | { conversationId: string; messageId: string; responseId: string; delta: string } |
agent.status | { conversationId: string; state: string; tool?: { name: string; label: string } } |
visitor.typing | { conversationId: string; active: boolean } |
lead.captured | { botId: string; conversationId: string | null; leadId: string; fields: Record<string, string> } |
handoff.requested | { botId: string; conversationId: string; handoffId: string; reason: string | null; summary: string | null } |
handoff.accepted | { botId: string; conversationId: string; handoffId: string; userId: string; userName: string } |
handoff.resolved | { botId: string; conversationId: string; handoffId: string | null; by: string } |
feedback.received | { botId: string; conversationId: string; messageId: string | null; kind: 'thumb' | 'csat'; value: number; comment?: string } |
bot.published | { botId: string; version: number } |
tool.called | { conversationId: string; name: string; isError: boolean; durationMs: number } |
Endpoints
"Access" is the scope an API token needs, or signed-in user (with the lowest role) for endpoints that only take a session. "(and the admin role)" means the token also needs a scope that acts as admin, such as bots:write. The server's /api/docs has the request and response shapes.
Sign-in and setup
| Method | Path | Access | |
|---|---|---|---|
GET | /api/v1/setup | Whether first-run setup is needed, and how people can sign in | public |
POST | /api/v1/setup | Create the first owner account and workspace | public |
POST | /api/v1/auth/login | Sign in with email and password (sets a session cookie) | public |
POST | /api/v1/auth/logout | Sign out | signed-in user |
GET | /api/v1/me | The signed-in user, workspace, CSRF token and this server | signed-in user |
POST | /api/v1/me/password | Change your password | signed-in user (viewer) |
GET | /api/v1/invites/{token} | What an invitation is for | public |
POST | /api/v1/invites/{token}/accept | Join a workspace | public |
Providers
| Method | Path | Access | |
|---|---|---|---|
GET | /api/v1/providers/catalog | The services that can be connected | any token or signed-in user |
GET | /api/v1/providers | Connected providers | bots:read |
POST | /api/v1/providers | Connect a provider with an API key (checked before it is saved) | providers:write |
PATCH | /api/v1/providers/{id} | Rename a connection or replace its key | providers:write |
DELETE | /api/v1/providers/{id} | Remove a connection | providers:write |
POST | /api/v1/providers/{id}/test | Check the key again | providers:write |
GET | /api/v1/providers/{id}/models | Models this key can use (cached for a day; refresh=1 to re-ask) | bots:read |
GET | /api/v1/providers/{id}/voices | Voices this key can use, with preview URLs | bots:read |
GET | /api/v1/providers/{id}/voices/{voiceId}/preview | A short audio sample of a voice | bots:read |
Bots
| Method | Path | Access | |
|---|---|---|---|
GET | /api/v1/bots/templates | Starting points for new bots | bots:read |
GET | /api/v1/bots | All bots | bots:read |
POST | /api/v1/bots | Create a bot (as a draft) | bots:write |
GET | /api/v1/bots/{id} | A bot: its draft and published config | bots:read |
PATCH | /api/v1/bots/{id} | Edit the draft: a deep patch, or a whole config | bots:write |
DELETE | /api/v1/bots/{id} | Delete a bot | bots:write |
POST | /api/v1/bots/{id}/duplicate | Copy a bot | bots:write |
POST | /api/v1/bots/{id}/publish | Publish the draft (live widgets switch to it) | bots:write |
GET | /api/v1/bots/{id}/versions | Published versions | bots:read |
POST | /api/v1/bots/{id}/versions/{version}/restore | Copy a version into the draft | bots:write |
POST | /api/v1/bots/{id}/status | Pause or resume a bot | bots:write |
POST | /api/v1/bots/{id}/identity-secret | Make a new identity-verification secret (shown once) | bots:write |
POST | /api/v1/bots/{id}/preview | A token to preview the draft in a widget | bots:read |
GET | /api/v1/bots/{id}/embed | What the embed snippets need | bots:read |
Custom faces
| Method | Path | Access | |
|---|---|---|---|
GET | /api/v1/faces | Custom faces made from photos | bots:read |
POST | /api/v1/faces | Upload a face (image + 468 landmarks from skinFromPhoto) | bots:write |
DELETE | /api/v1/faces/{id} | Delete a custom face | bots:write |
Conversations
| Method | Path | Access | |
|---|---|---|---|
GET | /api/v1/conversations | Conversations, newest first (pass nextBefore as before for the next page) | conversations:read |
GET | /api/v1/conversations/{id} | A transcript with tool calls and attachments | conversations:read |
GET | /api/v1/conversations/{id}/export | Download a transcript | conversations:read |
POST | /api/v1/conversations/{id}/tags | Set tags | conversations:write |
POST | /api/v1/conversations/{id}/close | Close a conversation | conversations:write |
DELETE | /api/v1/conversations/{id} | Delete a conversation and its files | conversations:write (and the admin role) |
GET | /api/v1/attachments/{id} | An image from a conversation | conversations:read |
POST | /api/v1/conversations/{id}/takeover | Take over a conversation (the AI goes quiet) | signed-in user (agent) |
POST | /api/v1/conversations/{id}/messages | Reply as yourself (or add an internal note) | signed-in user (agent) |
POST | /api/v1/conversations/{id}/typing | Show the visitor you are typing | signed-in user (agent) |
POST | /api/v1/conversations/{id}/release | Hand the conversation back to the AI | signed-in user (agent) |
Leads
| Method | Path | Access | |
|---|---|---|---|
GET | /api/v1/leads | Leads, newest first | leads:read |
GET | /api/v1/leads.csv | Leads as CSV | leads:read |
POST | /api/v1/leads | Add a lead (from your own systems) | leads:write |
PATCH | /api/v1/leads/{id} | Change a lead's status | leads:write |
DELETE | /api/v1/leads/{id} | Delete a lead | leads:write (and the admin role) |
Tools, MCP servers and secrets
| Method | Path | Access | |
|---|---|---|---|
GET | /api/v1/tools/http | HTTP tools | bots:read |
POST | /api/v1/tools/http | Add an HTTP tool | tools:write |
PATCH | /api/v1/tools/http/{id} | Change an HTTP tool | tools:write |
DELETE | /api/v1/tools/http/{id} | Delete an HTTP tool | tools:write |
POST | /api/v1/tools/http/{id}/test | Call an HTTP tool with sample arguments | tools:write |
GET | /api/v1/tools/mcp | MCP servers and their tools | bots:read |
POST | /api/v1/tools/mcp | Connect an MCP server (its tools are listed straight away) | tools:write |
PATCH | /api/v1/tools/mcp/{id} | Change an MCP server (headers and env replace the old ones unless keepHeaders/keepEnv) | tools:write |
POST | /api/v1/tools/mcp/{id}/refresh | List the server's tools again | tools:write |
POST | /api/v1/tools/mcp/{id}/test | Call one of the server's tools | tools:write |
DELETE | /api/v1/tools/mcp/{id} | Remove an MCP server | tools:write |
GET | /api/v1/secrets | Secret names (values are never shown) | tools:write |
PUT | /api/v1/secrets/{name} | Set a secret | tools:write |
DELETE | /api/v1/secrets/{id} | Delete a secret | tools:write |
Knowledge
| Method | Path | Access | |
|---|---|---|---|
GET | /api/v1/knowledge | Knowledge sources | bots:read |
POST | /api/v1/knowledge | Add a source: text, a page, a sitemap, or a site to crawl (files: POST /knowledge/files) | knowledge:write |
POST | /api/v1/knowledge/files | Upload a document (PDF, Markdown, text or HTML) | knowledge:write |
PATCH | /api/v1/knowledge/{id} | Change a source | knowledge:write |
POST | /api/v1/knowledge/{id}/reindex | Fetch and index again | knowledge:write |
DELETE | /api/v1/knowledge/{id} | Remove a source | knowledge:write |
GET | /api/v1/knowledge/{id}/documents | The pages or files a source has | bots:read |
POST | /api/v1/knowledge/search | Try a search (what the agent would find) | bots:read |
GET | /api/v1/knowledge/gaps | Questions the knowledge base couldn't answer | analytics:read |
Analytics
| Method | Path | Access | |
|---|---|---|---|
GET | /api/v1/analytics/summary | Totals for a period (default: the last 30 days) | analytics:read |
GET | /api/v1/analytics/timeseries | Per day | analytics:read |
GET | /api/v1/analytics/tools | Tool use | analytics:read |
GET | /api/v1/analytics/usage | Provider usage and estimated cost | analytics:read |
Webhooks
| Method | Path | Access | |
|---|---|---|---|
GET | /api/v1/webhooks | Webhook endpoints | webhooks:write |
POST | /api/v1/webhooks | Add an endpoint (the signing secret is shown once) | webhooks:write |
PATCH | /api/v1/webhooks/{id} | Change an endpoint | webhooks:write |
POST | /api/v1/webhooks/{id}/secret | A new signing secret | webhooks:write |
POST | /api/v1/webhooks/{id}/test | Send a test event now | webhooks:write |
GET | /api/v1/webhooks/{id}/deliveries | Recent deliveries | webhooks:write |
POST | /api/v1/webhooks/deliveries/{id}/redeliver | Send a delivery again | webhooks:write |
DELETE | /api/v1/webhooks/{id} | Remove an endpoint | webhooks:write |
Workspace, team and API tokens
| Method | Path | Access | |
|---|---|---|---|
GET | /api/v1/tokens | API tokens | signed-in user (admin) |
POST | /api/v1/tokens | Create an API token (shown once) | signed-in user (admin) |
DELETE | /api/v1/tokens/{id} | Revoke a token | signed-in user (admin) |
GET | /api/v1/members | People in the workspace, and open invitations | signed-in user (viewer) |
POST | /api/v1/members/invite | Invite someone (returns the link to send them) | signed-in user (admin) |
DELETE | /api/v1/members/invites/{id} | Cancel an invitation | signed-in user (admin) |
PATCH | /api/v1/members/{id} | Change a role | signed-in user (admin) |
DELETE | /api/v1/members/{id} | Remove someone | signed-in user (admin) |
PUT | /api/v1/me/presence | Online, away or offline (for handoffs) | signed-in user (agent) |
GET | /api/v1/settings | Workspace settings | settings:read |
PATCH | /api/v1/settings | Change workspace settings | signed-in user (admin) |
DELETE | /api/v1/workspace | Delete this hosted workspace and everything in it | signed-in user (owner) |
GET | /api/v1/audit | Who did what | signed-in user (admin) |
Visitors (data requests)
| Method | Path | Access | |
|---|---|---|---|
GET | /api/v1/visitors | Find visitors by email or your user id | privacy:write |
GET | /api/v1/visitors/{id}/export | Everything stored about a visitor (data request) | privacy:write |
DELETE | /api/v1/visitors/{id} | Erase a visitor and their conversations | privacy:write |