Request a callbackBook a call
← All posts

Build an AI Customer Support Agent: Full System Design, Cost Per Ticket and Failure Modes (2026)

TL;DR
  • A support agent resolves a ticket for about 7.3 cents in models, roughly 5 cents when it escalates. The human path is the expensive one at about $1.65 to $2.20 per ticket. That is why deflection rate, not model quality, is the number the business case rests on.
  • Confidence gating is the entire product. An agent that answers everything is a liability. An agent that answers 45% of tickets with a grounded citation and hands the rest to a human with a written brief is a business. Published production resolution rates for the leading vendor sit in the 42-53% band, not the 70%+ the marketing implies.
  • Building in-house beats per-resolution vendor pricing at roughly 14,000 tickets a month on the arithmetic below. Under that, buy it. That answer costs me consulting revenue to write down.
The seven components of an agentic support system
1 · Ingest and normalise

Email, chat widget, in-app and API tickets collapsed into one ticket record with a stable id, tenant, channel and thread history.

~$0 · fails: duplicate threads
2 · Triage classifier

One cheap model call that assigns intent, urgency, product area and a route. Deterministic rules run first and win ties.

$0.0028 · fails: silent misroute
3 · Retrieval

Hybrid search over public docs, internal runbooks and resolved past tickets. Past tickets are the highest-value and lowest-hygiene corpus you own.

$0.0002 · fails: stale doc wins
4 · Agent runtime

The loop, three budgets, the journal, and a hard rule that no irreversible tool runs without an idempotency key written first.

$0.066/ticket · fails: loops
5 · MCP tool surface

Read tools into CRM, billing and order systems; a very small number of write tools behind approval. Per-agent allowlist, always.

1.8k tok/turn · fails: over-scoped token
6 · Confidence gate

Grounding check, citation-span validation, policy check and a calibrated threshold. This is the layer that decides whether a customer ever sees the answer.

$0.0035 · fails: confident nonsense
7 · Escalation with context

A written brief (what the customer wants, what was checked, what was ruled out, the account facts) handed to a human with the trajectory attached.

saves ~1.5 min/ticket
Six of these seven components are ordinary software. Only the fourth involves an agent loop, and the sixth is where nearly all the engineering judgement lives. Teams that build this backwards, starting with a clever agent and adding a gate later, ship a system that is confidently wrong in front of customers. That is the one failure mode support organisations cannot absorb.

What is an AI customer support agent, architecturally?

A classifier, a retriever, a bounded agent loop with read-mostly tools, and a gate that decides whether the answer reaches a customer. Four parts. The agent loop is the smallest. Everything that makes the system safe to point at real customers lives in the classifier in front of it and the gate behind it.

That framing matters because the category is routinely mis-specified. People describe a support agent as a chatbot with your docs attached, which is a retrieval system with a text box. The thing that actually deflects tickets does more: it looks up the customer in your CRM, checks whether their invoice failed, reads the order status, decides whether its answer is grounded in something it can cite, and most of the time decides it is not sure enough and writes a handover brief instead.

Be clear on what this post is. It is a reference design: how I would architect this system and what the arithmetic says it costs, grounded in building AccioMatrix, an AI assessment and interview platform on an event-driven backbone orchestrating multiple LLMs, agents and MCP tooling, now serving 20+ enterprise clients. It is not a case study of a support agent I have shipped. Every dollar figure below is modelled arithmetic with its inputs printed, not an invoice.

The spine is the same nine-layer stack from the AI product architecture reference design. What changes in support is the weighting. Retrieval quality and the confidence gate carry almost all the product risk. The model router carries the cost lever. The tool layer carries all the blast radius, because a support agent is the one agent in your company with a plausible reason to touch a billing system.

One number frames the build. Published production resolution rates for the market-leading support agent cluster in the 42-53% band across real deployments, against a headline of 67% across Intercom's own 7,000+ customers. Plan against the lower band. A business case built on 70% deflection fails its first quarterly review.

ComponentWhat it doesImplementationCost per ticketPrimary failure modeSkip in v1?
Ingest / normaliseCollapses email, chat, widget and API tickets into one record with stable identity and thread historyYour helpdesk webhooks plus a Postgres ticket table~$0The same customer opens three threads; the agent answers each without the othersNo
Triage classifierIntent, urgency, product area, route. Deterministic rules first, model secondHaiku-class model, ~2k in / 150 out, structured output$0.0028Misroutes a billing dispute into the self-serve path and never escalatesNo
RetrievalHybrid BM25 plus vector over docs, runbooks and resolved tickets, with recency and product-version filtersPostgres plus pgvector until you have measured a limit$0.0002 amortisedA deprecated doc outranks the current one; the answer is fluent and wrongNo
Agent runtimeThe loop, step and token budgets, tool validation, journal writes, no-progress detectionIn-house loop over Sonnet-class model, 2-4 turns typical$0.066 (3-turn resolve)Oscillates between lookup tools for 40 turns on an ambiguous ticketNo
MCP tool gatewayScoped read access to CRM, billing, orders; writes behind approval; per-agent allowlistMCP servers behind a gateway, ~12 tools, ~1.8k tokens of schema per turnIn the turn cost aboveA token scoped to the whole CRM lets the agent read another tenant's accountGateway: no. MCP transport: often
Confidence gateGroundedness check, citation-span validation, policy check, calibrated thresholdCheap-model verifier plus deterministic span matching$0.0035Threshold calibrated on a golden set that does not resemble live trafficAbsolutely not
Escalation briefStructured handover: intent, facts checked, hypotheses ruled out, account state, suggested next actionOne templated model call on the escalate path$0.0045Brief is confidently wrong and the human trusts it, which is worse than no briefNo
Evals and tracesDeterministic CI suite, trajectory scoring, online judge on sampled traffic, cost attributionOTel GenAI spans plus a run/step journal you own$0.0006 (15% sample)Golden set goes stale; suite passes while live deflection quietly dropsDeterministic: no. Judges: month two

