Talk to an agent from your own application
Two endpoints: one starts a conversation, one continues it. Both stream the reply back as server-sent events, so you can render the answer as it is written rather than waiting for it.
No API key is involved. An agent's id is its capability — anyone holding it can talk to that agent, exactly as a visitor to your website can. Treat it as public, and pause the agent from its Publish page when you want it to stop answering. Your agent id is on that same page.
Start a conversation
POST /api/v1/chats/with-message
Creates the conversation and answers the first message in one call. The response is an SSE stream, not JSON — the first event carries the chat_id you need in order to continue.
| content | string, required | The visitor's message. |
| chatbot_id | uuid | Which agent answers. Without it the request is not attached to any agent. |
| title | string | A label for the conversation in your dashboard. Generated for you if omitted. |
| page_context | object | Where the visitor is on your site, so the agent can take the current page into account. |
curl -N -X POST https://chatly.analytos.ai/api/v1/chats/with-message \
-H "Content-Type: application/json" \
-d '{"chatbot_id": "YOUR_AGENT_ID", "content": "What are your opening hours?"}'-N matters: without it curl buffers the response and the stream looks like it arrives all at once at the end.
Continue a conversation
POST /api/v1/chats/{chat_id}/messages/stream
Sends the next message in an existing conversation. Use the chat_id from the chat_created event. Earlier turns are already in the agent's context — send only the new message.
curl -N -X POST https://chatly.analytos.ai/api/v1/chats/CHAT_ID/messages/stream \
-H "Content-Type: application/json" \
-d '{"content": "And on weekends?"}'Stream events
Every line is data: followed by one JSON object with a type. Read token and done; the rest are there if you want to show progress, and can be ignored safely. Treat an unknown type as something to skip — more may be added.
| chat_created | chat_id | First event of a new conversation. Keep this id. |
| token | content | One piece of the answer. Append them in order to build the reply. |
| sources | sources[] | Which knowledge documents the answer drew on. |
| status | step | What the agent is doing — for example classifying_intent. |
| tool_status | label | A human-readable note that a connected app is being used. |
| tool_calls | calls[] | Which tools ran, with the arguments they were given. |
| intent | intent, confidence | How the question was classified. |
| chart | spec | A chart to render, when the agent answered with data. |
| generated_image | image_url | An image the agent produced. |
| done | message_id, chat_title | The answer is complete. Stop reading. |
| error | message | The turn failed. No done follows. |
data: {"type": "chat_created", "chat_id": "4cff03c1-..."}
data: {"type": "status", "step": "classifying_intent"}
data: {"type": "sources", "sources": [{"title": "handbook.md"}]}
data: {"type": "token", "content": "We are open "}
data: {"type": "token", "content": "9 to 5, Monday to Friday."}
data: {"type": "done", "message_id": "8f1c...", "chat_title": "Opening hours"}Reading the stream in JavaScript:
const res = await fetch("https://chatly.analytos.ai/api/v1/chats/with-message", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ chatbot_id: AGENT_ID, content: "Hello" }),
})
const reader = res.body.getReader()
const decoder = new TextDecoder()
let buffer = ""
let answer = ""
while (true) {
const { value, done } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
// Events are newline-delimited; the last piece may be incomplete.
const lines = buffer.split("\n")
buffer = lines.pop() ?? ""
for (const line of lines) {
if (!line.startsWith("data: ")) continue
const event = JSON.parse(line.slice(6))
if (event.type === "token") answer += event.content
if (event.type === "done") return answer
if (event.type === "error") throw new Error(event.message)
}
}Agent appearance
GET /api/v1/chatbots/{agent_id}/widget-config
The agent's public presentation — display name, greeting, avatar, colours and which features are switched on. Use it to make your own interface match what you set under Appearance. It returns only what a visitor is meant to see; instructions, knowledge and credentials are never included.
curl https://chatly.analytos.ai/api/v1/chatbots/YOUR_AGENT_ID/widget-configErrors
Failures before the stream starts are ordinary JSON with a detail. Once the stream has started, a failure arrives as an error event instead, and no done follows it.
| 403 | Forbidden | The agent is paused. It answers nobody until it is activated again from its Publish page. |
| 404 | Not found | No such agent or conversation — or it was deleted. |
| 422 | Unprocessable | The body is missing something, usually content. |
| 500 | Server error | The turn failed. Retrying is safe; each request creates its own turn. |
Embedding the widget
If you want the ready-made chat bubble rather than your own interface, you do not need this API at all. Paste the snippet from your agent's Publish page:
<script
src="https://chatly.analytos.ai/chatly-widget.js"
data-chatbot-id="YOUR_AGENT_ID"
defer
></script>The widget handles the streaming, the appearance settings and the conversation history for you.
What this API does not do
These two endpoints cover talking to an agent. Creating agents, uploading knowledge, reading conversations and managing connected apps all require a signed-in session and are not part of this public surface — they are done from the dashboard.
An agent only uses connected accounts such as Gmail on behalf of the person who connected them, in the dashboard. A conversation started through this API is an anonymous visitor and never reaches anyone's personal connections.