SupportBot
Updated 2026-04-26

Developer Docs

HTTP API + drop-in chat widget. Same KB, same actions, same case-creation as the messengers — exposed for any code that can speak HTTPS.

Authentication

Every request to the API carries one bearer token in the Authorization header. The token is issued per group from Dashboard → API → Reveal. Rotating it from the same panel invalidates the previous value immediately — there is no grace period, so update your deploys before clicking Rotate.

Authorization: Bearer sb_a4f93b27c2e1d508f12c66e9b0a7d4c5

Treat the key like a password. Server-to-server calls can use it directly. Browser code embeds it via the chat widget snippet, which scopes it to the host origin you configured in the dashboard.

POST /v1/chat

The single entry point. Accepts a chat history + the latest user message and returns the bot reply, plus an optional case_id when the conversation produced a case worth keeping.

POSThttps://api.supportbot.info/v1/chat

curl https://api.supportbot.info/v1/chat \
  -H "Authorization: Bearer sb_a4f9..." \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [
      {"role": "user", "content": "How do I reset my 2FA?"}
    ],
    "session_id": "user-7421",
    "end_user_id": "acme_user_7421",
    "private": false
  }'

Request fields

FieldTypeRequiredDescription
messagesarrayYESOpenAI-shaped chat turns. Each item has role (user/assistant) and content (string or content blocks).
session_idstringrecommendedStable id for the conversation thread. Without it, every call is single-turn.
end_user_idstringrecommendedStable id for the person. Used for attribution, per-user rate limits, and the case "from" field.
privatebool—Default false. false = the conversation is real customer support — case may be created, KB write-back enabled, admins see it. true = ephemeral query; nothing persists. Use true for self-improving agent loops.
metadataobject—Free-form passthrough. Forwarded into action calls and audit logs.

Multimodal content

Each message.content can also be an array of blocks. Image inputs are passed through to the model and persisted in the case if one is created.

{
  "role": "user",
  "content": [
    { "type": "text",      "text": "Why does my widget look broken?" },
    { "type": "image_url", "image_url": { "url": "https://acme.com/screenshot.png" } }
  ]
}

For browser-uploaded files use POST /v1/files first to get a hosted URL, then reference it in the content block.

Response shape

{
  "id": "msg_01HZA9F2...",
  "reply": "Settings → Security → Reset 2FA. We'll email a recovery link valid for 30 min.",
  "escalated": false,
  "case_id": "case_01HZA9F2YJK...",
  "sources": [
    { "title": "FAQ — Account",     "url": "https://acme.com/docs/account" },
    { "title": "Case · 2FA reset",  "url": "https://supportbot.info/case/abc..." }
  ],
  "tool_calls": [
    { "name": "lookup_user", "args": { "id": "7421" }, "result": "..." }
  ],
  "attachments": [],
  "usage": { "input_tokens": 1820, "output_tokens": 184 }
}

case_id is null unless the bot decided this exchange was worth persisting.tool_calls reflects any custom Actions configured on the group (Dashboard → Actions).escalated: true means the bot decided a human should weigh in — see Human handoff.

Human handoff (Inbox)

When the bot is unsure or the user explicitly asks for a person, the response comes back withescalated: true and a reply that says something like "I've flagged this for a human — they'll reply here as soon as they're available." The session also lights up in Dashboard → Inbox with a red badge so an admin can pick it up.

What admins see

The Inbox shows every escalated 1-1 conversation across channels (widget, API, Telegram DM, Signal DM, WhatsApp DM) for groups the admin owns. They can read the thread and reply directly from the dashboard — no channel-switching.

What end-users see

An admin reply is delivered through the same channel the user wrote on, prefixed so they know it's a person:

  • Widget / API: "Human support: <text>"
  • Telegram / Signal / WhatsApp DM: "Admin: <text>" (or the UA equivalent Адмін:)

For the API, the admin reply arrives as the next assistant message in the conversation — poll GET /v1/sessions/{session_id}/messages?after=<id> or open a stream. The first admin reply un-escalates the thread, so the badge drops back by one.

Suppressing handoff

Pass private: true if your call is part of an internal agent loop — the bot still answers, but escalation, case creation, and Inbox surfacing are skipped.

Auto-brand from URL

One call, one URL, brand tokens out — accent color, font family, border radius, and a greeting line written in the voice of your site. Used by the dashboard's "Generate from your site" button; available to your own tools too.

POSThttps://api.supportbot.info/v1/auto-brand

curl https://api.supportbot.info/v1/auto-brand \
  -H "Authorization: Bearer sb_a4f9..." \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://acme.com" }'

