# Claude API Agent Skills: use a ready-made skill without Claude Code

> Connect a pptx skill with container.skills and code execution, inspect the response, and download its presentation through the Claude Files API.

Source: https://ailoot.ai/blog/en/claude-api-agent-skills/
Canonical: https://ailoot.ai/blog/en/claude-api-agent-skills/
Language: en
Publisher: Ailoot (https://ailoot.ai/)
Translation: guide-039
Revision: sha256-a59ee4373986ae6802d61b9cca780252bf8070e7ffde17fbfec54a96f60c1fb3


<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaway for this section">

Key takeaway

Ready-made Agent Skills for documents can be used through the direct Claude API: specify the skill in `container.skills` and enable `code_execution`. For your first request, use the compatible model and tool pairing from the official example. Download the generated file separately using its `file_id`; the example uses the same API key for the request and the download.

</div>



You can find additional instructions in the [Ailoot documentation](https://ailoot.ai/docs/).

## What do you need to use Claude API Agent Skills?

This guide is for calling the API directly from a program, rather than installing a skill in Claude Code. You need a Claude API key, access to the selected model, and Python 3. The code below uses the Python standard library and HTTP, so it does not depend on which SDK version you have installed. The key is passed through the `ANTHROPIC_API_KEY` environment variable; do not put it in the program text or a conversation.

The API offers ready-made Anthropic skills: `pptx` for presentations, `xlsx` for spreadsheets, `docx` for Word documents, and `pdf` for PDFs. They are connected using `type: "anthropic"`. This guide uses a ready-made skill, without uploading a custom one.

The [official quickstart](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/quickstart.md), opened when this material was prepared, uses the pairing `claude-opus-5-5` and `code_execution_20260521`. That is the pairing used below. This example has been checked against the documentation; it is not a report of a paid API run.

## Which beta headers are needed

The example follows the quickstart's current HTTP contract: the headers are `x-api-key`, `anthropic-version: 2023-06-01`, and `content-type: application/json`; it does not send `anthropic-beta`. Do not combine a model, a calling method, and a response format from different generations of the documentation.

The [Files API documentation](https://platform.claude.com/docs/en/build-with-claude/files.md) also states that the API is out of beta. The old `files-api-2025-04-14` header retains the old response formats; removing the header switches them to the current formats. That is why an old example using `client.beta.files` cannot simply be combined with a new one.

We are not migrating an existing service from the old beta contract here. If you already have such a service, check its headers and response formats first. The word `beta` in an SDK method name is not enough on its own: the Files API documentation separately explains how different SDK versions behave.





## Create a presentation and download the file

The example sends a task to create five slides about renewable energy, saves the entire response to `skill-response.json`, and then extracts the IDs of the generated files. It does not take a save path from the model's response: the files are saved in a new local folder called `skill-output`. If that folder already exists, the program stops so that it does not overwrite the previous result. The folder is created before the API call, so it also remains after a failed call. Inspect and retain that attempt before doing anything again; do not delete it merely to make a rerun pass.

```python
import json
import os
from pathlib import Path
from urllib.error import HTTPError
from urllib.request import Request, urlopen

API = "https://api.anthropic.com/v1"
KEY = os.environ["ANTHROPIC_API_KEY"]
HEADERS = {
    "x-api-key": KEY,
    "anthropic-version": "2023-06-01",
    "content-type": "application/json",
}

def request(path, payload=None, binary=False):
    data = None if payload is None else json.dumps(payload).encode("utf-8")
    req = Request(API + path, data=data, headers=HEADERS)
    try:
        with urlopen(req, timeout=600) as result:
            body = result.read()
    except HTTPError as error:
        try:
            details = json.loads(error.read().decode("utf-8"))
        except (ValueError, UnicodeError):
            details = {}
        info = details.get("error", {})
        request_id = error.headers.get("request-id") or details.get("request_id")
        diagnostic = {
            "http_status": error.code,
            "request_id": request_id,
            "error_type": info.get("type"),
            "message": str(info.get("message", "No JSON error message")).replace(KEY, "[REDACTED]")[:1000],
        }
        (out / "skill-error.json").write_text(
            json.dumps(diagnostic, ensure_ascii=False, indent=2), encoding="utf-8"
        )
        raise RuntimeError(
            f"Claude API HTTP {error.code}; request_id={request_id}; inspect skill-error.json"
        ) from None
    return body if binary else json.loads(body)

out = Path("skill-output")
out.mkdir(exist_ok=False)
response = request("/messages", {
    "model": "claude-opus-5-5",
    "max_tokens": 16000,
    "container": {
        "skills": [{"type": "anthropic", "skill_id": "pptx", "version": "latest"}]
    },
    "messages": [{
        "role": "user",
        "content": "Create a presentation about renewable energy with 5 slides in my preferred language.",
    }],
    "tools": [{"type": "code_execution_20260521", "name": "code_execution"}],
})
(out / "skill-response.json").write_text(
    json.dumps(response, ensure_ascii=False, indent=2), encoding="utf-8"
)
print("stop_reason:", response.get("stop_reason"))

file_ids = []
for block in response["content"]:
    if block["type"] == "bash_code_execution_tool_result":
        result = block["content"]
        if result["type"] == "bash_code_execution_result":
            for item in result["content"]:
                file_id = item.get("file_id")
                if file_id and file_id not in file_ids:
                    file_ids.append(file_id)
if not file_ids:
    raise RuntimeError("No generated file_id; inspect skill-response.json")

for index, file_id in enumerate(file_ids, start=1):
    from urllib.parse import quote
    file_path = "/files/" + quote(file_id, safe="")
    metadata = request(file_path)
    if metadata.get("downloadable") is not True:
        raise RuntimeError("This file cannot be downloaded through Files API")
    target = out / f"result-{index}.pptx"
    target.write_bytes(request(file_path + "/content", binary=True))
    print("Saved:", target)
```

The names `result-1.pptx` and so on were chosen for this presentation task. A file extension does not prove what the file contains: open the downloaded document and check its format, slide count, and text. Do not treat HTTP 200 alone, or the presence of a `file_id`, as a successful result.

The program prints `stop_reason`, the reason the response stopped, and saves the full response for inspection. It does not continue the conversation or automatically repeat the paid request. Having a downloaded file does not mean the whole task is complete: inspect the saved response and the document itself before deciding whether another request is needed.

For a failed attempt, inspect `skill-error.json` if present. It records the HTTP status, request ID when supplied, and a bounded provider error message with the configured API key removed. Review the message for other private content before sharing it. Compare authentication errors, permission/model errors, billing limits, and request-format errors with the [official error reference](https://platform.claude.com/docs/en/api/errors); do not infer that all failures need another paid generation.

If `skill-response.json` already contains generated `file_id` values and only a download failed, retain that response and diagnose the file metadata/download step using the same authorized workspace access. Do not run the generation block again just to download an existing output. If the request outcome is unknown, preserve its request ID and resolve its status before sending another generation. Only after deciding that a new generation is needed, change the local output line to an unused path, for example `out = Path("skill-output-attempt-2")`, and retain `exist_ok=False`. A fresh folder prevents overwriting; it does not prevent a duplicate API charge.

## What file permissions are needed

The Files API associates files with a workspace, rather than with an individual user of your application or a conversation. According to the documentation, any API key with access to that workspace can access files uploaded there. The example uses the same key for downloading as for generation. Never accept a `file_id` from end users or other untrusted sources. Store these IDs on the server and maintain the mapping between users and their files in your own application.

Check the file metadata separately: it must contain `downloadable: true`. The documentation allows downloading files generated by skills or the code execution tool; files you upload have `downloadable: false`, and an attempted download returns 400. Access to the workspace and the ability to download a particular file are separate requirements.

For a multi-user application, the Files API documentation recommends a separate workspace for each client as the file isolation boundary. This recommendation is specifically about files; do not extend it to all entities and permissions without checking the relevant API.

## Ready-made skills and custom skills

Ready-made skills have the IDs `pptx`, `xlsx`, `docx`, and `pdf`. In the [skills list reference](https://platform.claude.com/docs/en/api/skills/list.md), the `custom` source is described as private to the workspace, while `anthropic` is shared and read-only.

`version: "latest"` in the first example means the latest published version of the skill. For a reproducible integration, bear in mind that this selection can change; switching to a fixed version requires choosing an existing version of the skill you use, rather than an invented ID from an example.

## Limitations and the next step

The example covers only creating a presentation with a ready-made skill and downloading it. Uploading custom skills, deleting them, and accessing third-party services have not been tested here. Those tasks require a separate check of the relevant API.

If your task involves files in the Claude interface, there is a separate guide on [how to upload a file to Claude](https://ailoot.ai/blog/en/claude-file-upload/). For direct calls, see [how to calculate API token usage](https://ailoot.ai/blog/en/api-token-usage-calculation/). For work in a terminal agent, read [how to choose Claude Code skills](https://ailoot.ai/blog/en/choose-claude-code-skills/): they are connected in a different way.

For the first check, one ready-made `pptx` skill is enough: inspect the response, download a file that is permitted to be downloaded, and open it. Add custom skills and access separation between clients after that check.





When you are ready, get to know [Ailoot](https://ailoot.ai/).

## Questions about Claude API Agent Skills

### Why is a file_id not enough to declare the presentation finished?

The ID shows that a file was returned, not that it is a usable presentation. Check its metadata for `downloadable: true`, download it with workspace access, and open it to verify the file format, slide count, and text.

### Can I download any file uploaded to the Claude Files API?

No. The guide distinguishes generated files marked `downloadable: true` from uploaded files marked `downloadable: false`. Workspace access and permission to download a particular file are separate checks; do not accept arbitrary file IDs from end users.


## Ailoot · The company and its project portfolio

Ailoot combines established operating experience, a large client base, stable service and ongoing KPI control.

The company and its project portfolio: millions of customers; monthly sales in the billions.

Ailoot brings that operating experience to API access for AI features in customer products, agents, applications and automations.
