Building a Customer Support Bot with Claude
Building a customer support bot with Claude means combining three things: a well-scoped system prompt, a way to call the Claude API reliably from your app, and a plan for escalating to humans when the model shouldn't answer alone. This article walks through that full pipeline, from defining what the bot should and shouldn't do, to wiring up streaming responses and tool calls for ticket lookups.
If you're evaluating whether Claude is a good fit for support automation: it is, as long as you treat it as a drafting and triage layer rather than a fully autonomous agent for sensitive account actions. The rest of this guide shows how to structure that.
Architecture overview
A production support bot typically has four layers:
- Frontend — chat widget, Slack app, or help-center search box
- API layer — your backend calling Claude, handling auth, keys, and rate limits
- Context layer — knowledge base content, order/ticket data, retrieved via tool use
- Escalation layer — logic that hands off to a human agent
Most teams get stuck on the API layer: managing raw provider credentials, tracking per-feature usage, and giving teammates safe access without sharing one shared key. This is exactly the gap SubToAPI fills — it turns your Claude access into a standard HTTPS API with scoped sub_live_... keys, so your support bot, your internal dashboard, and your QA environment can each have their own key, usage visibility, and seat in one dashboard.
Step 1: Define the bot's scope before writing any code
Before touching the API, write down three lists:
- Allowed topics: billing explanations, how-to questions, troubleshooting steps, order status
- Escalation triggers: refund requests above a threshold, account security issues, anger/frustration signals, anything legal
- Forbidden actions: the bot should never issue refunds, change account emails, or promise SLAs it can't verify
This scope document becomes the backbone of your system prompt and your QA test cases later.
Step 2: Design the system prompt
The system prompt is where most of the "personality" and guardrails live. Keep it structured and explicit rather than conversational:
You are a support assistant for Acme Cloud.
- Answer only questions about Acme Cloud's product, billing, and account settings.
- If the user asks about a refund over €50, account deletion, or expresses strong frustration, respond with a brief acknowledgment and call the escalate_to_human tool.
- Never invent pricing, SLAs, or feature availability. If unsure, say you'll check and escalate.
- Keep responses under 150 words unless the user asks for a step-by-step guide.
Resist the temptation to cram your entire knowledge base into the system prompt. Instead, retrieve relevant docs per-query and pass them in as context, or use tool calls to fetch them on demand.
Step 3: Call the Claude API reliably
Once the prompt is ready, you need a dependable way to call it from your backend. Here's a minimal example using SubToAPI's Messages endpoint, which mirrors the standard Claude message format:
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-3-5-sonnet",
"max_tokens": 500,
"system": "You are a support assistant for Acme Cloud...",
"messages": [
{"role": "user", "content": "I was charged twice this month, what happened?"}
]
}'
Because it's a standard HTTPS endpoint with its own key, you can issue a separate sub_live_... key for staging versus production, rotate keys without touching provider settings, and see exactly how many requests your support bot is making per day. See /docs/quickstart for setup and /docs/messages for the full request reference.
Step 4: Stream responses for a real-time feel
Support chat feels sluggish if users stare at a blank bubble while the model generates a full answer. Streaming fixes this by sending tokens as they're produced:
const res = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "claude-3-5-sonnet",
max_tokens: 500,
stream: true,
messages: [{ role: "user", content: userMessage }],
}),
});
const reader = res.body.getReader();
// read chunks and append to the chat UI as they arrive
Full event format and reconnect handling is covered in /docs/streaming. Streaming matters more for support bots than most use cases, since users judge responsiveness as part of "being helped."
Step 5: Use tool calls for real data, not hallucinated answers
The single biggest source of support bot failures is the model guessing at order status, account state, or policy details it doesn't actually have. Tool use solves this: instead of answering from memory, Claude calls a function you define, gets real data back, and uses that to answer.
A typical setup defines tools like:
get_order_status(order_id)— hits your order DBget_account_plan(user_id)— returns current plan and billing cycleescalate_to_human(reason, summary)— opens a ticket and hands off
{
"name": "get_order_status",
"description": "Look up the current status of an order by ID",
"input_schema": {
"type": "object",
"properties": { "order_id": { "type": "string" } },
"required": ["order_id"]
}
}
Claude will respond with a tool call request instead of a guessed answer, your backend executes it, and you feed the result back in the next message. Tool definitions and the full request/response cycle are documented at /docs/tools.
Step 6: Track usage and manage access across your team
Once the bot is live, you'll want visibility into cost per conversation, which environment is using the most tokens, and who on your team has access to the production key. This is where a dedicated API layer pays off over calling the provider directly: SubToAPI's dashboard shows usage metadata per key, so you can separate your support bot's traffic from internal testing, and add teammates as seats instead of sharing one secret. Plans run from Solo at €9 for a single key up through Team (€19/seat) and Scale (€49/seat) for larger support orgs — see /pricing for details, or start on the free trial at /signup.
Common pitfalls to avoid
- No escalation path: always give the bot a way to say "I'll get a human to help" rather than guessing
- Overloaded system prompts: keep instructions short and move knowledge into retrieval, not the prompt
- No logging: store every conversation for QA review, especially escalated ones
- Ignoring latency: use streaming and keep
max_tokensreasonable for chat-style replies - Treating it as fully autonomous: Claude is excellent at drafting and triage, but refunds, cancellations, and account changes should go through confirmed actions, not free-text instructions alone
FAQ
Does Claude support function/tool calling for support bots? Yes. You define tools like order lookups or escalation handlers, and Claude requests them when needed instead of guessing at data it doesn't have. See /docs/tools.
How do I keep the bot from making up policy or pricing details? Keep your system prompt strict about not inventing information, and feed real policy/pricing data in via retrieval or tool calls rather than hoping the model remembers it correctly.
Can multiple team members manage the same support bot deployment? Yes — with SubToAPI you can add teammates as seats on Team or Scale plans, each with their own key and usage visibility, instead of sharing one shared credential.