Skip to main content

Conversations API

Send messages to the AI assistant and receive contextual responses.

Widget users

If you're using the Embeddable Widget, the widget handles conversations automatically. It uses dedicated widget endpoints (POST /widget/message) instead of the endpoints below. You don't need to call these endpoints directly. The widget's built-in UI manages the full conversation flow.

Send a message​

POST /assistant/message
Authorization: Bearer <token>
Content-Type: application/json

{
"message": "What should I enter in the account number field?",
"context": {
"url": "https://app.example.com/registration",
"title": "Registration Form",
"forms": [
{
"id": "reg-form",
"fields": [
{
"name": "account_number",
"type": "text",
"label": "Account Number",
"required": true,
"placeholder": "Enter your 10-digit account number"
}
]
}
]
},
"language": "en",
"conversation_id": 5231
}

conversation_id is optional: omit it and the turn resolves or creates the thread for that site. The response carries the assistant's answer along with the conversation_id the turn was recorded under, so a client that omitted it knows where the next turn belongs. POST /assistant/message/stream is the streamed variant of the same call.

The organization the turn runs in is the authenticated caller's, never a field in the body. If the page named in context.url is a registered domain belonging to a different organization, the turn is refused rather than served from that organization's configuration.

Conversation history​

The history routes are served under /user, not alongside the message endpoint above.

List a user's conversations​

GET /user/conversations
Authorization: Bearer <token>

Query parameters:

ParameterDescription
domainExact hostname match. The extension panel passes the hostname of the page the user is on and expects that thread list and no other
domain_searchCase-insensitive substring match, for a search box. shop finds shop.example.com
start_date, end_dateBounds on the last message
limitPage size, default 50

The response carries conversations, plus total and active_domains computed over the filters and ignoring limit, so a count shown beside a page never describes a different set than the page.

Each conversation carries a conversation_key, which is its id as a string. That is the value the detail, update and delete routes take. It used to be a user|domain|ordinal composite, and between April and September 2026 the list emitted a primary key in the ordinal position, so every thread opened from My Account came back empty. A client holding an old composite key still resolves, but nothing mints them any more.

Read one conversation​

GET /user/conversations/:id
Authorization: Bearer <token>

Returns { conversation, messages }. Ownership is checked against the token.

Delete a conversation​

DELETE /user/conversations/:id
Authorization: Bearer <token>

Hard delete of the conversation and every turn in it, including turns that were written without a parent conversation and would survive a cascade. DELETE /user/conversations does the same for the caller's whole history. Both answer 200 with a JSON body rather than 204, because the clients parse the response and an empty body made a successful delete look like a failure.

Export​

POST /user/export
Authorization: Bearer <token>

Returns everything the account holds, conversations and their turns included, with a manifest naming each section, its row count and anything withheld. Two things are withheld on purpose because they carry other people's data: request log bodies, and the stored snapshot of the page a turn ran on.

Context object​

The context object tells the AI about the user's current page. The more context you provide, the better the answers:

interface PageContext {
url: string;
title: string;
forms?: FormContext[];
selectedText?: string;
pageStructure?: {
headings: string[];
navigation: string[];
};
}

interface FormContext {
id?: string;
action?: string;
method?: string;
fields: FieldContext[];
}

interface FieldContext {
name: string;
type: string;
label?: string;
placeholder?: string;
required?: boolean;
options?: string[];
value?: string;
}
warning

Avoid sending actual field values in the context unless necessary. Use field metadata (labels, types, placeholders) for context. If you must send values, ensure PII masking is enabled.