What does the full architecture look like?

Twelve boxes across four columns: ingest, triage and retrieval, agent and tools, and the gate that splits into answer or escalation. The load-bearing detail: the confidence gate sits between the agent and the customer, not inside the agent. A model cannot grade its own homework in the same call that produced it.

Read the diagram left to right and the product logic falls out. Tickets arrive on three channels and get normalised into one record, because an agent that cannot tell a chat message and an email are the same customer will answer both and contradict itself. A cheap classifier assigns intent and route before any expensive model runs. That single decision removes roughly 20% of tickets from the agent path, because password resets, order-status lookups and known-incident replies are deterministic flows that never needed a language model.

Retrieval draws from three corpora with very different characteristics. Public documentation is clean, structured and often out of date. Internal runbooks are accurate and full of language you must never show a customer. Resolved past tickets are the most valuable corpus and the messiest: the actual phrasing customers use, the actual resolutions that worked, and a long tail of wrong answers a previous agent gave that must not be recycled. Filtering past tickets to those with a positive CSAT and a confirmed resolution is not optional hygiene. It is the difference between a system that learns from success and one that launders old mistakes.

The MCP tool gateway is where the blast radius lives. A support agent has a legitimate reason to read a customer's subscription state, invoice history and open orders, which means it holds credentials that can read those things for every customer. Scope the token per request to the resolved tenant, validate every tool argument against a schema before dispatch, and bound every tool result at ingestion. A full invoice history serialised naively is a 40,000-token payload that blows your context window and your budget in one call. The full gateway pattern is in MCP in production.

Everything downstream of the agent is deliberately dull. The gate is a verifier plus deterministic span matching plus a threshold. The escalation path is a template. The journal is a Postgres table. Dull is the point: when a customer complains the agent told them something wrong, you answer what it read, what it called, what it cost and why it sent that, from a single SELECT.

The system
Agentic support system: full reference architecturepersistroute=agentroute=humansearchtools/callscoped tokenevery stepdraft answerscore >= 0.72score < 0.72citation spans
Channelsemail · chat · in-app · API
Ingest + normaliseone ticket record, stable id, thread merge
Ticket storePostgres · tenant, thread, CSAT, resolution
Triage classifierHaiku-class · intent, urgency, route
Support agent runtimeloop · step/token/wall budgets · journal
Run + step journalevery model call, tool call, cost, latency
Hybrid retrievaldocs · runbooks · resolved tickets
MCP tool gatewayallowlist · schema validation · result bounding
Confidence gategroundedness · citation spans · policy
Answer to customerwith citations and a rating prompt
CRM · Billing · Ordersscoped read, writes behind approval
Human queue + briefintent, facts checked, ruled out, next action
Two edges out of the gate. That fork is the product. Note also that the triage classifier has its own edge straight to the human queue: about one ticket in six should never reach the agent, because it is a refund dispute, a security report or an angry escalation where the correct latency-to-human is zero and the correct model cost is $0.0028.

What happens on one ticket, end to end?

Enqueue, classify, retrieve, two to three agent turns with one or two MCP tool calls, a grounding check, then either an answer with citations or an escalation brief. Roughly 9 to 14 seconds wall clock, 3 to 5 model calls, one row per step in a journal you own. Below is that sequence with tokens and dollars on every model arrow.

The ordering detail that matters most is where the CRM lookup happens. It is tempting to enrich every ticket with account context up front, because it makes the prompt richer and the demo better. Do not. Roughly 60% of tickets are answerable from documentation alone, and pre-fetching account state on all of them adds an external API call, a latency tail you do not control, and a privacy surface you have to justify, in exchange for context the agent never reads. Let the agent ask for it. The tool call is cheap. The unconditional enrichment is not.

The second detail: the idempotency key is written before the tool executes, not after. This is not academic. The small set of write tools (issue a credit, cancel a subscription, resend an invoice) are exactly the operations where a retried step becomes a customer-visible incident and a finance reconciliation. The key derives from the run id, the step index, the tool name and a canonical hash of the arguments, and the step row goes in first. The full pattern with the crash-window reconciliation branch is in the agent loop in production.

The third detail is the shape of the latency budget. Support is one of the few agentic products where the user is actively waiting, and the perceived budget is not the total. Stream a status line (checking your account, reading our billing docs) within 400 milliseconds and the 12-second run reads as attentive. Return nothing for 12 seconds and the same run reads as broken. That front-end decision changes your acceptable back-end budget by an order of magnitude, and it belongs in the architecture conversation, not the design review.

