HTTP API

Skills over HTTP

List a project's skills, save a skill bundle as a draft, publish it as the next version, download it, and archive it with /v1/skills.

A skill is a folder with a SKILL.md and the scripts and references it needs, kept in a project so every agent on it can install the same version (see Skills). /v1/skills stores a skill as a bundle: you save a draft, then publish it as the next version. Most people use the CLI, which builds the bundle for you; see Skills from the CLI.

Every request names the project by its slug: project in the JSON body of a POST, or ?project= on a GET or DELETE.

List the skills

curl -H "Authorization: Bearer $KEY" "$SITE/v1/skills?project=web"
# → { "skills": [ { "_id": "…", "name": "release-notes", "version": 3, "draftRevision": 7, "fileCount": 4, "totalSize": 18342, "updatedAt": 1756… } ] }

version is the latest published version (0 if none yet). draftRevision goes up by one with every save and every publish. You need both to write. Archived skills are left out.

Read one skill

curl -H "Authorization: Bearer $KEY" "$SITE/v1/skills?project=web&name=release-notes"
curl -H "Authorization: Bearer $KEY" "$SITE/v1/skills?project=web&name=release-notes&version=2"
curl -H "Authorization: Bearer $KEY" "$SITE/v1/skills?project=web&name=release-notes&download=1" -o release-notes.json

The first answers with the skill, every published version with its downloadUrl, the current draft's draftUrl, and the latest version's downloadUrl. version picks an older version. download=1 answers with the bundle itself, as JSON.

The bundle

{
  "schemaVersion": 1,
  "name": "release-notes",
  "hash": "<sha-256 of the manifest>",
  "files": [
    { "path": "SKILL.md", "contentBase64": "…", "sha256": "…", "size": 1204, "executable": false },
    { "path": "scripts/collect.sh", "contentBase64": "…", "sha256": "…", "size": 812, "executable": true }
  ]
}
  • name is lowercase letters, digits and hyphens, up to 64 characters.
  • files holds 1 to 1,000 files, and one of them is SKILL.md at the root. Each file is at most 5 MB, the whole bundle at most 20 MB.
  • sha256 is the hex SHA-256 of the file's bytes, and size their length.
  • path is relative, with / between folders: no leading /, no . or .., no backslashes. Two paths can't differ only by case.
  • hash is the hex SHA-256 of a JSON array with one [path, sha256, size, executable] entry per file, sorted by path.

sfora checks every hash and size, stores the bytes exactly, keeps the executable flags, and never runs a skill's files.

Save a draft

curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" --data-binary @draft.json $SITE/v1/skills
draft.json
{ "project": "web", "bundle": { "schemaVersion": 1, "name": "release-notes", … }, "expectedVersion": 3, "expectedRevision": 7 }

expectedVersion is the version you read (0 for a new skill). For a skill that already exists, expectedRevision is the draftRevision you read. If the skill changed since then, nothing is saved and you get 409. The answer is the skill, with its new draftRevision.

Publish a skill

curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{"project":"web","name":"release-notes","expectedVersion":3,"expectedRevision":8}' $SITE/v1/skills/publish

Publishes the current draft as the next version (here, 4). Send the version and draftRevision the save answered with. If someone saved or published in between, it answers 409; with no draft to publish, 400.

Archive a skill

curl -X DELETE -H "Authorization: Bearer $KEY" "$SITE/v1/skills?project=web&name=release-notes"
# → { "ok": true }

An archived skill leaves the list.

Read published skills as files

The published skills are also on the /v1/fs tree, read-only:

curl -H "Authorization: Bearer $KEY" $SITE/v1/fs/projects/web/skills
curl -H "Authorization: Bearer $KEY" $SITE/v1/fs/projects/web/skills/release-notes
curl -H "Authorization: Bearer $KEY" $SITE/v1/fs/projects/web/skills/release-notes/SKILL.md

The first lists the skills with a published version, the second a skill's files with their sha256, size and executable flag, and the third one file's bytes, with its SHA-256 as the ETag. These show the latest published version; a draft appears only after you publish it.

Errors

StatusWhen
400project or name is missing, the bundle is invalid (the message says what), or there is no draft to publish.
403You aren't a member of the project.
404No such project or skill.
409The skill changed since you read it. Read it again and retry.

Troubleshooting

A save answers 409 for a new skill

A skill with that name already exists in the project. List the skills, then save with its version and draftRevision.

"Bundle hash mismatch"

hash must be computed over the entries sorted by path, as JSON with no spaces. Recompute it after any change to the files.

Last updated on