Advanced: build a support bot via the API
Developer-facing tutorial. This page drives the whole flow from the API. For the no-code path through the ScaiGrid admin UI, see Build a bot with the admin UI.
Build a support bot
You're going from zero to a deployed support bot that:
- answers from your product docs,
- escalates billing questions to a human queue,
- escalates angry visitors,
- attributes conversations to your authenticated users.
Roughly 30 minutes if you have the docs and queue ready.
1. Decide the bot's shape
Before any API calls, settle these:
- Scope. What does the bot answer? ("product features, pricing tiers, account management" — keep it tight; broader scope means worse answers.)
- Voice. Casual or formal? Verbose or concise? Empathetic? Mirror your brand.
- Escalation triggers. What words / intents are obvious "human, not bot" territory?
- Knowledge. Which documents go in? Pull together PDFs / docs / FAQ pages.
2. Create the bot
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | curl -X POST "$SCAIGRID_HOST/v1/modules/scaibot/bots" \
-H "Authorization: Bearer $SCAIGRID_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Support",
"slug": "acme-support",
"model": "scailabs/poolnoodle-omni",
"display_name": "Acme Support",
"welcome_message": "Hi there! I can help with product features, pricing, or your account. What do you need?",
"max_tokens_per_response": 600,
"max_context_messages": 20,
"knowledge_enabled": true,
"knowledge_mode": "managed"
}'
|
Save the bot.id. Everything below targets it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | curl -X PUT "$SCAIGRID_HOST/v1/modules/scaibot/bots/$BOT_ID/tone" \
-H "Authorization: Bearer $SCAIGRID_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"persona_name": "Avery",
"persona_description": "A friendly Acme support specialist.",
"formality": "casual",
"verbosity": "concise",
"empathy_level": "high",
"language": "en",
"terminology": ["Acme", "Widget", "Acme Cloud", "WidgetPro"],
"topics_in_scope": ["product features", "pricing", "account management", "billing"],
"topics_out_of_scope": ["legal advice", "third-party products"],
"custom_instructions": "If unsure, offer to escalate rather than guess."
}'
|
4. Upload knowledge
Loop through your documents. Wait for each to index before uploading the next if you want predictable ordering; in practice you can fire them all and check status afterwards.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 | import httpx, os, time
H = {"Authorization": f"Bearer {os.environ['SCAIGRID_API_KEY']}"}
BOT = os.environ["BOT_ID"]
HOST = os.environ["SCAIGRID_HOST"]
docs = [
("Product Handbook.pdf", "product-handbook.pdf"),
("Pricing Guide.pdf", "pricing-guide.pdf"),
("Account FAQ.md", "account-faq.md"),
]
uploaded = []
for name, path in docs:
with open(path, "rb") as f:
r = httpx.post(
f"{HOST}/v1/modules/scaibot/bots/{BOT}/documents",
headers=H,
files={"file": (path, f)},
data={"name": name},
)
r.raise_for_status()
uploaded.append(r.json()["data"]["id"])
# Wait for indexing
while True:
statuses = []
for doc_id in uploaded:
s = httpx.get(
f"{HOST}/v1/modules/scaibot/bots/{BOT}/documents/{doc_id}",
headers=H,
).json()["data"]["status"]
statuses.append(s)
if all(s == "indexed" for s in statuses):
break
if any(s == "failed" for s in statuses):
raise RuntimeError(f"document failed: {statuses}")
time.sleep(2)
|
5. Add escalation rules
Order matters — first by priority (high → low), then by creation order. We'll add three:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 | # Highest priority: explicit human request
curl -X POST "$SCAIGRID_HOST/v1/modules/scaibot/bots/$BOT_ID/escalations" \
-H "Authorization: Bearer $SCAIGRID_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Explicit request",
"priority": 100,
"trigger_type": "explicit",
"action_type": "scaiqueue",
"action_config": {"topic": "support-human", "priority": 5},
"message": "Got it — let me get a human on this. One moment."
}'
# Billing keywords
curl -X POST "$SCAIGRID_HOST/v1/modules/scaibot/bots/$BOT_ID/escalations" \
-H "Authorization: Bearer $SCAIGRID_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Billing keywords",
"priority": 80,
"trigger_type": "keyword",
"trigger_config": {"keywords": ["refund","chargeback","double charged","wrong amount"]},
"action_type": "scaiqueue",
"action_config": {"topic": "support-billing", "priority": 8},
"message": "Billing questions go to our specialist team — connecting you now."
}'
# Sustained negative sentiment — safety net
curl -X POST "$SCAIGRID_HOST/v1/modules/scaibot/bots/$BOT_ID/escalations" \
-H "Authorization: Bearer $SCAIGRID_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Frustrated visitor",
"priority": 30,
"trigger_type": "sentiment",
"trigger_config": {"threshold": -0.6, "window_messages": 3},
"action_type": "scaiqueue",
"action_config": {"topic": "support-human", "priority": 9},
"message": "I want to make sure you get the right answer — let me bring in someone who can help."
}'
|
6. Embed with authenticated visitors
Add a token endpoint to your backend (see embed on your site):
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | @app.route("/chat-token")
def chat_token():
user = current_user()
resp = httpx.post(
f"{HOST}/v1/modules/scaibot/bots/{BOT}/embed-token",
headers=H,
json={
"ttl_seconds": 3600,
"visitor_id": user.id,
"visitor_email": user.email,
"metadata": {"plan": user.plan, "signup_date": user.signup_date.isoformat()},
},
)
return resp.json()["data"]
|
Drop the widget into your authenticated pages:
| <script
src="https://scaigrid.scailabs.ai/static/modules/scaibot/widget.js"
data-bot-id="bot_abc123"
data-token-url="/chat-token"
data-primary-color="#0D9488"
data-position="bottom-right"
async>
</script>
|
7. Confirm via the conversation log
After a few real chats:
| curl "$SCAIGRID_HOST/v1/modules/scaibot/conversations?bot_id=$BOT_ID&limit=20" \
-H "Authorization: Bearer $SCAIGRID_API_KEY"
|
Each conversation shows:
- Visitor id and email (from the token, not the HTML).
- Every turn with its citations and which knowledge chunks fired.
- Whether an escalation rule fired and what action ran.
- Tokens spent and latency per turn.
8. Watch the metrics
The admin UI's ScaiBot dashboard has time-series for:
- Conversations started / completed / escalated.
- Average turns per conversation.
- Tokens spent (against your tenant budget).
- Top topics by retrieved-chunk frequency.
- Top escalation triggers.
Adjust the bot when you see:
- High escalation rate on a specific topic → knowledge gap, write a doc.
- Low completion rate (visitors abandoning early) → tone is wrong, or the welcome message overpromises.
- High tokens-per-conversation → cap
max_tokens_per_response lower or trim max_context_messages.
Done
You have a deployed support bot with grounded answers, sensible escalation, and end-to-end attribution. Iterate from here — the bot is just data, every parameter is editable in place.