The resolve path
One ticket, end to end, with tokens and cost on every model callCustomerGatewayTriageAgentRetrievalMCP GWVerifierPostgres
ticket created (email thread)
INSERT ticket + agent_run (queued)
SQL
classify: 2.0k in / 150 out
$0.0028 · Haiku 4.5
intent=billing.failed_charge · route=agent
hybrid search: docs + resolved tickets
top-8 after rerank
6.0k tokens of chunks + span ids
turn 1: 12.0k in (4.5k cached) / 400 out
$0.0199 · Sonnet 5
INSERT agent_step + idempotency_key
before the side effect
tools/call get_subscription(tenant, customer)
result bounded to 800 tokens
full payload to object store
turn 2: 13.2k in (4.5k cached) / 350 out
$0.0218
turn 3 (final): 14.0k in / 450 out
$0.0244
groundedness + citation spans: 3.0k in / 100 out
$0.0035 · Haiku 4.5
score 0.81 · 3/3 claims cited
UPDATE agent_run (resolved, cost_micros=73200)
answer + citations + rate this
Total modelled cost: $0.0732. The single largest line is not the tool call and not the retrieval. It is the three agent turns at $0.066 combined, because each turn re-sends the entire conversation plus the retrieved chunks. That is why capping the agent at four turns is a cost control, not just a safety control, and why a fourth turn should have to justify itself.
Where 12 seconds goes
12900ms totalbudget 14000ms
Ingest + normalise180ms
Triage classify620ms
Hybrid retrieval + rerank940ms
Agent turn 13100ms
MCP tool call (CRM)720ms
Agent turn 23300ms
Agent turn 3 (final)3400ms
Grounding + span check640ms
Roughly 12.9 seconds against a 14-second budget, and 78% of it is model time on three turns. Two levers actually move this: cutting a turn, which is a prompt and tool-design problem rather than an infrastructure one, and streaming the final turn so the customer sees text at 3.5 seconds instead of 12.9. The MCP call is 720ms of external system you do not control, which is why it needs its own timeout and fallback answer.

How does the agent decide when to answer and when to escalate?

A gate outside the agent that combines four independent signals: whether every factual claim maps to a retrieved span, whether a cheap verifier model agrees the answer is grounded, whether the ticket class is on the allowed-to-auto-answer list, and whether the agent used a write tool. Any one failing sends the ticket to a human.

Self-reported confidence is not one of the four, and that is deliberate. Ask a model how confident it is in the same call that produced the answer and you get a number that tracks fluency, not correctness. The signals that work are external and mostly deterministic: citation-span validation is string matching, ticket-class allowlisting is a lookup, write-tool usage is a boolean. Only the groundedness verifier is a model call, and it is a different model looking at the answer cold, without the reasoning that produced it.

The threshold is an operating decision, not a technical one, and it should be a configuration value a support leader can move. At 0.72 you get roughly 45% deflection with a low rate of wrong answers reaching customers. At 0.60 deflection rises toward 60% and so does the rate of confidently wrong answers. At 0.85 you deflect 25% and almost never embarrass yourself. Every support organisation sits at a different point on that curve. Let them choose it with the trade-off written on the slider, then re-measure monthly, because the curve moves when your docs change.

The escalation path deserves as much engineering as the answer path and usually gets none. A ticket that reaches a human queue with a brief (what the customer wants, what I checked, what I ruled out and why, their subscription state, what I would do next) is materially cheaper to handle than a raw one. Modelling that as 6 minutes down to 4.5 minutes of handle time is conservative, and at $22/hour fully loaded it is $0.55 of value for $0.0045 of model cost. That is the best ratio in the system, and it applies to 55% of tickets rather than 45%.

One warning next to the good news. A brief that is confidently wrong is worse than no brief, because a human under queue pressure trusts it. Every claim in the brief must carry the same citation discipline as the customer-facing answer, and the brief must visibly separate facts read from a system of record from inferences the agent made. Two visual styles, one rule: if it came from a tool, it is a fact; if it came from the model, it is a hypothesis.

confidence-gate.ts
import { z } from "zod";

export const AnswerSchema = z.object({
  reply_markdown: z.string().min(1),
  claims: z.array(z.object({
    text: z.string(),
    chunk_id: z.string(),
    quoted_span: z.string().min(12),
  })).min(1),
  used_write_tool: z.boolean(),
  ticket_class: z.string(),
});

export type Answer = z.infer<typeof AnswerSchema>;

const AUTO_ANSWERABLE = new Set([
  "howto.product",
  "billing.invoice_lookup",
  "billing.failed_charge",
  "account.settings",
  "order.status",
]);

type Chunk = { id: string; text: string };

export type GateResult =
  | { decision: "answer"; score: number }
  | { decision: "escalate"; score: number; reason: string };

export async function gate(
  answer: Answer,
  chunks: Map<string, Chunk>,
  verify: (a: Answer, c: Map<string, Chunk>) => Promise<number>,
  threshold = 0.72,
): Promise<GateResult> {
  // 1. deterministic: never auto-answer a class we have not approved
  if (!AUTO_ANSWERABLE.has(answer.ticket_class)) {
    return { decision: "escalate", score: 0, reason: "class_not_allowlisted" };
  }

  // 2. deterministic: any write tool means a human confirms the outcome
  if (answer.used_write_tool) {
    return { decision: "escalate", score: 0, reason: "write_tool_used" };
  }

  // 3. deterministic: every claim must quote a span that really exists
  for (const claim of answer.claims) {
    const chunk = chunks.get(claim.chunk_id);
    if (!chunk) {
      return { decision: "escalate", score: 0, reason: "citation_unknown_chunk" };
    }
    if (!normalise(chunk.text).includes(normalise(claim.quoted_span))) {
      return { decision: "escalate", score: 0, reason: "citation_span_not_found" };
    }
  }

  // 4. the only model call in the gate, on a cheap model, answer seen cold
  const score = await verify(answer, chunks);
  return score >= threshold
    ? { decision: "answer", score }
    : { decision: "escalate", score, reason: "below_threshold" };
}

function normalise(s: string): string {
  return s.toLowerCase().replace(/\s+/g, " ").trim();
}
The gate in full. Three of the four checks are deterministic and cost nothing. Only verifyGroundedness is a model call, on a Haiku-class model at about $0.0035. The subtle line is the last check in requireCitations: a claim counts as supported only if the cited span is actually a substring of the chunk it points at. That catches the most common hallucination in a RAG support answer, a real citation id attached to a sentence the source never said.
What happens to a ticket, by class
Should this ticket reach the agent, a human, or a deterministic flow?
Known incident, password reset, order status lookup
Deterministic flow

