## Compatibility ## How on-demand compaction works ## Request a summary ## Continue from the summary ### Compact again ## Compact in a loop ### When to compact ## Write your own summarization prompt ## Handle a missing summary or an error ### Errors ## Count compaction usage ## Limits and interactions with other features
The whole hunk
821 lines, new pageA whole new page. There's nothing to diff it against, so here is what it says.
---
title: Compaction on demand
url: https://platform.claude.com/docs/en/build-with-claude/compaction-on-demand
description: Ask Claude to summarize a conversation when your application chooses, then continue from the summary.
---
## Compatibility
- Status: Beta
- [Beta header](https://platform.claude.com/docs/en/api/beta-headers): `compact-2026-09-04`
- Supported models: `claude-fable-5-1`, `claude-mythos-5-1`, `claude-fable-5`, `claude-mythos-5`, `claude-mythos-preview`, `claude-opus-5`, `claude-opus-4-8`, `claude-opus-4-7`, `claude-opus-4-6`, `claude-sonnet-5`, `claude-sonnet-4-6`
- Platforms: Claude API (beta), Claude Platform on AWS (beta), Microsoft Foundry (beta); not available on Amazon Bedrock, Google Cloud
With on-demand compaction, your application decides when a conversation is summarized: you send one request with the `compaction` parameter, and Claude returns a summary in place of a reply.
## How on-demand compaction works
A compaction request is separate from your conversation turns. You send the conversation as it stands with the `compaction` parameter, and the response contains a single `compaction` block. The block holds the summary as text you can read, and a signature. Send it in future requests exactly as it came.
From then on the block takes the place of the messages it summarizes. It goes first in `messages`, the summarized messages are removed, and your next turn follows it. Claude sees the summary where those messages were.

## Request a summary
Send the `compact-2026-09-04` beta header on the request that asks for the summary and on every later request that carries the signed block. To check whether a model supports on-demand compaction, call the [Models API](https://platform.claude.com/docs/en/api/beta/models/list) with the beta header and read each model's `capabilities.compaction`. You can't combine `compaction` with `context_management` on one request.
Send the conversation as it stands with `"compaction": {"type": "summarize"}`. The API summarizes every message in the request once, generates no reply after it, and returns the block alone with `stop_reason` `"compaction"`. Send the same `system` prompt and `tools` that you use for the rest of the conversation. The summarizer reads them, and if you keep turns after the block on a model with [preserved thinking](https://platform.claude.com/docs/en/build-with-claude/preserved-thinking), the thinking in those turns stays valid only if `system` and `tools` match. The conversation in this example has no `system` prompt or tools, so the request sends neither:
<CodeGroup>
```bash cURL
# max_tokens caps the whole call, including any thinking, so allow several thousand tokens.
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: compact-2026-09-04" \
-H "content-type: application/json" \
-d '{
"model": "claude-opus-5",
"max_tokens": 4096,
"messages": [
{"role": "user", "content": "I am building a recipe app. Help me name the main entities in the data model."},
{"role": "assistant", "content": "Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe."},
{"role": "user", "content": "Good. Now suggest field names for Recipe."}
],
"compaction": {"type": "summarize"}
}'
```
<MultiFileExample language="cli" label="CLI">
```bash CLI
ant beta:messages create --beta compact-2026-09-04 < request.yaml
```
<File filename="request.yaml">
```yaml
model: claude-opus-5
# max_tokens caps the whole call, including any thinking, so allow several thousand tokens.
max_tokens: 4096
messages:
- role: user
content: I am building a recipe app. Help me name the main entities in the data model.
- role: assistant
content: Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe.
- role: user
content: Good. Now suggest field names for Recipe.
compaction:
type: summarize
```
</File>
</MultiFileExample>
```python Python
from anthropic.types.beta import BetaMessageParam
client = anthropic.Anthropic()
history: list[BetaMessageParam] = [
{
"role": "user",
"content": "I am building a recipe app. Help me name the main entities in the data model.",
},
{
"role": "assistant",
"content": "Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe.",
},
{"role": "user", "content": "Good. Now suggest field names for Recipe."},
]
response = client.beta.messages.create(
model="claude-opus-5",
# max_tokens caps the whole call, including any thinking, so allow several thousand tokens.
max_tokens=4096,
betas=["compact-2026-09-04"],
messages=history,
compaction={"type": "summarize"},
)
print(f"Stop reason: {response.stop_reason}")
```
```typescript TypeScript
const client = new Anthropic();
const history: Anthropic.Beta.Messages.BetaMessageParam[] = [
{
role: "user",
content: "I am building a recipe app. Help me name the main entities in the data model."
},
{
role: "assistant",
content:
"Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe."
},
{ role: "user", content: "Good. Now suggest field names for Recipe." }
];
const response = await client.beta.messages.create({
model: "claude-opus-5",
// max_tokens caps the whole call, including any thinking, so allow several thousand tokens.
max_tokens: 4096,
betas: ["compact-2026-09-04"],
messages: history,
compaction: { type: "summarize" }
});
console.log(`Stop reason: ${response.stop_reason}`);
```
```csharp C#
using Anthropic.Models.Beta;
using Anthropic.Models.Beta.Messages;
using Model = Anthropic.Models.Messages.Model;
AnthropicClient client = new();
List<BetaMessageParam> history =
[
new()
{
Role = Role.User,
Content = "I am building a recipe app. Help me name the main entities in the data model.",
},
new()
{
Role = Role.Assistant,
Content = "Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe.",
},
new() { Role = Role.User, Content = "Good. Now suggest field names for Recipe." },
];
var response = await client.Beta.Messages.Create(new MessageCreateParams
{
Model = Model.ClaudeOpus5,
// max_tokens caps the whole call, including any thinking, so allow several thousand tokens.
MaxTokens = 4096,
Betas = [AnthropicBeta.Compact2026_09_04],
Messages = history,
Compaction = new BetaCompactionConfig(), // type defaults to "summarize"
});
Console.WriteLine($"Stop reason: {response.StopReason?.Raw()}");
```
```go Go
client := anthropic.NewClient()
history := []anthropic.BetaMessageParam{
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("I am building a recipe app. Help me name the main entities in the data model.")),
{
Role: anthropic.BetaMessageParamRoleAssistant,
Content: []anthropic.BetaContentBlockParamUnion{anthropic.NewBetaTextBlock("Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe.")},
},
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Good. Now suggest field names for Recipe.")),
}
response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
Model: anthropic.ModelClaudeOpus5,
// max_tokens caps the whole call, including any thinking, so allow several thousand tokens.
MaxTokens: 4096,
Betas: []anthropic.AnthropicBeta{anthropic.AnthropicBetaCompact2026_09_04},
Messages: history,
Compaction: anthropic.BetaCompactionConfigUnionParam{
OfSummarize: &anthropic.BetaSummarizeCompactionParam{},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println("Stop reason:", response.StopReason)
```
```java Java
import com.anthropic.models.beta.AnthropicBeta;
import com.anthropic.models.beta.messages.BetaCompactionConfig;
import com.anthropic.models.beta.messages.MessageCreateParams;
void main() {
var client = AnthropicOkHttpClient.fromEnv();
var params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5)
// max_tokens caps the whole call, including any thinking, so allow several thousand tokens.
.maxTokens(4096)
.addBeta(AnthropicBeta.COMPACT_2026_09_04)
.addUserMessage("I am building a recipe app. Help me name the main entities in the data model.")
.addAssistantMessage("Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe.")
.addUserMessage("Good. Now suggest field names for Recipe.")
.compaction(BetaCompactionConfig.builder().build()) // type defaults to "summarize"
.build();
var response = client.beta().messages().create(params);
response.stopReason().ifPresent(reason -> IO.println("Stop reason: " + reason));
}
```
```php PHP
use Anthropic\Beta\AnthropicBeta;
use Anthropic\Beta\Messages\BetaCompactionConfig;
use Anthropic\Beta\Messages\BetaMessageParam;
use Anthropic\Beta\Messages\BetaMessageParam\Role;
$client = new Client();
$history = [
BetaMessageParam::with(
role: Role::USER,
content: 'I am building a recipe app. Help me name the main entities in the data model.',
),
BetaMessageParam::with(
role: Role::ASSISTANT,
content: 'Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe.',
),
BetaMessageParam::with(role: Role::USER, content: 'Good. Now suggest field names for Recipe.'),
];
$response = $client->beta->messages->create(
model: Model::CLAUDE_OPUS_5,
// max_tokens caps the whole call, including any thinking, so allow several thousand tokens.
maxTokens: 4096,
betas: [AnthropicBeta::COMPACT_2026_09_04],
messages: $history,
compaction: BetaCompactionConfig::with(), // type defaults to 'summarize'
);
echo "Stop reason: {$response->stopReason}", PHP_EOL;
```
```ruby Ruby
client = Anthropic::Client.new
history = [
{
role: "user",
content: "I am building a recipe app. Help me name the main entities in the data model."
},
{
role: "assistant",
content: "Start with Recipe, Ingredient, and Step. Add a RecipeIngredient entry that holds the quantity and unit for each ingredient in a recipe."
},
{ role: "user", content: "Good. Now suggest field names for Recipe." }
]
response = client.beta.messages.create(
model: Anthropic::Model::CLAUDE_OPUS_5,
# max_tokens caps the whole call, including any thinking, so allow several thousand tokens.
max_tokens: 4096,
betas: [Anthropic::AnthropicBeta::COMPACT_2026_09_04],
messages: history,
compaction: { type: "summarize" }
)
puts "Stop reason: #{response.stop_reason}"
```
</CodeGroup>
```json Response
{
"id": "msg_013Zva2CMHLNnXjNJJKqJ2EF",
"type": "message",
"role": "assistant",
"model": "claude-opus-5",
"content": [
{
"type": "compaction",
"content": "Summary of the conversation: the user is designing the data model for a recipe app. The entities agreed so far are Recipe, Ingredient, Step, and RecipeIngredient, which holds the quantity and unit. The user then asked for field names for Recipe.",
"signature": "EuYBCkQY..."
}
],
"stop_reason": "compaction",
"usage": {
"input_tokens": 0,
"output_tokens": 0,
"iterations": [{ "type": "compaction", "input_tokens": 144, "output_tokens": 276 }]
}
}
```
The summarization call uses the request's model, `system`, `tools`, thinking settings, and `max_tokens`. The summarizer reads the tool definitions but never runs a tool, and the response carries no thinking. `max_tokens` caps the whole call, including any thinking the model does before it writes the summary, so allow several thousand tokens. [Count compaction usage](https://platform.claude.com/docs/en/build-with-claude/compaction-on-demand#understanding-usage) shows how the call is billed.
If the last `assistant` turn ends in a tool call with no result yet, the API rejects the request. Send that turn's tool results first. Also leave out `stop_sequences`, structured-output `output_config.format`, and a `tool_choice` of type `any` or `tool`. They would do nothing on a summarization call, and the API rejects them. The conversation must still fit the model's context window, so compact before you outgrow it, not after.
When you stream the response, the block arrives whole. You get one `content_block_start` event carrying the complete block, then `content_block_stop`, with no `content_block_delta` events. `ping` events can arrive before or between them.
Cut at 300 lines. The page has the rest.