Compaction is a beta Claude API feature (header anthropic-beta: compact-2026-01-12) that summarizes older conversation content server-side as a session approaches the context limit. You enable it under context_management.edits with a compact_20260112 edit and a trigger threshold (default 150,000 input tokens, minimum 50,000). When the trigger fires, the model writes a summary into a special compaction content block; on your next request you append the entire response to messages, and the API automatically drops everything before the compaction block. For long-running agents it's a lifeline. It also introduces two behaviors that break naive client code.
Behavior 1: a stop reason your switch statement doesn't know
With the optional pause_after_compaction: true, the response stops right after the summary with stop_reason: "compaction" so you can handle continuation yourself — inspect the summary, adjust caching, then reissue. A long session can therefore pause mid-workflow with a stop reason that isn't end_turn, max_tokens, or tool_use. Code that treats unknown stop reasons as errors (or, worse, as completion) will misbehave exactly in the sessions long enough to need compaction. The correct handling is mechanical: on "compaction", append the full response to the message history and continue the request loop.
In a stream, the compaction block has a distinctive rhythm: a content_block_start, then a single content_block_delta carrying the entire summary at once, then content_block_stop. If your UI renders deltas as they arrive, expect one large chunk rather than a token-by-token trickle — worth special-casing so users don't see a wall of summary text flash into an assistant reply.
Behavior 2: the usage fields stop meaning what they meant
Compaction runs as extra model work inside your request — and a single request can compact more than once. The response's usage.iterations array lists each compaction iteration and the message iteration separately. The trap: the top-level input_tokens and output_tokens fields do not include the compaction iterations. Any billing reconciliation, cost dashboard, or per-request budget check that reads only the top-level numbers undercounts every compacted request. The true billed amount is the sum across all iterations:
def billed_tokens(usage):
its = getattr(usage, "iterations", None) or []
if not its:
return usage.input_tokens, usage.output_tokens
return (sum(i.input_tokens for i in its),
sum(i.output_tokens for i in its))
This matters doubly in streaming, where usage arrives incrementally: token counts in message_delta events are cumulative (never sum the deltas), and the compaction accounting comes on top of that rule.
usage — billing exports, cost alerts, token budgets. If any of them read only top-level input_tokens/output_tokens, they are now underreporting.Keeping prompt caching alive across compactions
Compaction rewrites the front of your conversation, which is exactly where prompt-cache prefixes live. Anthropic's guidance: put cache_control: {"type": "ephemeral"} on compaction blocks and cache the system prompt separately, so a new compaction doesn't invalidate the system-prompt cache entry. Also useful: /v1/messages/count_tokens applies existing compaction blocks (without ever triggering new compaction) and returns context_management.original_input_tokens alongside the post-compaction count — a clean way to measure how much context compaction is actually saving you.
Availability notes
Compaction supports Claude Fable 5, Mythos 5 and Mythos Preview, Opus 4.8/4.7/4.6, Sonnet 5, and Sonnet 4.6, and is eligible for Zero Data Retention. It is a beta feature of the Anthropic API surface; if you run on a third-party platform, confirm current support in that platform's documentation before depending on it — beta features do not land everywhere at once (Claude Platform on AWS passes beta headers through like the first-party API; Bedrock does not support the anthropic-beta header).
Where to go next
For the feature end to end, read context compaction explained. For the stop-reason landscape your handler should cover, see stop reasons, and for the event grammar, streaming event types.