No model. A template and a database read. Roughly 20% of volume and it costs a fraction of a cent. Every ticket you remove here is a ticket you never have to make an agent safe for.

How-to, docs-answerable, invoice lookup, failed charge
Agent, auto-answer allowed

The 45% band. Gated at 0.72, cited, with a rating prompt attached so you get a live quality signal instead of a quarterly one.

Bug report, integration debugging, unusual account state
Agent, escalate always

Run the agent for the brief, never for the reply. You get the handle-time saving without the wrong-answer risk. This is the most under-used configuration in the category.

Refund dispute, cancellation, security report, legal
Straight to human

Triage routes it before any expensive model runs. Correct latency-to-human is zero and correct model spend is $0.0028.

Angry sentiment or second contact on the same thread
Straight to human

A repeat contact is prima facie evidence the automated answer failed. Auto-answering it again is how a support incident becomes a public one.

Only the second branch produces a customer-visible AI answer. The third is the branch most teams never build and the one with the best risk-adjusted return: full agent reasoning, zero wrong-answer exposure, and a measurable handle-time saving on the hardest tickets in the queue.

What tools should the agent have, and how are they defined?

About twelve, of which nine are reads and three are writes behind human approval. Reads: customer lookup, subscription state, invoice history, payment method status, order status, shipment tracking, entitlement check, known-incident check, and past-ticket search. Writes: resend invoice, apply a bounded goodwill credit, and escalate with brief.

The count is a budget, not an aspiration. Every tool schema costs roughly 150 tokens on every turn, so twelve tools is about 1,800 tokens per turn, around 5,400 across a three-turn resolve, or $0.011 per ticket in pure catalogue before the agent does any work. Adding thirty more tools because they exist takes that to $0.038 per ticket and, worse, measurably degrades tool selection accuracy. If you need a large surface, defer the definitions and let the model discover them: Anthropic reports on-demand tool discovery cut context from 77,000 to 8,700 tokens in their own testing, an 85% reduction, while tool-selection accuracy rose.

The write tools need three properties the read tools do not. Each takes an explicit bound the agent cannot exceed: a goodwill credit tool that accepts an arbitrary amount will eventually issue an arbitrary amount. Each writes its idempotency key before dispatch. And each returns a receipt the human approver sees, not just a success boolean, because the person clicking approve needs to know what they are approving in the customer's terms, not the API's.

On transport: use MCP where you are integrating something you did not build, and plain typed function calls where you did. The July 2026 spec revision made this materially easier to operate. The 2026-07-28 spec removed the initialize handshake and the Mcp-Session-Id header from Streamable HTTP, so any server instance can serve any request behind an ordinary round-robin load balancer. That turns an MCP server from a stateful thing you have to route carefully into a plain HTTP workload. It also broke every client written against the old handshake, which is the general lesson about protocol dependencies in a fast-moving spec.

The tool definition below is the shape I would ship. Note three things absent from the naive version: the tenant is not a parameter the model can set, the result is bounded at the server, and the description tells the model when not to call it. That last line is worth more than most prompt engineering. A tool description that says do not call this for pre-purchase questions removes an entire class of wasted turn.

mcp-tools/get-subscription.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

const server = new McpServer({ name: "billing", version: "2.1.0" });

server.registerTool(
  "get_subscription",
  {
    title: "Get subscription and recent billing state",
    description:
      "Returns the current plan, renewal date, payment method status and the " +
      "three most recent invoices for the customer on this ticket. Call this " +
      "when the customer asks about charges, renewals, plan limits or a failed " +
      "payment. Do NOT call this for pre-purchase pricing questions - those are " +
      "answered from documentation.",
    inputSchema: {
      customerId: z.string().describe("Customer id from the ticket record"),
      includeInvoices: z.boolean().default(true),
    },
  },
  async ({ customerId, includeInvoices }, extra) => {
    // tenant is NOT a model-settable argument - it comes from the verified
    // request context. This single line is the multi-tenant isolation story.
    const tenantId = requireTenant(extra.requestInfo);

    const sub = await billing.getSubscription({ tenantId, customerId });
    if (!sub) {
      return {
        content: [{ type: "text", text: "No subscription found for that customer." }],
        isError: false, // a 404 is information for the model, not an exception
      };
    }

    const invoices = includeInvoices
      ? (await billing.listInvoices({ tenantId, customerId, limit: 3 }))
          .map((i) => ({
            id: i.id,
            amount: i.amountMinor / 100,
            currency: i.currency,
            status: i.status,
            failureCode: i.failureCode ?? null,
            issuedAt: i.issuedAt,
          }))
      : [];

    // bounded at the server. A 400-invoice customer must not be able to
    // produce a 40k-token tool result.
    const payload = {
      plan: sub.planName,
      status: sub.status,
      renewsAt: sub.renewsAt,
      paymentMethod: { brand: sub.pmBrand, last4: sub.pmLast4, valid: sub.pmValid },
      seats: { used: sub.seatsUsed, purchased: sub.seatsPurchased },
      invoices,
    };

    return {
      content: [{ type: "text", text: JSON.stringify(payload) }],
      structuredContent: payload,
    };
  },
);
One MCP tool, production-shaped, on the 2026-07-28 stateless spec. Three details do the work. tenantId comes from the validated request context and is never a model-settable parameter, which is the whole multi-tenant isolation story in one line. The result is bounded server-side to the three most recent invoices, so a customer with 400 invoices cannot blow the context window. And the description ends with a negative instruction, which reliably removes wasted turns.
The twelve-tool surface, with scope and blast radius
 KindScopeApprovalBlast radius if wrong
