Home › Agent Skills › Enabling pre-built skills on the API (container.skills + code execution)
Agent Skills · Build

Enabling pre-built skills on the API (container.skills + code execution)

The wiring contract: skills only run inside the code-execution container, are selected per-request via container.skills, and their output comes back through the Files API.

In one line

Two things must be present together — container.skills and the code-execution tool, with no beta header needed any more — and if the tool is missing you get a plain text answer with no file and no error.

Why you'd careThe problem it solves

You added the skill reference, sent the request, and got back a competent description of the spreadsheet you asked for. No file. No error. No hint about what went wrong. This is the characteristic failure of enabling skills on the Messages API, and it is nasty precisely because it looks like success: HTTP 200, sensible prose, a model that seems to have understood the task. What actually happened is that the code-execution tool was absent, no container ever started, and Claude answered the only way it could — in text. Then there is a second trap waiting: the legacy beta header code-execution-2025-08-25, which older examples still carry, and the tool type code_execution_20260521 are two different version strings that look like the same thing. Copying one into the other's slot is a 400 you will stare at.

ConceptWhat it is

This is the wiring contract rather than a skill in itself: the set of request fields that must appear together for a pre-built skill to actually run on the Messages API, and the path the resulting artefact takes back to you.

Two pieces, both required. No beta header is part of the contract any more: Skills, the code-execution tool that skills run inside, and the Files API are all out of beta. The container.skills array selects which skills are mounted, per request, by ID. The code-execution tool declaration in tools is what actually provisions the container; without it there is no filesystem for a skill to exist on.

Selection is per request, not per account, which is the design point most worth internalising. You are not turning a feature on somewhere. Each call decides its own skill set, so one request can mount xlsx, the next pptx, and the one after neither. Progressive disclosure makes that cheap — an unmatched skill costs roughly its name and description in tokens.

The exit path is the third moving part and the one people forget: artefacts are written to the container filesystem, not into the response. The response carries file IDs. The bytes come from the Files API. A pipeline that never calls it has no output.

How it worksThe mechanics

The full shape, as raw HTTP:

code
POST /v1/messages
anthropic-version: 2023-06-01

{
  "model": "claude-opus-5",
  "max_tokens": 16000,
  "container": {
    "skills": [
      {"type": "anthropic", "skill_id": "xlsx", "version": "latest"}
    ]
  },
  "tools": [
    {"type": "code_execution_20260521", "name": "code_execution"}
  ],
  "messages": [{"role": "user", "content": "..."}]
}

On headers, the sources used to disagree and now agree: send none. The code-execution tool page says none of its three tool versions (20250825, 20260120, 20260521) requires an anthropic-beta header, and the skills guide says “The Skills API is out of beta and needs no beta header”. The Files API is out of beta too. The old headers — code-execution-2025-08-25, skills-2025-10-02, files-api-2025-04-14 — still work as opt-ins, but a request that keeps sending skills-2025-10-02 keeps receiving the beta response shapes, so a header left in for safety silently pins old field names and version formats. Drop it.

Now the version-string trap. The legacy header date and the tool type version are independent. The latest tool type is code_execution_20260521 while the legacy header is code-execution-2025-08-25 — the tool has been revised repeatedly without the header date moving. Read them as two separate identifiers that happen to share a word, and pin the tool type from current documentation rather than inferring it from an old header.

Two further details. The container parameter is polymorphic in the documented examples: an object with a skills array to configure a new container, or a bare container ID string to reuse a previous one. The two compose: the skills guide's multi-turn example passes {"id": ..., "skills": [...]} as one object, carrying the previous response's container ID alongside the skills. And on the Managed Agents surface over raw HTTP, every request still carries the managed-agents-2026-04-01 beta header, so the header set differs from the Messages API's, which now needs none.

At a glanceSee it

Enabling pre-built skills on the API (container.skills + code execution) diagram

All three request pieces must be present; missing one degrades silently into a text answer.

Where it runsSurfaces and availability

