Skip to content

Skills through the API

The concept behind agents is explained in Agents.

On this page you upload a skill through the API and offer it on a turn. A skill packages reusable instructions for a task, such as writing release notes or triaging tickets. You store the package once and offer it on the turns that need it. When the model has to execute an action, use tools.

Your key needs the skills capability. You do the same with a mouse in Skills in the console.

Package format

Core supports the Agent Skills format. A package contains SKILL.md and optional supporting text files. In SKILL.md, place name and description in a metadata block between --- lines, followed by Markdown instructions.

---
name: release-notes
description: Write release notes for one version from a list of merged changes.
---

Group the changes under Added, Changed, Fixed. One line each, in the past tense, naming
what a reader can now do. Never mention an internal ticket id.

See `examples.md` for two notes written this way.

Upload a skill

curl -s -X POST "$CORE_URL/skills" \
    -H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
    -d @- <<'JSON'
{
  "skill_md": "---\nname: release-notes\ndescription: Write release notes...\n---\n\nGroup the changes...",
  "resources": [{ "path": "examples.md", "content": "## 2.4.0\n\n### Added\n..." }]
}
JSON
Call Answers
POST /skills 201 + Location; 409 if the name is taken
GET /skills your skills, cursor-paged
GET /skills/{skill_id} one skill: name, description, body, and each resource's path and size
GET /skills/{skill_id}/resources/{path} one resource's content
PUT /skills/{skill_id} replace body, description and resources
DELETE /skills/{skill_id} 204

Listings include resource paths and sizes. Fetch a resource separately when you need its content.

A resource path is relative and may contain / to place files in folders, such as templates/summary.md; fetch it with the path as is: GET /skills/{skill_id}/resources/templates/summary.md. A path uses letters, digits, ., _, - and /, up to 200 characters; it must not start with / or contain a .. segment, and each path in a skill is unique. Write each path in its plain form, without //, ./ or a trailing /: the API normalises the URL it is fetched by, so a resource stored under such a path cannot be fetched by it.

DELETE answers 409 while the latest version of an agent links the skill by its id or its name; the error lists the agents in conflicts, at most the first twenty, and counts the ones your key may not read in withheld (the fields). An agent is also refused, with a 422 on the entry, when it names a skill your organisation does not have.

PUT requires the existing package name; changing it returns 409. To rename a skill, create a new one, update references and delete the old one when it is no longer used.

Every PUT writes the skill's next version. An administrator can also save one as a draft, in the console or its API: your key does not see a draft. GET answers the published version, a skill that has only a draft does not exist for your key, and a reference release-notes@3 to a draft number answers 404 as for a version never written. The draft takes effect when it is published.

Unknown metadata fields, missing name or description, absolute resource paths and paths containing .. return 422 with the affected field.

Offer a skill

{
  "model": "<a model id>",
  "skills": ["release-notes"],
  "input": "Write the notes for 2.4.0 from these merged changes: ..."
}

Pass up to 32 skill names or skill_ ids in skills. An unavailable reference or disabled capability returns 404. Check GET /key and the skill identifier when troubleshooting.

Your key's policy decides which skills it reaches. A skill the policy does not name is absent from GET /skills, answers 404 by id, and is never offered or injected on a turn, retrieval-driven loading included. Storing a package under a name the policy does not name answers 403.

Skill loading

The key configuration selects one of two loading methods:

  • Model-driven (the default) — Core offers a builtin__load_skill tool listing only the skills this request named, with each one's description. The model reads a skill when it decides it needs one, and can ask for a packaged resource by path. Linking skills is what offers it: a request that names it in tools[] answers 400.
  • Retrieval-driven — Core compares your turn's own last user text against each named skill's description and injects the best matches directly, before the model runs: those scoring at least min_score, at most retrieval_top_k of them. A matching skill is never left out for the length of its body; the turn's trace shows how many were left out by the count and what the injected bodies cost. No builtin__load_skill tool is offered; the choice was already made.

In both modes, the skill body is a trusted instruction at the same authority level as instructions. Only store instructions you intend to grant that authority.

Audit records

The audit history records writes (skill.created, skill.replaced, skill.deleted) and use (skill.loaded, skill.injected) with the originating request id. Use these events to check which instructions a turn loaded.