Chat and sessions
How threads are stored, how end users are separated, and how retrieval picks passages.
Every question runs against one workspace, for one of your end users, inside a thread the server stores.
Sessions
A session is a thread. Omit session_id and the server creates one, titling it from your first message; pass it back and the conversation continues with the last twenty turns as context.
Sessions are stored server-side and survive anything on your end. You do not need to keep transcripts yourself — though you may want to, since the list endpoints are the only way to read them back.
curl -X POST "https://api.daneshyar.info/api/v1/workspaces/8f14e45f-ceea-467a-9f4c-1a2b3c4d5e6f/chat/" \
-H "X-API-Key: dk_live_9f3aC2xQ7mB1vT8sE4nK6pR0jY5wZ2hL" \
-H "Content-Type: application/json" \
-d '{
"external_user_id": "user_4821",
"session_id": "b6d2e1a4-77c3-4f59-9a10-2e8d6c4b0f37",
"message": "And for patients over 70?"
}'Separating your end users
external_user_id is your identifier for whoever is asking. It is required on every call, and it is what keeps your end users apart despite sharing one API key.
Use a stable identifier from your own system — a user id, not an email or a session cookie. If it changes, that person loses their history. If two people share one, they see each other's.
How retrieval works
When a question arrives, the server embeds it and finds the closest passages across the workspace's ready resources by vector similarity, then answers from those passages plus recent conversation history.
- By default it retrieves the five closest passages across the whole workspace.
- Pass resource_ids and retrieval is both forced and widened — about fifteen passages, plus a few from each named resource so a document is represented even when it isn't the closest match.
- A workspace with no ready resources answers from general knowledge instead, without citations. That is the one case where an answer is not grounded in your documents.
When the answer is a form
Sometimes the model needs input before it can answer usefully. Instead of guessing, it returns response.type of form with a set of fields to collect.
{
"status": "ok",
"data": {
"assistant_message": {
"role": "assistant",
"content": "Which department should I answer for?",
"response": {
"type": "form",
"parts": [{ "text": "Which department should I answer for?", "source": null }],
"form": {
"title": "Narrow the question",
"fields": [
{
"name": "department",
"label": "Department",
"type": "select",
"required": true,
"options": ["Cardiology", "Neurology", "Oncology"]
}
]
}
}
}
}
}Each field has a name, a label, a type and a required flag, and may carry a placeholder and options. The types are text, textarea, number, select, checkbox, radio, date and email.
One subtlety: a checkbox field with options is a multi-select group, and the same field without options is a single yes/no toggle. Branch on whether options is present.