Skip to main content

Chat API

Send a user message to a QSC Agent and get back a structured response (Markdown text, product data, or [[QSCACTION]] buttons).

Calls are handled by the Agent Server. You need an agent configuration in QSC Admin — its tenant and code become the path parameters below. How to set up MCP servers, LLM models, and the system prompt is covered there. For the ready-made web widget, see Chatbot.

tip

To try the endpoints, use the Postman collection (folder 4 – Chat — import steps in the Overview). For the API reference, see the Open API Explorer.

Two endpoints are available:

EndpointDescription
ChatSingle request/response turn
Streaming ChatSame turn, streamed as newline-delimited JSON

Authentication

Pass your static API token in the X-QSC-Token header — same header name as the other APIs, different token:

X-QSC-Token: <your-token>

This is the agent-server AUTH_TOKEN — separate from the per-tenant admin and feeding tokens described in Authentication. If it's missing or doesn't match, the request is rejected with 401.

Chat

POST /api/v1/agent/chat/{tenant}/{code}
Path ParameterDescription
tenantTenant code of the agent configuration (e.g. demo)
codeThe agent config code (e.g. support-bot)

Request

Starting a conversation

Send type: "init" with no text as the first call. This resets the agent's runtime state for the session and returns the configured welcome_message plus the list of selectable models — no LLM call is made.

You may pass a sessionId (e.g. one you generated client-side or from a previous visit) or omit it — if omitted, the server generates one and returns it as session_id.

{
"type": "init",
"sessionId": "3f8c9e2a-1b4d-4b2e-9c3a-6e7d8f1a2b3c"
}

Sending a message

{
"text": "What running shoes do you have under 100 euros?",
"sessionId": "3f8c9e2a-1b4d-4b2e-9c3a-6e7d8f1a2b3c",
"userId": "user-42",
"id": "msg-1",
"model": "haystack:eu.anthropic.claude-opus-4-8"
}
PropertyTypeRequiredDescription
textstringYes (unless init or attachments is non-empty)The user's message. Not sent when type is init.
typestringNoSet to "init" to start/reset a conversation and receive the configured welcome message instead of running the agent.
sessionIdstringNoConversation/session identifier. Optional on init (server generates one if omitted). On later calls, send back the session_id from the previous response to keep history.
userIdstringNoCaller-supplied end-user identifier, forwarded for tracking.
idstringNoClient-supplied message id, echoed back in the response.
modelstringNoOverrides the default LLM from the agent config for this request (must be one of the config's llm_models).
attachmentsarrayNoImages or files to send alongside the message — see Sending attachments.

Sending attachments

Attach images or files to a message by adding an attachments array. Each entry is sent inline as base64 — there's no separate upload endpoint or multipart support.

{
"text": "What's in this image?",
"sessionId": "3f8c9e2a-1b4d-4b2e-9c3a-6e7d8f1a2b3c",
"id": "msg-2",
"model": "haystack:eu.anthropic.claude-opus-4-8",
"attachments": [
{
"name": "shoe.jpg",
"mimeType": "image/jpeg",
"data": "/9j/4AAQSkZJRgABAQAAAQABAAD..."
}
]
}
PropertyTypeRequiredDescription
namestringNoOriginal filename. Used for file attachments; ignored for images.
mimeTypestringNoMIME type (e.g. image/png, application/pdf). Any image/* type is sent to the model as an image; everything else is sent as a generic file. Recommended — if omitted, the server guesses it from the bytes.
datastringYesBase64-encoded file content. Must be non-empty, valid base64, and under ~11 MB decoded.
note

Attachments only work with haystack: models — see Agent Server for how llm_models and its haystack:/openai: prefixes are configured. Sending attachments with an openai: model returns 400. Whether the model actually uses an attachment also depends on the underlying model supporting multimodal input (e.g. a vision-capable model for images) — that's a model capability, not something the endpoint can guarantee.

Attachments apply only to the turn that sends them — conversation history replays prior text, not prior attachments, so resend them if a later message still needs to reference the same file.

Response

{
"type": "markdown",
"data": "Here are a few options under €100:\n\n1. **Trail Boots X** — €89.99\n2. **City Sneaker** — €59.99",
"id": "msg-1",
"date": 1753088400000,
"session_id": "3f8c9e2a-1b4d-4b2e-9c3a-6e7d8f1a2b3c",
"models": [
{ "model": "haystack:eu.anthropic.claude-opus-4-8", "label": "Claude Opus 4.8", "selected": true },
{ "model": "haystack:eu.anthropic.claude-sonnet-4-5", "label": "Claude Sonnet 4.5", "selected": null }
]
}
PropertyTypeDescription
typestringRender hint for the client, e.g. markdown or broadcast
datastringThe agent's reply (Markdown, may include [[QSCACTION]] tokens)
idstring | nullEchoes the request's id
dateintegerResponse timestamp, epoch milliseconds
session_idstring | nullThe session id — save this and send it back on the next call
modelsarrayAvailable models from the agent config, with the active one flagged "selected": true

Streaming Chat

POST /api/v1/agent/chat/streaming/{tenant}/{code}

Same path parameters and request body as Chat. The response is Content-Type: application/json but the body is a stream of newline-delimited JSON objects, not a single JSON document — read and parse it line by line.

  • Zero or more delta lines, emitted as the agent generates content:

    { "data": "Here are a few", "streaming": true }
    { "data": " options under €100:", "streaming": true }
  • Exactly one final line, a full Chat response object with "streaming": false, carrying the resolved session_id, id, and models:

    { "type": "markdown", "data": "", "id": "msg-1", "date": 1753088400000, "session_id": "3f8c9e2a-1b4d-4b2e-9c3a-6e7d8f1a2b3c", "models": [...], "streaming": false }

Concatenate the data of all delta lines to get the full reply; the final line carries no additional content (data is empty) but confirms the turn is complete and supplies the metadata. If the agent fails mid-stream, one delta line with an error message is emitted, followed by a final line with "type": "broadcast".

tip

type: "init" also works on the streaming endpoint and returns the welcome message as a single streamed turn.

Status Codes

CodeDescription
200Request processed successfully
400Invalid request body (e.g. missing text/attachments when type is not init, invalid attachment base64, or attachments sent with a non-haystack: model)
401Missing or invalid token
403Token rejected by the config service
404Agent configuration not found for tenant/code
500Upstream/LLM provider error