get_customerreadtenant-scoped by request contextnoneWrong customer named in the reply — embarrassing, recoverable
get_subscriptionreadtenant + customernoneWrong plan quoted; customer makes a decision on bad data
list_invoicesread, capped at 3tenant + customernoneContext overflow if uncapped; this is why the cap is server-side
get_order_statusreadtenant + customernoneWrong delivery date quoted — the most common CSAT complaint
check_entitlementreadtenant + customer + featurenoneAgent tells a customer they have a feature they do not
check_known_incidentsreadglobal, public status onlynoneMissed incident means N duplicate tickets answered individually
search_past_ticketsread, CSAT-filteredtenant only, PII-redactednoneLeaks another customer's phrasing if redaction fails — the serious one
search_docsreadproduct + version filterednoneDeprecated doc outranks current; fluent wrong answer
resend_invoicewrite, idempotenttenant + customerauto if same email on fileDuplicate email; annoying, not dangerous
apply_goodwill_creditwrite, capped at $25tenant + customerhuman approval alwaysUnbounded credits. The cap is in the schema, not the prompt
cancel_subscriptionnot exposedn/an/aDeliberately absent. Cancellation is a human conversation
escalate_with_briefwrite, always allowedtenantnoneA wrong brief that a rushed human trusts — style facts vs hypotheses
The eleventh row is the important one: the most defensible tool decision in a support agent is the tool you choose not to build. Cancellation, refunds above a threshold and account deletion are conversations, not API calls, and exposing them buys you a demo moment and a permanent risk.

What does one ticket actually cost?

About 7.3 cents when the agent resolves it, about 5 cents plus roughly $1.65 of human time when it escalates, against a baseline of about $2.20 for a fully human ticket. At 45% deflection over 10,000 tickets a month the blended cost is roughly $0.97 a ticket, a modelled saving of about $12,300 a month against an all-human baseline.

Work through the resolve path and the shape becomes obvious. Triage on a Haiku-class model is $0.0028. Retrieval is a rounding error at $0.0002 amortised. The three Sonnet-class agent turns are $0.0199, $0.0218 and $0.0244: $0.066 combined, or 90% of the total. The grounding verifier is $0.0035 and the sampled online judge adds $0.0006. Total $0.0732. Every number is the token count times the August 2026 list rate, and every token count is printed in the sequence diagram above.

The escalate path is cheaper in models and far more expensive in total. Triage plus two agent turns plus the brief is about $0.048, then a human spends the time. Modelled at $22/hour fully loaded and 4.5 minutes with a brief against 6 minutes without, the brief is worth about $0.55 and costs less than half a cent. That is the highest-leverage 100 lines of prompt in the system, and the part nobody demos because it has no customer-facing surface.

Now the part most build-versus-buy analyses skip. At 10,000 tickets a month, buying beats building. Intercom documents Fin at $0.99 per resolution; 4,500 resolutions is $4,455 a month, against a modelled build of about $6,580 a month once you amortise roughly nine engineer-weeks over 24 months and add three engineer-days a month of maintenance. The crossover is around 14,000 tickets a month. Below it, build is the worse financial decision and you should say so out loud.

Three reasons to build anyway, all non-financial. You need tools into systems no vendor will integrate with. You need the answer policy to be yours, versioned in your repo and auditable. Or support is your product surface rather than a cost centre, in which case the deflection curve is a product decision you cannot outsource. If none apply at your volume, buy it, instrument it properly, and revisit at 15,000 tickets. The general cost machinery (routing, caching, price snapshots) is in LLM routing and caching, cost per request.

Line itemModel / rateTokens or unitsResolve pathEscalate pathNote
Triage classificationHaiku 4.5 · $1 / $5 per 1M2,000 in / 150 out$0.00275$0.00275Runs on every ticket including the ones routed straight to a human
Hybrid retrievalpgvector + BM25, amortised2 queries, 6k chunk tokens$0.00020$0.00020Infrastructure amortised across volume, not a per-call API charge
Agent turn 1Sonnet 5 · $2 / $10 per 1M12.0k in (4.5k cached) / 400 out$0.01990$0.01990Cached prefix = system + 12 tool schemas + few-shot
Agent turn 2 (after tool call)Sonnet 513.2k in (4.5k cached) / 350 out$0.02180$0.02180History plus the bounded 800-token tool result
Agent turn 3 (final answer)Sonnet 514.0k in (4.5k cached) / 450 out$0.02440n/aEscalate path stops at two turns
Grounding + span verificationHaiku 4.53,000 in / 100 out$0.00350n/aDeterministic span check costs $0; only the verifier is a model call
Escalation briefSonnet 58.0k in / 550 outn/a$0.00450Worth ~$0.55 of saved handle time. Best ratio in the system
Online judge (15% sample)Sonnet 5, sampled3.5k in / 200 out$0.00060$0.00060Sampling rate is the knob; 15% is enough for a weekly trend
Model subtotal$0.07315$0.04975Modelled from Aug 2026 list prices, not measured
Human handling$22/hr fully loaded0 min vs 4.5 min$0.00$1.65000Baseline without an AI brief is 6 min = $2.20
Total per ticket$0.073$1.700Blended at 45% deflection: $0.967
Where 7.3 cents goes
$ per resolved ticket, 3-turn agent runlower is better
Agent turn 3 (final answer)33%. The most expensive turn is always the last$0.0244
Agent turn 2 (after tool call)30%$0.0218
Agent turn 127%$0.0199
Grounding + span verification5%. The cheapest insurance in the build$0.0035
Triage classification4%. Runs on 100% of tickets$0.0028
Online judge (15% sample)1%$0.0006
Retrieval (amortised)<1%$0.0002
Total$0.0732
Ninety per cent of the bill is three agent turns, and the reason is structural: each turn re-sends the whole conversation plus 6,000 tokens of retrieved chunks. Cutting one turn saves more than every other optimisation here combined. Two ways to do it: better retrieval, so the agent has what it needs on turn one, and tool descriptions that stop it fetching context it will not use.
Build vs buy, by monthly ticket volume
49,89637,42224,94812,47401,0005,00010,00025,00050,000100,000Monthly cost ($)Tickets per month
Crossover ~14,000 tickets/month
Buy — $0.99 per resolution at 45% deflectionBuild — models + infra + amortised build + maintenance
The build line is nearly flat because fixed costs dominate: roughly nine engineer-weeks amortised over 24 months plus three engineer-days a month of maintenance, about $6,250 before a single ticket arrives. The buy line is pure variable. Below ~14,000 tickets a month the vendor wins on money, and the only valid reasons to build are integration reach, policy ownership and support being your product. Above it, every additional 10,000 tickets is roughly $4,400 a month of vendor spend against roughly $330 of marginal model cost.
The four numbers to put on the business case
$0.073
modelled model cost of a ticket the agent resolves end to end
$0.967
blended cost per ticket at 45% deflection, including human time
vs $2.20 all-human
42-53%
production resolution band independent write-ups report for the leading vendor, against a 67% headline
~14,000
tickets per month where building overtakes per-resolution vendor pricing
The third figure is the one to argue about before you build anything. A business case at 67% deflection and a business case at 45% deflection are different businesses, and the gap between those two numbers is worth more than any model choice you will make.

What breaks in production, and how would you know?

Eleven things, and the three most expensive all return a successful response. A confidently wrong answer, a stale document that outranks the current one, and a cache serving yesterday's policy all look like healthy 200s on every dashboard you own. No error rate moves. That is why a support agent needs quality instrumentation, not uptime instrumentation.

The failure-mode table below is the one I would put on the wall. Read the second column first, what the user sees, because it determines whether a failure is a ticket, an escalation or a screenshot on social media. A wrong delivery date is a CSAT hit. A leaked snippet of another customer's ticket is a breach notification, and it is a plausible outcome of an unredacted past-ticket index. That is why that row has its own hardened pipeline rather than a filter in a prompt.

Four signals catch most of it: deflection rate by ticket class, reopen rate on tickets the agent resolved, tool error rate broken out by tool, and cost per resolved ticket. Reopen rate is specific to this domain and the one most teams miss. A ticket the agent closed that the customer opens again within 72 hours is the cleanest available signal that the answer was wrong, and it needs no judge, no labelling and no eval set. Wire it on day one.

Alert on rates, never on individual tickets. An agent that gets one answer in three hundred wrong is a normal support organisation. An agent whose reopen rate moved from 6% to 14% overnight is an incident, usually caused by a documentation change or a prompt edit that broke no tests. Add a distribution alert on the confidence-gate score too: if the average gate score moves by more than a few points week over week, something upstream changed and you want to know before your deflection rate does.

The last row is the one people find hardest to accept. If you cannot answer, for a specific angry customer three weeks ago, exactly what the agent retrieved, which tools it called, what it cost and why the gate let it through, you do not have a production support agent. You have a demo with customers attached. That capability is one journal table and one query, and it is the difference between apologising with facts and apologising without them.

Failure modeWhat the user seesWhere to fix itDetection signalCost of getting it wrong
Confidently wrong answer passes the gateA fluent, cited reply that is factually falseGate threshold and the citation-span validator; raise threshold and widen the class denylistReopen rate within 72h; thumbs-down rate; sampled judgeOne CSAT hit per instance, plus a $2.20 human ticket you already paid $0.073 for
Deprecated doc outranks the current oneInstructions for a UI that no longer existsRetrieval: version and recency filters at query time, not post-hoc rerankingCitation age distribution; share of citations to docs older than N monthsSystemic — affects every ticket in that product area until fixed
Past-ticket index leaks another customer PIIA snippet naming someone else's account or emailIngestion pipeline: PII redaction before embedding, not before displayRedaction test suite in CI; entity scan on the index, not the outputBreach notification. The one failure on this list that is not recoverable with an apology
Agent loops between lookup toolsNothing for 90 seconds, then a timeoutRuntime: step budget of 4, plus a repeated-call hash detector that terminates into stalledStep-budget-exceeded rate; distribution of turns per run$0.30-$0.90 per stuck run and a customer who now needs two contacts
Tool returns an unbounded payloadTruncated or empty replyMCP server: cap the result server-side; never trust the caller to bound itTool result size p99; context-overflow error rateBlown context window, wasted turn, roughly $0.03 and one failed ticket
Over-scoped credential reads another tenantCorrect-looking answer about the wrong accountTool gateway: tenant from verified request context, never a model parameterCross-tenant assertion in every tool test; audit log anomalyIsolation breach. Treat as sev-1 even when the customer never notices
Triage misroutes a dispute into self-serveA cheerful automated reply to a furious refund requestTriage: deterministic rules ahead of the model for regulated and emotional classesClass distribution drift; sentiment-flagged tickets that were auto-answeredEscalation to a manager and, occasionally, a public screenshot
Cache serves a stale policy answerYesterday's refund window quoted after it changedCache key must include a docs-corpus version; invalidate on publishCache hit rate against corpus version; answers citing a superseded revisionEvery cached hit is wrong until TTL expires — potentially thousands
Duplicate side effect on retryTwo credits applied, two invoices resentRuntime: idempotency key persisted before the tool call, plus provider-side keyDuplicate idempotency_key insert attempts; finance reconciliationDirect money loss plus a reconciliation task nobody owns
Escalation brief is confidently wrongA human repeats the agent's mistake with authorityBrief template: cite tool-sourced facts, visually separate model hypothesesAgent-disagreement rate — how often the human's resolution contradicts the briefWorse than no brief. The failure that makes support teams distrust the whole system
Golden set goes staleNothing. Everything looks fineEvals: re-mine the golden set from production traces monthly, stratified by failure classDivergence between offline pass rate and live reopen rateYou lose the ability to tell a regression from a bad week
Checklist
The five signals to instrument before launch day
  • Deflection rate, broken out by ticket classAggregate deflection hides everything. A drop from 45% to 40% is usually one class collapsing, not a uniform decline.
  • Reopen rate within 72 hours on agent-resolved ticketsThe single best quality signal in the domain. No judge, no labelling, no eval set. Wire it on day one.
  • Confidence-gate score distribution, weeklyA shift in the distribution precedes a shift in deflection. It is your earliest warning that docs or prompts changed.
  • Tool error rate and result-size p99, per toolPage above 20% error on any single tool. The p99 size catches the unbounded-payload failure before your context window does.
  • Cost per resolved ticket, with a price snapshotStore cost as bigint micros with the rate card id. A vendor price change must not silently rewrite last quarter's report.
  • Agent-disagreement rate on escalated ticketsHow often the human's final resolution contradicts the brief. Harder to instrument, and the only direct measure of brief quality.
