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.
What name and description may contain¶
The metadata block takes one key: value pair per line and nothing else: no indented lines, no
lists, no line without a colon. Core reads name and description, accepts license,
compatibility and allowed-tools and ignores them, and refuses any other key with a 422. When a
key appears twice, the last one counts.
The value is everything after the first colon on the line, without the spaces around it. It must fit on
one line: YAML's multi-line forms (> and | followed by indented lines) are refused. Colons
inside the value are fine for Core.
nameuses lowercase letters, digits and single hyphens (release-notes), at most 64 characters.descriptionis required, at most 1024 characters, and not empty.
Core removes one pair of matching outer quotes ("..." or '...') and changes nothing inside. It
does not interpret escapes, so \", \\ and \n stay as you wrote them, and '' stays two
apostrophes. It does not strip comments either: in an unquoted value, # text is part of the
description. A value that opens with ", ', [ or { must end the line with its matching closer
(" for ", ' for ', ] for [, } for {), or the upload fails with a 422 saying the front matter is not valid YAML.
Other tools that read the file as YAML refuse a plain value that contains : (a colon followed by
a space) or starts with a special character. Write the description as one plain line with no :
and no #. When it needs either, or has to start with a special character, quote it, and put no
quote or backslash inside.
description: Write release notes for one version from a list of merged changes.
description: "[Internal] Write release notes for one version from merged changes."
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 |
GET /skills/{skill_id}/versions/{version}/resources/{path} |
one resource's content as that version held it; 404 for a version or path the skill does not have |
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 reads the X-Core-Subject header like every write, so a rule of the person it names can refuse it with 403. It 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_skilltool 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 intools[]answers400. - 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 mostretrieval_top_kof 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. Nobuiltin__load_skilltool 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.