SurfaceStatusNotes
Claude CodeNoNo container.skills concept. Skills here are folders on disk, discovered by the harness — a different loading model with a different failure mode.
Claude API / Messages APIYesGenerally available, with no beta header. This contract is defined for exactly this surface.
Managed AgentsNoSkills exist but the wiring is different: a skills array on the agent object, up to 500 skills per session counted across every agent in it, with the built-in toolset rather than a declared code_execution tool. Do not copy this request shape there.
Claude Desktop / claude.aiNoSkills are toggled in the product UI. There is no request body for you to construct.
Claude Agent SDKUnverifiedNo documented container.skills equivalent; it builds on the Claude Code harness rather than raw Messages requests.
Claude Platform on AWSYesBeta. Same request shape; SigV4 auth and a required workspace ID instead of an API key.
Amazon BedrockNoNeither the skills parameter nor the code-execution tool exists. The request will not work no matter how the headers are arranged.
Google Vertex AINoSame — no code-execution tool, so no container.
Microsoft FoundryYesBeta for skills; code execution is generally available there. Both require a Hosted on Anthropic deployment.
OpenAI / Google Agent SkillsNoDifferent vendors, different mechanisms entirely. There is no container.skills analogue to port.

The split worth designing around: this exact request shape is a Messages API contract and does not transfer. Managed Agents puts skills on a persisted agent object; Claude Code puts them on disk; Bedrock and Vertex have nowhere to put them. If your architecture may move between these, keep the skill selection behind one function in your code, because the field names, the object it hangs off, and the tool that must accompany it all change with the surface.

ExampleIn the real world

A team ships an endpoint that turns a support-ticket export into a workbook. The first version returns HTTP 200 with a paragraph describing what the workbook would contain, and no file. Nothing in the response says anything is wrong.

They diff their request against the contract. container.skills is there with xlsx. The tools array is empty — they had removed a custom tool during refactoring and, since the skill was still declared, nothing looked missing. No code-execution tool means no container; no container means the skill has nowhere to live; and Claude, asked for a spreadsheet with no way to make one, described it instead.

They add the tool back and get a 400: their pinned constant was code-execution-2025-08-25, the legacy beta header string pasted into the tool's type slot. With {"type": "code_execution_20260521", "name": "code_execution"} the next call returns a file ID.

The download that follows needs no beta header at all — the Files API is out of beta. Two separate failures, two different signatures: silent text, and a tool-type 400. Anyone who has enabled skills has hit at least one of them.

Not thisWhat it is often confused with

  • Not an account-level settingthere is nothing to switch on in a console. Skill selection is per request, in the request body, and a request that omits it simply has no skills.
  • Not a tool definitioncontainer.skills entries have no input_schema and are never called. They mount files; the only tool in the request is code_execution.
  • Not the Managed Agents skills fieldthose hang off the agent object, cap at 500 per session, and pair with the built-in agent toolset. Same skill IDs, different plumbing, and the shapes are not interchangeable.
  • Not a way to load custom skills by foldera skill directory on your laptop is invisible to the API. Custom skills must be registered first, through the Skills API, before any request can name one.
  • Not enough on its own to get the filethe artefact lives on the container filesystem. Without a Files API call you have an ID and nothing to open.

LimitsWhen not to reach for it

  • The task needs no artefact.Mounting a skill and provisioning a container to answer a question in text is pure overhead. Send a plain request.
  • You need portability across Bedrock or Vertex.This contract does not exist there. If one deployment must run everywhere, do the file production in your own code.
  • You want a persistent, versioned configuration.Re-sending the same skills array on every call is a smell. Managed Agents stores model, system prompt, tools and skills on a versioned agent object — that is what it is for.
  • You are inside Claude Code.The container path is not available and not needed; write a local SKILL.md folder and let the harness discover it.
  • You cannot let the skill change under you."version": "latest" follows Anthropic's updates to a pre-built skill, and the tool type is versioned. If your change-control process cannot absorb that, pin a dated skill version such as 20251013, or build the document generation yourself.
Checked

Verified 2026-09-12. Moves in weeks. Treat anything specific here as a starting point, not a fact. Provider: Anthropic.

A living map of modern AI — kept current every morning