← Blog

How to Build a Customer Support Bot With Claude

2026-10-26 · 5 min read · SubToAPI Team

Building a customer support bot with Claude means combining three things: a system prompt that encodes your support policies, a way to pull real data (order status, account info, docs) into the conversation, and an API layer that handles auth, streaming and logging in production. This article walks through each piece with working code, so by the end you'll have a bot that can answer tickets, escalate when unsure, and look up account data instead of guessing.

The short answer to "how do I build this": you don't need a custom ML model. You need Claude's Messages API, a well-scoped system prompt, tool definitions for your internal systems (ticketing, CRM, order database), and a frontend (widget, Slack bot, email pipeline) that forwards user messages and streams the response back.

Step 1: Define the support bot's scope in the system prompt

Most failed support bots fail at the prompt, not the model. Be explicit about what the bot can and cannot do.

You are the support assistant for Acme Cloud.
You can: answer questions about billing, plans, and account status using the lookup_account tool;
explain product features using the knowledge base; create a support ticket when a human is needed.
You cannot: issue refunds, change account permissions, or make promises about future features.
If the user's issue requires a human (fraud, legal, bugs you can't reproduce), call create_ticket
and tell the user a human will follow up within 1 business day.
Always be concise. Never invent account details — only use what lookup_account returns.

This single block prevents the two most common failure modes: hallucinated account data and over-promising fixes.

Step 2: Give it tools to fetch real data

A support bot that can't see the customer's actual account is just a FAQ page with extra latency. Use Claude's tool use (function calling) to let the model request account lookups, order status, or ticket creation, and you execute the real call server-side.

{
  "name": "lookup_account",
  "description": "Fetch account status, plan, and recent invoices by customer email",
  "input_schema": {
    "type": "object",
    "properties": {
      "email": { "type": "string" }
    },
    "required": ["email"]
  }
}

When Claude decides it needs account data, it returns a tool_use block instead of text. Your backend runs the real database query, then sends the result back as a tool_result in the next turn. Claude then writes the final answer using that real data. This loop — model asks, you fetch, model answers — is the core of any support bot that needs to be accurate rather than just conversational. See /docs/tools for the full request/response shape.

Step 3: Wire up the API call

Here's a minimal server-side handler using SubToAPI, which exposes Claude through a standard HTTPS API with your own sub_live_ key — useful if you already have Claude access through a subscription and want a normal API key/billing setup for your support stack instead of managing provider credentials directly.

const response = 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-sonnet-4",
    max_tokens: 1024,
    system: SUPPORT_SYSTEM_PROMPT,
    messages: [{ role: "user", content: ticketMessage }],
    tools: [lookupAccountTool, createTicketTool]
  })
});

const data = await response.json();

If data.content contains a tool_use block, run your real lookup function, append a tool_result message, and call the endpoint again. Full request examples are in /docs/messages and /docs/quickstart.

Step 4: Stream responses for chat widgets

Support widgets feel broken if the user stares at a blank bubble for 3-4 seconds. Streaming fixes that by sending tokens as they're generated:

const stream = 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-sonnet-4",
    max_tokens: 1024,
    stream: true,
    system: SUPPORT_SYSTEM_PROMPT,
    messages: conversationHistory
  })
});

Pipe the server-sent events straight to your widget's UI. Details on event types and handling partial tool calls mid-stream are in /docs/streaming.

Step 5: Handle escalation and conversation memory

Two practical details separate a demo bot from a production one:

Step 6: Monitor usage and cost per team

Support bots can generate unpredictable token volume — a single confused back-and-forth with tool calls can burn through several thousand tokens. If you're running this across a support team, you want visibility into who/what is consuming tokens without building your own dashboard. SubToAPI's dashboard gives you per-key usage metadata and team seats so you can see cost broken down by channel (widget vs. email bot vs. Slack) without extra tooling. Plans start at Solo (€9) up through Team (€19/seat) and Scale (€49/seat); see /pricing for details, or start with a free trial at /signup.

Putting it together

A production-ready flow looks like: user message → Claude with system prompt + tools → tool_use for account/order lookup → your backend executes and returns tool_result → Claude's final answer streamed to the widget → escalation ticket created if needed → conversation logged for QA. None of this requires training a model — it's prompt design, tool wiring, and a reliable API layer.

FAQs

Does Claude need fine-tuning to act as a support bot? No. A detailed system prompt plus tool use for real data access is sufficient for the vast majority of support use cases. Fine-tuning adds cost and maintenance without meaningfully improving accuracy if your prompt and tools are well-designed.

How do I stop the bot from giving wrong account information? Never let the model state account facts from memory — require it to call a lookup_account tool and only use the returned data. Explicitly instruct it in the system prompt not to invent account details.

Can I run this bot across multiple channels (widget, Slack, email)? Yes — the Messages API call is identical regardless of channel; only the transport layer (widget JS, Slack events API, email parser) changes. Centralize the system prompt and tools in one backend service so all channels behave consistently.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →