Capstone: Ship a Tool-Using Integration
Assemble every piece from this track into a production-grade support-ticket triage service — tool loop, schema output, prompt caching, typed-error retry wrapper, usage ledger, and a deploy-time smoke gate that proves it all works before you ship.
What This Track Built
This track started with a single endpoint: POST /v1/messages. Ten lessons later, you have a complete toolkit for building production software on top of Claude.
Before writing the capstone, it is worth mapping where each lesson's contribution lands in a real system.
The architecture map is not aspirational. Every component has appeared in a prior lesson with working code and a concrete mental model. What the capstone does is put them together into a single service — the support-ticket triage integration — where each piece earns its place by solving a real problem.
The Service: Support Ticket Triage
The target system: an API endpoint that takes a support ticket ID, calls Claude to triage it, and returns a structured verdict — priority level, category, summary, and whether to escalate. This is a realistic production use case that exercises the full track.
Why this scenario? It assembles every track component naturally. It needs a tool loop (to fetch ticket context the model does not have). It needs structured output (the verdict must be a typed object, not free text). It benefits from prompt caching (the triage rules and system instructions are shared across all tickets). It needs error handling and a usage ledger (it processes high volumes and cost matters). And it needs a smoke gate (the deploy pipeline must verify the full path before traffic hits it).
The scenario also mirrors real systems in production. The knowledge OS that powers this academy's AI Tutor, Mission Control's intelligence pipeline, and similar integrations at Tesseract Intelligence all follow this same structural pattern: a frozen system prompt describing the agent's role and rules, a tool loop for fetching context the model does not have at request time, structured output for downstream consumption, and infrastructure for cost control and error recovery.
Building It Layer by Layer
Layer 1: The Frozen System Prompt (the prompt-caching lesson)
The system prompt is your frozen prefix — the triage rules, priority definitions, escalation criteria, and category taxonomy that apply to every ticket. This is the content that makes caching valuable.
import Anthropic from "@anthropic-ai/sdk"
const client = new Anthropic()
const SYSTEM_PROMPT = `You are a support-ticket triage assistant.
[Full triage rulebook — priority definitions, escalation criteria, category taxonomy]`
// In the API call:
const response = await client.messages.parse({
model: "claude-sonnet-4-6",
max_tokens: 1024,
system: [
{
type: "text",
text: SYSTEM_PROMPT,
cache_control: { type: "ephemeral" }, // GA and fully typed on system text blocks
},
],
// ... messages, tools, output_config
})
The cache_control breakpoint goes on the last system block. Requests 2 through N will read the cached prefix at 0.1× cost. If the system prompt is 3,000 tokens, the first request pays 3,750 tokens (3,000 × 1.25× cache write). Every subsequent request pays 300 tokens for that prefix (3,000 × 0.1×). At any meaningful volume, the economics are substantial.
The silent invalidator check is critical before deploying: build the system prompt string, make two identical requests, verify usage.cache_read_input_tokens > 0 on the second. If it is zero, audit every dynamic value in the string construction path.
Layer 2: The Tool Loop (the two tool-use lessons)
The model cannot access your ticket database. It needs a tool. Define the tool schema with a description that tells Claude exactly when to call it — not just what it does:
const TOOLS: Anthropic.Tool[] = [{
name: "get_ticket",
description: "Call this when you need to read the full content of a support ticket before classifying it. Always call this before producing a triage verdict.",
input_schema: {
type: "object",
properties: {
ticket_id: { type: "string", description: "The ticket ID, e.g. TKT-001" },
},
required: ["ticket_id"],
additionalProperties: false,
},
}]
The description phrasing "Always call this before..." is deliberate — conservative models benefit from explicit trigger conditions. A vague "retrieves ticket data" description leaves the model to infer when to call. An explicit instruction removes ambiguity.
The loop itself:
const messages: Anthropic.MessageParam[] = [
{ role: "user", content: "Triage ticket " + ticketId },
]
while (true) {
const response = await client.messages.parse({ model, max_tokens, system, tools, messages, output_config })
if (response.stop_reason === "tool_use") {
// Append the assistant turn verbatim FIRST (the tool-loop lesson rule),
// then send all tool results in ONE user message
messages.push({ role: "assistant", content: response.content })
const toolResults: Anthropic.ToolResultBlockParam[] = []
for (const block of response.content) {
if (block.type === "tool_use") {
toolResults.push({
type: "tool_result",
tool_use_id: block.id,
content: executeTool(block.name, block.input),
})
}
}
messages.push({ role: "user", content: toolResults })
continue
}
if (response.stop_reason === "end_turn") {
return response.parsed_output // the structured verdict
}
throw new Error("Unexpected stop_reason: " + response.stop_reason)
}
All tool results go in ONE user message. This is not a style choice — it is the protocol. A response with two tool_use blocks requires both results in a single user message before the loop continues.
Layer 3: Production-Legal Schema (the structured-output lesson)
The triage verdict must be a typed structured object. With Zod and the SDK helper:
import { z } from "zod"
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod"
const TriageVerdict = z.object({
priority: z.enum(["urgent", "normal", "low"]),
category: z.string(),
summary: z.string(),
suggestedAction: z.string(),
requiresEscalation: z.boolean(),
}).strict() // .strict() = additionalProperties: false in the emitted JSON Schema
The .strict() call is not optional. Without it, the emitted JSON Schema lacks additionalProperties: false, which the Anthropic validator requires on every object. This is the constraint that separates a schema that works in local testing from one that works in production.
Pass it to the API with zodOutputFormat:
output_config: { format: zodOutputFormat(TriageVerdict) }
After the call, response.parsed_output contains the typed object if the request succeeded. Always check stop_reason before trusting parsed_output — a refusal or max_tokens stop_reason means the output may not match the schema.
Layer 4: The Error Wrapper (the production-hardening lesson)
The triage service wraps every API call with typed exception handling and usage logging:
try {
// ... API call
recordUsage(model, response.usage)
return response.parsed_output
} catch (e) {
if (e instanceof Anthropic.RateLimitError) { /* queue and retry */ }
if (e instanceof Anthropic.OverloadedError) { /* longer backoff */ }
if (e instanceof Anthropic.BadRequestError) { /* fix the request */ }
throw e
}
The Ship Gate
The last component is the one that makes the integration trustworthy in CI: the deploy-time smoke gate.
The smoke gate is a function that runs in your deploy pipeline — after build, before traffic. It calls the live API with a known-good input and asserts the full path works:
export async function smokeGate(): Promise<void> {
const result = await triageTicket("TKT-001")
if (!result) throw new Error("Smoke gate: parsed_output is null")
if (!["urgent", "normal", "low"].includes(result.priority)) {
throw new Error("Smoke gate: priority is not a valid enum value")
}
if (typeof result.requiresEscalation !== "boolean") {
throw new Error("Smoke gate: requiresEscalation is not boolean")
}
// A thrown error here blocks the deploy
}
This gate catches what unit tests cannot catch: a schema that the SDK validated locally but the API validator rejects, a tool that returns an unexpected shape, a stop_reason that the loop did not handle. The smoke gate is the line between "the tests are green" and "the system actually works."
The five conditions the gate checks mirror the five ship criteria from the production checklist:
- Schema legal — the live call returned a non-null parsed_output (the API accepted the schema)
- Smoke green — the full tool loop completed with end_turn (no unexpected stop_reason)
- Cache verified — a second identical call shows cache_read_input_tokens > 0
- stop_reason switch total — every stop_reason is handled, no fall-through
- Usage ledger emitting — usage is logged on the response
All five green means ship. Any one failing means fix before deploying.
What You Have Built
Across this track, you have built a complete mental model of the Claude API as a system:
- The Claude API orientation lesson: The three-model family as a deliberate capability/cost ladder. The Models API as live source of truth, not a hardcoded belief.
- The conversations-and-stop-reasons lesson: Stateless message arrays as a design decision. The stop_reason discipline that makes conversations resilient.
- The tool-loop lesson: The tool-use loop as the central abstraction of modern AI engineering — every agentic product is this loop with polish.
- The reliable-tools lesson: Tool quality engineering — descriptions that lift call rate,
is_erroras the feedback channel,tool_choicemodes for control. - The structured-output lesson: The structured output validator rules as hard constraints. The smoke gate as the only proof a schema is production-legal.
- The prompt-caching lesson: Prompt caching economics — the difference between a viable 24/7 agent and one that costs too much to run.
- The adaptive-thinking lesson: Adaptive thinking as the model self-moderating effort. The effort dial as a cost-quality control.
- The delivery-modes lesson: Three delivery modes for three use cases — sync, stream, and the 50%-off Batch API.
- The production-hardening lesson: Typed exceptions most-specific-first. The usage ledger as operational data. Model routing as architecture.
- The capstone lesson: This lesson — assembling the complete system with a smoke gate that proves it works.
The support-ticket triage service is the capstone, but the pattern transfers to any integration: an AI tutor, a research assistant, a content enrichment pipeline, an agentic code reviewer. The components are the same. The assembly is the skill.
Build the smoke gate first. The rest is architecture.