Five of these six are a day's work on top of a journal table you already need. The sixth requires the human to record a resolution code, an organisational change rather than an engineering one, which is exactly why it is the one that does not get built.

What does it take to build, in engineer-weeks?

About nine engineer-weeks for a two-person team to a production-grade v1, plus three engineer-days a month of steady-state maintenance. The distribution is lopsided: the agent loop is roughly one week; the retrieval corpus, the confidence gate and evals are five. Anyone quoting this as a two-week build is quoting the demo.

Week one is ingestion and the journal: normalising three channels into one ticket record, the run and step tables, and cost attribution middleware with a price snapshot. It has no demo value and everything after it depends on it. Weeks two and three are the retrieval corpus, the phase underestimated by the widest margin. Chunking documentation is a day. Building the past-ticket pipeline with CSAT filtering, reopen exclusion and PII redaction is the rest.

Week four is the agent runtime with its budgets and journal writes. Week five is the MCP tool surface and the gateway: twelve tools, schema validation, tenant scoping and server-side result bounds. Weeks six and seven are the confidence gate and its calibration, which means building a labelled set from real tickets and measuring where the threshold sits on the deflection-versus-wrong-answer curve. Weeks eight and nine are evals, dashboards, the escalation brief and the work of shipping to a support team who did not ask for this.

Two things routinely get cut and both are mistakes. The first is the escalation brief, cut because it is invisible in a demo, despite applying to the majority of tickets and having the best cost-to-value ratio in the system. The second is threshold calibration, cut because an intuited number appears to work in testing. Then deflection is 30% instead of 45%, or wrong answers reach customers at four times the acceptable rate, and nobody can tell which because the curve was never measured.

To reach a working version faster, compress scope rather than quality: one channel, four ticket classes, six tools, a gate with a conservative threshold. That is closer to four weeks and it is a real system. The general approach to compressing a build like this is in how to build an MVP in days with AI, and the ongoing engineering is what AI product development is for.

Nine engineer-weeks, sequenced
  1. Week 1
    Ingest, journal, cost attribution

    Three channels into one ticket record with thread merging. Run and step tables. Cost middleware with a price snapshot. Zero demo value, and everything else depends on it.

  2. Weeks 2‑3
    The retrieval corpus

    Docs and runbooks are a day. The past-ticket pipeline (CSAT filtering, reopen exclusion, PII redaction before embedding, version and recency metadata) is the other nine. Most underestimated phase in the build.

  3. Week 4
    Agent runtime and budgets

    The loop, step and token and wall-clock budgets, no-progress detection, validated tool calls, journal writes on every step. Smaller than people expect.

  4. Week 5
    MCP tool surface and gateway

    Twelve tools, per-agent allowlist, tenant scoping from request context, schema validation, server-side result bounds, audit log. Two of them are writes and both need approval flows.

  5. Weeks 6‑7
    Confidence gate and calibration

    Span validation, groundedness verifier, class allowlist, and the labelled set you need to actually place the threshold on the deflection-versus-wrong-answer curve. Cut this and you are guessing.

  6. Weeks 8‑9
    Evals, briefs, rollout

    Deterministic CI suite, trajectory scoring, the escalation brief template, dashboards, and shipping to a support team that has to trust it. Shadow mode for two weeks before any customer sees an answer.

Note what is not in week one: the agent. Teams that start with the loop build an impressive demo in three days and then spend two months discovering that the demo was the easy 20%. Starting with the journal feels slow and is the only ordering that ends on time.
Support economics, modelled at 10,000 tickets a month
All-human baseline
Tickets handled by a human
10,000
Average handle time
6.0 min
Fully loaded cost per hour
$22
Model spend
$0
Monthly cost
$22,000
Cost per ticket
$2.20
Agent at 45% deflection with briefs
Resolved by agent
4,500 at $0.073 = $329
Escalated with a brief
5,500 at 4.5 min = $9,075
Model cost on escalated tickets
5,500 at $0.050 = $274
Infrastructure and observability
$400
Amortised build + maintenance
$5,850
Monthly cost
$15,928 · $1.59 per ticket
Modelled saving $6,072/month at 10,000 tickets, and the vendor is cheaper here
This is the comparison most vendor pages will not show you, because it includes the amortised build cost. At 10,000 tickets the in-house build saves money against all-human but loses to $0.99-per-resolution pricing. The saving only becomes decisive above roughly 14,000 tickets a month, and the handle-time assumption, 6 minutes down to 4.5, is the input most worth challenging before you commit.

