API Reference.
saavos has two API surfaces. The embed widget API (/api/chat) streams responses via NDJSON and is used by the JavaScript widget on your site. The REST API (/api/v1/*) is a JSON API for server-side integration, authenticated with API keys you generate in your dashboard settings.
Chat in 10 lines.
The REST API works with any HTTP client — no SDK required. Generate an API key in Dashboard → Settings → API keys and send your first message:
const res = await fetch('https://saavos.com/api/v1/bots/my-bot-slug/chat', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk-...',
'Content-Type': 'application/json',
},
body: JSON.stringify({ message: 'What is your return policy?' }),
});
const { reply, conversationId } = await res.json();
console.log(reply);List your bots
fetch('/api/v1/bots', { headers: { Authorization: 'Bearer sk-...' } }) — returns [{ id, name, slug, status, createdAt }]
Conversation history
GET /api/v1/bots/[slug]/conversations — returns { conversations: [{id, createdAt, messageCount, visitorEmail}], nextCursor }. Paginate with ?limit=20&cursor=....
JavaScript SDK (coming soon)
A typed SDK is in development. Email founder@5minbot.com for early access.
Two auth models.
Embed widget (/api/chat)
Uses short-lived HMAC-signed session tokens. The widget fetches a token automatically on mount. If you are integrating the widget directly, call POST /api/chat/session first, then send messages. No API key required.
REST API (/api/v1/*)
Uses API keys. Generate a key in Dashboard Settings API keys. Pass it in every request as Authorization: Bearer <key>. Keys are scoped to your account and can list and chat with your bots only.
API key format: a random 43-character base64url string. The full key is shown only once when created — store it in your secret manager. Only a SHA-256 hash is stored server-side; we cannot recover a lost key.
GET /api/v1/bots
Authentication
Authorization: Bearer <key> — required.
Purpose
Returns the list of bots owned by the API key holder. Only bots that belong to your account are returned.
Response
[{ "id": "uuid", "name": "...", "slug": "...", "status": "active|draft|...", "createdAt": "ISO8601" }]
Rate limit
100 requests per minute per key.
Error codes
401 missing or invalid key. 429 rate limit exceeded.
curl https://saavos.com/api/v1/bots \
-H "Authorization: Bearer <your-api-key>"Response: 200 OK
[
{
"id": "b1e2d3c4-...",
"name": "Acme Support",
"slug": "acme-support-a1b2c3",
"status": "active",
"createdAt": "2026-05-01T10:00:00.000Z"
}
]POST /api/v1/bots/[slug]/chat
Authentication
Authorization: Bearer <key> — required. The bot slug must belong to the key owner.
Purpose
Send a message to one of your bots and get a complete response as a JSON object. Synchronous — no streaming. Conversation history is maintained per key per bot automatically.
Request body
message (string, 1–4000 chars, required).
Response
{ "reply": "...", "conversationId": "uuid", "tokensIn": N, "tokensOut": N }
Rate limit
100 requests per minute per key.
Error codes
400 invalid payload · 401 invalid key · 404 bot not found or not active · 429 rate limit or plan message limit · 502 upstream model error
curl -X POST https://saavos.com/api/v1/bots/acme-support-a1b2c3/chat \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-d '{"message":"What is your return policy?"}'Response: 200 OK
{
"reply": "Our return policy allows 30 days for any unused item.",
"conversationId": "01HX...",
"tokensIn": 312,
"tokensOut": 44
}GET /api/v1/bots/[slug]/conversations
Authentication
Authorization: Bearer <key> — required. The bot slug must belong to the key owner.
Purpose
Returns paginated conversation history for a bot. Each conversation includes its ID, start time, total message count, and visitor email (if captured via lead form).
Query params
limit (1–100, default 20) · cursor (opaque string from the previous response's nextCursor field).
Response
{ "conversations": [{ "id": "uuid", "createdAt": "ISO8601", "messageCount": N, "visitorEmail": "..." }], "nextCursor": "..." }. nextCursor is null when there are no more pages.
Rate limit
100 requests per minute per key.
curl "https://saavos.com/api/v1/bots/acme-support-a1b2c3/conversations?limit=20" \
-H "Authorization: Bearer <your-api-key>"Response: 200 OK
{
"conversations": [
{
"id": "01HX...",
"createdAt": "2026-05-17T14:32:00.000Z",
"messageCount": 8,
"visitorEmail": "visitor@example.com"
}
],
"nextCursor": "MjAyNi0wNS0xN1QxNDozMjowMC4wMDBa"
}Programmatic content management.
Three dashboard routes now accept Authorization: Bearer <key> as an alternative to a browser session. Useful for CI pipelines, monitoring scripts, or any server-side automation that needs to inspect or update bot state without a logged-in user.
GET /api/bots/[id]/status
Returns ingestion progress: { "pages": N, "chunks": N, "status": "crawling|ready|failed" }. Poll this after adding sources to know when training is complete.
GET /api/bots/[id]/sources/export
Downloads a CSV of all sources for the bot (kind, title, source_url, status, created_at). Useful for auditing or mirroring your knowledge base.
GET /api/bots/[id]/theme
Returns the full widget theme object (colors, font, position, etc.). Read-only — for writes use PATCH /api/bots/[id]/theme (paid plans only).
Authentication
Pass Authorization: Bearer <key> to authenticate with an API key, or omit the header and use dashboard session cookies as usual. The bot [id] must belong to the key owner.
Rate limit
100 requests per minute per key or per owner.
# Check training status
curl https://saavos.com/api/bots/<bot-id>/status \
-H "Authorization: Bearer <your-api-key>"
# → {"pages":12,"chunks":340,"status":"ready"}
# Export sources as CSV
curl https://saavos.com/api/bots/<bot-id>/sources/export \
-H "Authorization: Bearer <your-api-key>" \
--output sources.csv
# Read widget theme
curl https://saavos.com/api/bots/<bot-id>/theme \
-H "Authorization: Bearer <your-api-key>"
# → {"primaryColor":"#7c3aed","fontFamily":"inherit",...}API versioning.
All REST API routes are namespaced under /api/v1/. When a breaking change is required, a new version is introduced alongside the old one. Deprecations are announced in the changelog with at least 90 days notice before removal.
The embed widget endpoints (/api/chat and /api/chat/session) are not versioned — they are stable and used by the widget script which auto-updates.
POST /api/chat/session
Authentication
None. Public endpoint.
Purpose
Issues a session token for a specific bot. The token is an HMAC-SHA256 signed payload containing the bot slug, a session ID, and a 24-hour expiry. Pass it to every subsequent chat request.
Request body
{ "botSlug": "your-bot-slug" }
Response
{ "token": "...", "sessionId": "...", "botName": "...", "fallbackMessage": "..." }
Error codes
400 invalid payload. 404 bot not found or not active.
curl -X POST https://saavos.com/api/chat/session \
-H "Content-Type: application/json" \
-d '{"botSlug":"your-bot-slug"}'Response: 200 OK
{
"token": "eyJ...",
"sessionId": "01HX...",
"botName": "Acme Support",
"fallbackMessage": "I don't have that information — reach us at hello@acme.com."
}POST /api/chat
Authentication
HMAC session token in the request body. Tokens expire after 24 hours.
Runtime
Node.js. Maximum execution time: 60 seconds.
Request body
token (string, required) — from the session endpoint.message (string, 1–4000 chars, required).history (array, max 20 turns, optional) — previous turns in the format [{ "role": "user"|"assistant", "content": "..." }].
Response format
200 streaming application/x-ndjson. Each line is a JSON object terminated by \n. The stream is complete when a done or error event arrives.
Error codes
400 invalid payload · 401 invalid or expired token · 404 bot not active · 429 rate limit (max 30 messages per session or less than 1 second between requests)
curl -N -X POST https://saavos.com/api/chat \
-H "Content-Type: application/json" \
-d '{
"token": "eyJ...",
"message": "What is your return policy?",
"history": []
}'Streaming NDJSON response (one JSON object per line):
{"type":"start","conversation_id":"01HX...","message_id":"01HX..."}
{"type":"token","content":"Our return policy "}
{"type":"token","content":"allows 30 days "}
{"type":"token","content":"for any unused item."}
{"type":"citations","sources":[{"ref":1,"id":"src_abc","name":"Return Policy","url":"https://acme.com/returns"}]}
{"type":"done","tokens_in":312,"tokens_out":44}NDJSON event types.
start
Emitted once, before any tokens. Contains conversation_id and message_id for deduplication on your side.
token
One chunk of the model output. Concatenate all token.content values in order to reconstruct the full response.
citations
Emitted once, after all tokens. Contains the source documents used in the response. Each source has ref (1-indexed), id, name, and url (nullable for text sources).
done
Stream complete. Contains tokens_in and tokens_out for metering. After this event, the connection closes.
error
Emitted instead of done when something goes wrong mid-stream. Contains code and message. This can arrive after some token events — your reader must handle partial output.
Rate limits.
Rate limits differ by API surface:
REST API (/api/v1/*)
100 requests per minute per API key. Exceeded requests return 429 with a Retry-After header in seconds.
Widget API — maximum messages per session
30 messages. After that, the session is exhausted and you need a new one.
Widget API — minimum interval between messages
1 second. Requests within 1 second of the previous one return 429.
Plan message caps (all APIs)
If the bot owner's monthly message quota is exhausted, the widget API returns a 200 with a plan-limit message in the stream. The REST API returns 429 with error code plan_limit.
Message length limit
4,000 characters per message. Exceeding this returns 400.
Cross-origin access.
Both /api/chat/session and /api/chat include Access-Control-Allow-Origin: * and handle OPTIONS preflight requests. The /api/v1/* REST endpoints are server-to-server and do not send CORS headers.
Integration notes.
Session token expiry
Tokens expire after 24 hours. If your session token expires mid-session, call /api/chat/session again to get a new one. The sessionId you receive can be reused if you want conversation continuity — pass the same history array.
Reading the stream
Use a streaming fetch with a line-by-line reader. Each \n-terminated line is a complete JSON object. Do not try to buffer the entire response before parsing — the stream can stay open for up to 60 seconds on long responses.
History window
Pass the last 10–20 turns for best context continuity. More than 20 turns are ignored server-side to keep prompt sizes bounded. Older context is retrieved from the vector index, not the history array.
REST API conversation history
POST /api/v1/bots/[slug]/chat maintains a conversation per key per bot automatically — each successive call continues the same thread. Token usage counts toward your plan's monthly message limit.
Last updated 2026-05-13 · Was this helpful?