# 200 OK
{
  "accent":   "#0F62FE",
  "font":     "Inter, system-ui, sans-serif",
  "radius":   12,
  "greeting": "Hi! Ask anything about Acme."
}

Soft-capped at 3 generations / day per group. On overflow returns429 with a Retry-After header (seconds until reset). Page fetches are bounded to 8 s and 256 KB — pages behind auth or geo-walls return400 fetch_failed with a hint.

Streaming variant (multi-agent)

For higher-fidelity styling — full chat-widget CSS that matches your brand's typography, density, and shadow recipe — call the streaming endpoint. It runs a multi-agent multimodal loop (probe → extract → code → critic → diff-code) and emits one Server-Sent Event per phase so a UI can show live progress.

POSThttps://api.supportbot.info/v1/auto-brand/stream

curl -N https://api.supportbot.info/v1/auto-brand/stream \
  -H "Authorization: Bearer sb_a4f9..." \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://acme.com" }'

# Server-sent events; each line is "data: <json>".
data: {"phase": "probing",   "url": "https://acme.com"}
data: {"phase": "extracting","url": "https://acme.com/", "title": "Acme"}
data: {"phase": "coding",    "iter": 1}
data: {"phase": "iter_done", "iter": 1, "accent":"#0F62FE", "font":"Inter",
                              "radius":12, "custom_css":"...", "greeting":"..."}
data: {"phase": "critic",    "iter": 1}
data: {"phase": "coding",    "iter": 2}
...
data: {"phase": "done", "iter": 4, "final_score": 9, "elapsed_s": 430.5,
       "accent":"#0F62FE", "font":"Inter", "custom_css":"...",
       "greeting":"...", "site_name":"Acme"}

The final done event also persists the brand into your group, so any subsequent widget-mount picks it up via /v1/widget-config. Shares the same 3 / day soft cap; runs ~6–8 minutes; uses an internal headless browser (not a 256 KB HTML fetch), so it works on JS-heavy sites and pages with brand fonts loaded via @font-face.

Errors

All errors return a JSON body with error.code and error.message.

401 Missing or revoked API key.403 Origin not in the public-key allowlist (browser keys only).404 Unknown session_id for an explicit get/replay endpoint.413 Request exceeded 1 MB or message history exceeded 200 turns.429 Rate limit hit. Retry-After header indicates seconds.5xx Transient — safe to retry with exponential backoff.

Chat widget

One-line embed for any web page. The snippet is generated for you in Dashboard → API → Chat widget with your key, accent color, and greeting baked in.

<script
  src="https://supportbot.info/widget.js"
  data-token="sb_a4f93b27c2e1d508f12c66e9b0a7d4c5"
  data-accent="#2C6BED"
  data-position="bottom-right"
  data-greeting="Hi! Ask anything about Acme."
  defer></script>

The widget renders an isolated iframe (no host CSS leakage) with a floating bubble that expands into a panel.

Widget data-* attributes

AttributeTypeDefaultDescription
data-tokenstring—Your public API key (required).
data-accentcolor#2C6BEDPrimary color for the bubble + user message.
data-positionenumbottom-rightOne of bottom-right, bottom-left.
data-greetingstring—First bot message shown when the panel opens.
data-radiusnumber14Border radius (px) for bubbles and panel.
data-fontstringsystemOverride the in-widget font family.
data-end-user-idstring—If your site has logged-in users, pass their id so threads survive across visits.

React example

If your app already speaks React, skip the widget and call the API directly so you control the UI:

import { useState } from 'react';

export function Chat({ apiKey, userId }: { apiKey: string; userId: string }) {
  const [history, setHistory] = useState<{role:string; content:string}[]>([]);
  const [busy, setBusy] = useState(false);

  async function send(text: string) {
    const next = [...history, { role: 'user', content: text }];
    setHistory(next);
    setBusy(true);
    const r = await fetch('https://api.supportbot.info/v1/chat', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        messages: next,
        session_id: userId,
        end_user_id: userId,
      }),
    });
    const { reply } = await r.json();
    setHistory([...next, { role: 'assistant', content: reply }]);
    setBusy(false);
  }
  // …render history + input box…
}

Rate limits

  • 60 requests / minute per API key on shared infrastructure.
  • Per-end-user soft cap of 30 turns / hour, configurable per token.
  • /v1/auto-brand and /v1/auto-brand/stream: 3 generations / day per group (shared quota). Re-runs reuse the cached result unless explicitly busted.
  • Image uploads via /v1/files: 10 MB / file, 100 files / hour.
  • Hitting a limit returns 429 with a Retry-After header — back off and retry.

Need higher limits for production traffic? Email hello@supportbot.info with your expected volume.

Academia Tech © 2026