What should you skip in version one?

Multi-agent orchestration, a dedicated vector database, autonomous write actions, and multilingual support. Each is a real capability and each is a month you spend before finding out whether your deflection rate clears 40%, the only question that matters in the first quarter.

Multi-agent first, because it is the most tempting. A triage agent, a research agent and a resolution agent looks right on a whiteboard and is almost always worse in practice: Anthropic's own guidance reports multi-agent implementations typically consume three to ten times the tokens of a single-agent approach for equivalent tasks. A support ticket is a short-horizon, single-domain task with a clear success criterion, the exact shape where one well-prompted agent with twelve tools wins. The decision procedure is in multi-agent versus single agent.

Skip the dedicated vector database. Postgres with pgvector handles a support corpus of a few million chunks comfortably and keeps your tenant ACLs in the same transaction as your data, which matters more here than anywhere else. Permission-aware retrieval in a multi-tenant support system is not a wrapper you can add later. The full treatment is in RAG over private documents.

Skip autonomous writes. Ship every write tool behind human approval in v1, measure how often the human approves without modification, and only then discuss automating the ones that clear 98%. The order matters: teams that ship autonomous writes first and add approval after an incident get a support organisation that no longer trusts the system, a much harder problem than the incident.

And skip multilingual until the monolingual deflection number is solid. Retrieval over a translated corpus, gate calibration per language and CSAT measurement per language are three separate projects wearing one hat. Do not skip: the journal, cost attribution, the confidence gate, PII redaction in the ticket index, and reopen-rate instrumentation. Those five are about two weeks of the nine and they are the difference between a system you can operate and one you can only apologise for.

Build now, or build when you have a number
pick
Build in v1
Five things, none of them impressive in a demo
  • The run and step journal in your own Postgres, queryable with one SELECT
  • Cost attribution with a price snapshot, stored as bigint micros
  • The confidence gate, with the threshold calibrated on a labelled set from real tickets
  • PII redaction before embedding in the past-ticket index, not before display
  • Reopen-rate instrumentation, the cheapest quality signal in the domain
Defer until measured
Four things you will be sold in month one
  • Multi-agent orchestration: 3-10x tokens for equivalent tasks on Anthropic's own numbers
  • A dedicated vector database: pgvector until you have hit a measured limit
  • Autonomous write actions: approval first, automate what clears 98% approval-without-edit
  • Multilingual: three projects wearing one hat, none until monolingual deflection is stable
The left column is roughly two of the nine weeks. The right column is where most support-AI budgets go in month two, before anyone has measured a deflection rate on live traffic.

Building an AI customer support agent: common questions

How much does an AI customer support agent cost per ticket?

About 7.3 cents in model spend when the agent resolves a ticket end to end and about 5 cents when it escalates, against roughly $2.20 for a fully human ticket at $22/hour and 6 minutes of handle time. Blended at 45% deflection over 10,000 tickets a month, the all-in figure including human time and amortised build is about $1.59 per ticket. These are modelled figures from August 2026 list prices with the token counts printed, not measurements of a deployed system.

What deflection rate should I plan for?

Plan for 40-50% and treat anything higher as upside. Independent write-ups put the production resolution rate for Intercom Fin, the most widely deployed system in the category, in the 42-53% band, against a published headline of 67% across 7,000+ customers. The gap comes from how resolution is counted and from knowledge base quality. Build the business case on the lower number, because a plan that needs 70% deflection fails its first review.

Should I build this or buy an off-the-shelf support agent?

Buy it below roughly 14,000 tickets a month. Intercom documents Fin at $0.99 per resolution, which at 45% deflection is $4,455 a month on 10,000 tickets, against a modelled in-house cost of about $6,580 once you amortise nine engineer-weeks over 24 months and add maintenance. Build when you need tools into systems no vendor integrates with, when the answer policy must be versioned in your own repo for audit, or when support is your product surface rather than a cost centre.

How do you stop the agent giving a confidently wrong answer?

Four independent checks outside the agent. Every factual claim must quote a span that is literally present in a retrieved chunk, which is a string-matching test rather than a model judgement. The ticket class must be on an approved auto-answer list. No write tool may have been used. And a separate cheap model, seeing the answer cold without the reasoning that produced it, must score it above a calibrated threshold. Self-reported confidence is deliberately excluded, because it tracks fluency rather than correctness.

What tools does a support agent actually need?

About twelve: nine reads (customer, subscription, invoices, order status, entitlement, known incidents, past tickets, docs, shipment) and three writes, of which two sit behind human approval and one is escalation. Keep the count low, because every schema costs roughly 150 tokens on every turn, so twelve tools is about 1,800 tokens per turn and 5,400 across a three-turn resolve. Cancellation and large refunds should be deliberately absent: those are conversations, not API calls.

How long does it take to build one?

About nine engineer-weeks for a two-person team to a production-grade v1, plus roughly three engineer-days a month of maintenance. The agent loop itself is about one week. The retrieval corpus is two, the confidence gate and its calibration are two, and evals plus rollout are two. A narrower version (one channel, four ticket classes, six tools, conservative threshold) is closer to four weeks and is a legitimate way to get a real deflection number before committing to the full build.

Does the agent need access to write to the CRM or billing system?

Far less than teams assume. Resending an invoice and applying a bounded goodwill credit cover most of the legitimate write surface, and both should be capped in the tool schema rather than the prompt and gated behind human approval in version one. Cancellations, refunds above a threshold and account deletion should not be exposed at all. Measure how often a human approves a proposed write without modification, and only automate the classes that clear about 98%.

Ready to talk numbers?

Twenty minutes, straight to the engineer. No sales rep, no deck.