— Docs · API Reference

THE 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.

← 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: 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"
}

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: 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.

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/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?


Keep exploring