> ## Documentation Index
> Fetch the complete documentation index at: https://vetta.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Skills

> Versioned, org-scoped skills attached to agents.

A **skill** is a reusable, versioned instruction module — a `SKILL.md` document — that agents load on demand. Skills are org-scoped and immutable per version: each push creates a new version identified by its content hash.

## The skill object

**id** `string`

Unique id (e.g. `skl_01H...`).

**name** `string`

Unique, human-readable name within the org (e.g. `refund-policy`).

**description** `string | null`

Short description of what the skill does.

**latest\_version** `integer`

The highest version number.

**created\_at** `string`

Creation timestamp.

## The skill version object

**id** `string`

The skill this version belongs to (`skl_…`).

**slug** `string`

The skill's slug.

**version** `integer`

Monotonic version number.

**sha256** `string`

SHA-256 of the `SKILL.md` content. Verifies integrity.

**size\_bytes** `integer`

Content size.

**created\_at** `string`

When this version was pushed.

## Create a skill

`POST /v1/skills` → `201 Created` for a new slug, `200 OK` when the slug already exists (the push is applied as a new version of it). A skill is never created empty: the create *is* the first push. Both responses are the [skill version object](#the-skill-version-object).

**content** `string` *required*

The `SKILL.md` text. Version 1 of a new slug.

**slug** `string`

Unique slug within the org — lowercase letters, digits and dashes. Required unless you send `name`.

**name** `string`

The same thing spelled `name`. Send one of `slug` or `name`.

**description** `string`

Short description. Defaults to `null`.

**sha256** `string`

SHA-256 hex digest of `content`. Optional: the server hashes the bytes itself and refuses a digest that does not match.

```bash cURL theme={"system"}
curl -fsSL https://api.vetta.sh/v1/skills \
  -H "authorization: Bearer sk_live_..." \
  -H "content-type: application/json" \
  -H "idempotency-key: $(uuidgen)" \
  -d '{ "slug": "refund-policy", "description": "How to process refunds.", "content": "# Refund policy\n...", "sha256": "9f86d0..." }'
```

```json Response theme={"system"}
{
  "id": "skl_01H8XZ...",
  "slug": "refund-policy",
  "version": 1,
  "sha256": "9f86d0...",
  "size_bytes": 1024,
  "created_at": "2026-08-20T17:00:00Z"
}
```

## Retrieve & list

```bash theme={"system"}
GET /v1/skills/{id}    # retrieve one
GET /v1/skills         # list (cursor-paginated)
```

See [Pagination](https://vetta.sh/docs/api/pagination.md).

## Push a new version

`POST /v1/skills/{id}/versions` uploads a new `SKILL.md`. The body is the document text, optionally with its `sha256`; the server rejects the request with `validation_failed` if the hash does not match the content. Pushing bytes identical to the current version mints nothing and answers `200`.

**content** `string` *required*

The full `SKILL.md` text.

**sha256** `string`

SHA-256 hex digest of `content`. Optional: the server hashes the bytes itself and refuses a digest that does not match.

**description** `string`

Replace the skill's description as part of this push.

```bash theme={"system"}
curl -fsSL https://api.vetta.sh/v1/skills/skl_01H8XZ.../versions \
  -H "authorization: Bearer sk_live_..." \
  -H "content-type: application/json" \
  -d '{ "content": "# Refund policy\nConfirm the order first.\n...", "sha256": "b1946ac9..." }'
```

```json Response theme={"system"}
{
  "id": "skl_01H8XZ...",
  "slug": "refund-policy",
  "version": 2,
  "sha256": "b1946ac9...",
  "size_bytes": 2048,
  "created_at": "2026-08-20T18:00:00Z"
}
```

## List versions

`GET /v1/skills/{id}/versions` returns version history, newest first. Cursor-paginated.

```json Response theme={"system"}
{
  "data": [
    { "version": 2, "sha256": "b1946ac9...", "size_bytes": 2048, "created_at": "2026-08-20T18:00:00Z" },
    { "version": 1, "sha256": "9f86d0...", "size_bytes": 1024, "created_at": "2026-08-20T17:00:00Z" }
  ],
  "has_more": false,
  "next_cursor": null
}
```

## Retrieve one version

`GET /v1/skills/{id}/versions/{n}` — scope `agents:read`. Returns that immutable version's metadata **with its body**: the [skill version object](#the-skill-version-object) plus a `content` string carrying the raw `SKILL.md`.

This is the second half of progressive disclosure. The list above carries only `version`, `sha256`, `size_bytes`, and `created_at`; the text itself costs this extra call, so a runtime pays for it once when the skill is actually used rather than on every turn.

```bash theme={"system"}
curl -fsSL https://api.vetta.sh/v1/skills/refund-policy/versions/2 \
  -H "authorization: Bearer sk_live_..."
```

`{id}` accepts a slug or an id, and `{n}` is the integer version. `not_found` (404) if either does not resolve.

## Delete a skill

`DELETE /v1/skills/{id}` — scope `agents:write`. Removes the skill and its whole version history.

A skill an agent lists — pinned (`refund-policy@3`) or floating (`refund-policy`) — is not deleted. The call answers `409` `state_conflict` and names the agents: `Used by Editor and Writer. Remove it from them first.` So is a skill that a [deployment](https://vetta.sh/docs/api/deployments.md) pinned to an older agent version still lists: `A routine of Editor runs an older version that uses it. Delete or update that routine first.`

```json Response theme={"system"}
{ "id": "skl_01H9BC...", "object": "skill", "deleted": true }
```

## Pin a version

An [agent](https://vetta.sh/docs/api/agents.md)'s `skills[]` accepts two reference forms:

| Reference | Resolution |
| - | - |
| `refund-policy` | Floats to the skill's `latest_version`, resolved **at session start**. |
| `refund-policy@3` | Pinned to the immutable version `3`. Never moves, even after new pushes. |
| `naive/landing-page-copy` | The [platform catalogue](#the-platform-catalogue), floating to its newest published version at session start. |
| `naive/landing-page-copy@1` | The platform catalogue, pinned. |

The `naive/` prefix is the whole difference: a bare slug is always one of **your** skills, a prefixed one is always the platform's, and your `landing-page-copy` and the platform's never collide. Either kind that cannot be resolved fails the agent write with `404 not_found`, `param: "skills"`.

Skill versions are content-addressed and immutable, so pushing a new `SKILL.md` never silently changes an already-pinned agent. Use `GET /v1/skills/{id}/versions` to find the numbers you can pin.

> [!NOTE]
>
> Attach a skill to an agent by slug or id. An unpinned reference (`refund-policy`) resolves to the latest version at session start; a pinned one (`refund-policy@3`) is frozen.

## The platform catalogue

The platform publishes a small catalogue of skills — landing-page copy, client onboarding, SEO briefs, clip selection and the like — that any agent can reference as `naive/<slug>`. It is read-only from the API: versions are published with a platform release, and like your own skills they are content-addressed, so a published version never changes.

```http theme={"system"}
GET /v1/skills/catalog                          # list, newest version per slug
GET /v1/skills/catalog/{slug}                   # one slug, its newest version
GET /v1/skills/catalog/{slug}/versions/{n}      # one version, with its content
```

All three accept any authenticated caller — an API key with any scope, or a signed-in session that does not yet belong to an organization — because a catalogue is something to browse before there is anything to attach it to.

### The skill catalogue entry object

**object** `string` *required*

`skill_catalog_entry`

**slug** `string` *required*

The handle you reference as `naive/<slug>`.

**version** `integer` *required*

The version described; the number to pin.

**name** `string` *required*

Display name.

**description** `string` *required*

One line on what the skill is for.

**tags** `string[]` *required*

Families the skill belongs to, e.g. `agency`, `seo`, `media`.

**content\_hash** `string` *required*

SHA-256 of `content`, lowercase hex.

**published\_at** `string` *required*

When this version was published.

**content** `string`

The raw `SKILL.md`. **Only** on the version read; the list never carries it.

### List the catalogue

`GET /v1/skills/catalog` — the newest version of every slug, alphabetical, cursor-paginated (the cursor is a slug). `?tag=` narrows to one family and `?search=` is free text over `slug`, `name` and `description`; the two compose.

```bash theme={"system"}
curl -fsSL "https://api.vetta.sh/v1/skills/catalog?tag=seo" \
  -H "Authorization: Bearer $VETTA_API_KEY"
```

```json Response theme={"system"}
{
  "data": [
    {
      "object": "skill_catalog_entry",
      "slug": "seo-content-brief",
      "version": 1,
      "name": "SEO content brief",
      "description": "Intent, target query, outline, entities to cover, internal links — before a word of the draft.",
      "tags": ["media", "seo", "content"],
      "content_hash": "3f1a…",
      "published_at": "2026-09-08T00:00:00.000Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

### Resolve one `naive/<slug>`

`GET /v1/skills/catalog/{slug}` — the **newest** version of that slug, without `content`. This is the route that answers "does `naive/short-video-hooks` exist, and at what version?" — `GET /v1/skills/{ref}` is *your* skills and answers `404` for a `naive/` name, which is not the same claim. A slug nothing published answers `404 not_found`, `param: "slug"`.

```bash theme={"system"}
curl -fsSL https://api.vetta.sh/v1/skills/catalog/seo-content-brief \
  -H "Authorization: Bearer $VETTA_API_KEY"
```

### Read a catalogue version

`GET /v1/skills/catalog/{slug}/versions/{n}` — the entry **with** `content`. An unknown slug or version answers `404 not_found`, `param: "slug"`.

```bash theme={"system"}
curl -fsSL https://api.vetta.sh/v1/skills/catalog/seo-content-brief/versions/1 \
  -H "Authorization: Bearer $VETTA_API_KEY"
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.