Skills on the Messages API ride the code-execution container, so container.skills and the code_execution tool must both be present in the same request or it fails; no beta header is required.
Why you'd careThe problem it solves
You want a .pptx out of one stateless API call. No agent runtime, no session to drive, no event stream to consume — one request, one response, a file at the end. So you add container={"skills": [...]} to your existing messages.create call and get a 400. It takes another round trip to discover that this is not a parameter you bolt onto a normal request. It is a composition of two features that only works assembled: the skill list, and the execution tool that gives the skill somewhere to run. Either one missing and the request is invalid.
ConceptWhat it is
On the Messages API a skill is not a runtime of its own. It is a set of files staged into the code-execution container — the sandboxed environment Anthropic provisions when you declare the code-execution tool. The skill's instructions enter the model's context; the skill's scripts and assets land on a filesystem the model can reach because that filesystem exists for the execution tool. Take the tool away and there is nowhere to stage anything, which is why the two are inseparable here.
That explains the shape of the request. container is where you say which skills; tools is where you say that there is a container at all. No beta header gates the pair. Anthropic's pre-built document skills — pptx, xlsx, docx, pdf — are referenced by name with type of anthropic; skills you registered yourself are referenced by their skill_id with type of custom.
The boundary against Managed Agents is worth stating precisely, because a correction was needed here. This surface is stateless: one request, one container, no persisted configuration. Managed Agents is stateful and persists the skill set on a versioned Agent object. Both fully support skills. Choosing the Messages API is a choice about statefulness and about who runs the loop — it is not the only path to a generated document, and reaching for an Agent to build a deck is not a wrong turn.
How it worksThe mechanics
The two required pieces, together, on the standard messages endpoint:
response = client.messages.create(
model="claude-opus-5",
max_tokens=16000,
container={"skills": [
{"type": "anthropic", "skill_id": "pptx", "version": "latest"}
]},
tools=[{"type": "code_execution_20260521", "name": "code_execution"}],
messages=[{"role": "user", "content": "Build a 3-slide deck on Q3 churn."}],
)Four details that trip people up:
- It is plain
client.messages.create, with nobetasargument.The Skills API is out of beta and all three code execution tool versions are generally available, so the composed request needs no beta header.skills-2025-10-02still works as an opt-in, but a request that keeps sending it keeps getting the beta response shapes; the legacy code-execution headers remain valid opt-ins too. - Both pieces, not one.
container.skillsand thecode_executiontool do different halves of the job. Sending the skills without the tool is the most common first 400. - Generated files come back as IDs, not bytes.The skill writes into the container; the response carries a file ID per output. Download it through the Files API, which is also out of beta and needs no header.
- Listing available skills is a separate call.
GET /v1/skillsneeds no beta header either; sendskills-2025-10-02there and you get the beta response shapes back.
One thing that used to be open is now documented. The container parameter is overloaded: documented examples pass a bare container ID string to reuse a previous container, and pass an object carrying skills to enable skills. The skills guide now shows the two together — skills plus a warm container in one request — by passing the previous response's container.id inside the object, container={"id": ..., "skills": [...]}, along with the conversation history.
At a glanceSee it
The three-part request that enables a skill on the Messages API, and how the output file gets back to you.
Where it runsSurfaces and availability
| Surface | Status | Notes |
|---|---|---|
| Claude API / Messages API | Yes | Generally available. This is the surface the entry describes: container.skills plus the code-execution tool, with no beta header — not even for the Files API when you upload inputs or download what a Skill produces. Maximum 20 Skills per request. Confirmed on Using Agent Skills with the API (platform.claude.com/docs/en/build-with-claude/skills-guide). |
| Claude Platform on AWS | Yes | Beta. Anthropic-operated with same-day parity: the docs state you use pre-built and custom Agent Skills “with the same container.skills parameter as the Claude API”, and that all four pre-built Skills work out of the box (platform.claude.com/docs/en/build-with-claude/claude-platform-on-aws). |
| Managed Agents | Yes | Skills work here too, but through a different field — skills on the Agent object, not container.skills. Do not port the request shape across, and note the limits differ by more than an order of magnitude: 20 skills per Messages API request versus up to 500 per Managed Agents session. |
| Claude Code | No | Not applicable rather than unsupported: Claude Code resolves skills from local directories and never sends container.skills. |
| Claude Desktop or claude.ai | Unverified | claude.ai does have Skills — pre-built ones active for document creation, custom ones uploaded as zips under Settings > Features, and they require code execution enabled on a Pro, Max, Team or Enterprise plan. But whether the underlying request uses this same container.skills mechanism is not documented, and cannot be settled from outside. |
| Agent SDK | No | Now confirmable. The Agent SDK's skills are filesystem artefacts discovered through settingSources/setting_sources, and the docs state directly that “you create skills as files on disk. The SDK doesn't provide a programmatic API for registering them” (code.claude.com/docs/en/agent-sdk/skills). Nothing stops SDK-hosted code from hand-rolling a container.skills request against the API, but there is no first-class option for it. |
| Amazon Bedrock | No | Neither Agent Skills nor code execution is available; Anthropic's comparison table records Agent Skills as “Not available (requires code execution)” and notes the anthropic-beta header is not supported on Bedrock at all. The entire path is absent, not gated. |
| Google Vertex AI | No | Same. The Claude on Google Cloud page lists Agent Skills, code execution, web fetch and the Files API together under “Features not supported” (platform.claude.com/docs/en/build-with-claude/claude-on-vertex-ai). |
| Microsoft Foundry | Yes — on a Hosted on Anthropic deployment only | Beta, with a hosting gate the availability table shows only as a footnote. Foundry offers two hosting options, and Agent Skills, code execution, the Files API and programmatic tool calling are all listed under “Additional features not supported when hosted on Azure”; requests using them against a Hosted on Azure deployment “return a 400 Bad Request error by design”. Choosing Default settings in the Foundry portal provisions Hosted on Azure for any model offering both, so the deployment that works here is the one you have to opt into. Source: platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry. |
| OpenAI, Google Gemini APIs | No | These are Anthropic parameters on Anthropic's endpoint. Other vendors' skill features are separate mechanisms with separate request shapes. |
The pattern is that skills follow code execution. Every surface where code execution exists carries skills; every surface where it does not, does not — and the vendor docs bear this out row by row, with Vertex listing Agent Skills and code execution in the same “not supported” block and Bedrock refusing beta headers outright. That makes availability easy to predict, and it makes Foundry the interesting case twice over. It supports this stateless path but not Managed Agents — so a document pipeline built on the Messages API ports there and one built on Agents does not — and even the stateless path is conditional on picking the Hosted on Anthropic deployment. Verify the hosting option before you promise a Foundry customer a delivery date: the failure mode is not a missing feature flag but a hard 400, and the portal's default settings land on the wrong side of it.
ExampleIn the real world
A finance tool needs to hand users a formatted .xlsx of their monthly reconciliation. There is no chat interface, no long-running task, just a nightly job that produces a file.
The service sends one request: model, max_tokens, container={"skills": [{"type": "anthropic", "skill_id": "xlsx"}]}, the code_execution_20260521 tool, and a user message containing the reconciliation rows plus the required column layout.
Server-side, a container comes up, the Excel skill's instructions and helper files are staged into it, and the model's context gains the skill's guidance on how to build a spreadsheet properly — sheet structure, formula construction, number formatting. The model then writes and runs Python in that container using those conventions rather than inventing its own.
The response comes back as an interleaved stream of text and tool-result blocks, with a file ID for the workbook the container wrote. The service takes that ID, calls the Files API — no beta header needed — and writes the bytes to object storage.
Total server-side state held by the caller: none. That is the reason to pick this surface over an Agent. If the next month's run needs different behaviour, it is a different request, not a new agent version.
Not thisWhat it is often confused with
- Not Managed Agentsdifferent namespace, different field, different lifecycle. Managed Agents puts
skillson a persisted Agent object and runs the loop for you. This surface is one stateless request where you own the loop. A prior version of this catalogue implied Agents were the wrong path for document generation; that was wrong, and they support skills fully. - Not a toolskills are declared in
container, not intools. The only thing intoolshere iscode_execution. The model never emits atool_useblock naming a skill. - Not the Skills API
/v1/skillsis the registry where custom skill objects are created and versioned.container.skillsonly references what already exists. Anthropic's pre-built skills need no registration; yours do. - Not code execution on its owndeclaring the execution tool without
container.skillsgives the model a sandbox and no expertise. Declaring skills without the tool gives it expertise and nowhere to apply it. The pairing is the feature. - Not a local SKILL.md folderthe API cannot read your filesystem. A folder must be registered through the Skills API before
container.skillscan name it.
LimitsWhen not to reach for it
- The work is genuinely long-running or multi-turn.If the task needs a persistent workspace, mounted repos, or many turns of tool use, use Managed Agents — skills are supported there and you stop hand-rolling a loop.
- You need the same skill set enforced across many callers.Repeating the container block in every call site means it drifts. Put the skills on a versioned Agent object instead and let sessions point at it.
- You are targeting Bedrock or Vertex.There is no request shape that makes this work there. Move the deployment to Claude Platform on AWS, or generate the document with your own library and use the model only for content.
- What you want is a callable function.If the requirement is a deterministic action with validated arguments, define a tool with a schema. A skill will not give you a typed contract.
- The user needs to see progress.A single stateless request has no event stream. If you need per-step visibility, that is a reason to move to a session-based surface.
Verified 2026-09-12. Moves on a scale of months. Re-check before you depend on it. Provider: Anthropic.