← Docs / API Reference
Quickstart — 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.
Authentication — 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:
401missing or invalid key.429rate 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:
400invalid payload ·401invalid key ·404bot not found or not active ·429rate limit or plan message limit ·502upstream 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'snextCursorfield). - Response:
{ "conversations": [{ "id": "uuid", "createdAt": "ISO8601", "messageCount": N, "visitorEmail": "..." }], "nextCursor": "..." }.nextCursorisnullwhen 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"
}REST API — 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:
400invalid payload.404bot 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:
200streamingapplication/x-ndjson. Each line is a JSON object terminated by\n. The stream is complete when adoneorerrorevent arrives. - Error codes:
400invalid payload ·401invalid or expired token ·404bot not active ·429rate 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. Containsconversation_idandmessage_idfor deduplication on your side.token— One chunk of the model output. Concatenate alltoken.contentvalues in order to reconstruct the full response.citations— Emitted once, after all tokens. Contains the source documents used in the response. Each source hasref(1-indexed),id,name, andurl(nullable for text sources).done— Stream complete. Containstokens_inandtokens_outfor metering. After this event, the connection closes.error— Emitted instead ofdonewhen something goes wrong mid-stream. Containscodeandmessage. This can arrive after sometokenevents — 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-Afterheader 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.
CORS
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/sessionagain to get a new one. ThesessionIdyou 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]/chatmaintains 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?