> ## 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

<ResponseField name="id" type="string">Unique id (e.g. `skl_01H...`).</ResponseField>
<ResponseField name="name" type="string">Unique, human-readable name within the org (e.g. `refund-policy`).</ResponseField>
<ResponseField name="description" type="string | null">Short description of what the skill does.</ResponseField>
<ResponseField name="latest_version" type="integer">The highest version number.</ResponseField>
<ResponseField name="created_at" type="string">Creation timestamp.</ResponseField>

## The skill version object

<ResponseField name="id" type="string">The skill this version belongs to (`skl_…`).</ResponseField>
<ResponseField name="slug" type="string">The skill's slug.</ResponseField>
<ResponseField name="version" type="integer">Monotonic version number.</ResponseField>
<ResponseField name="sha256" type="string">SHA-256 of the `SKILL.md` content. Verifies integrity.</ResponseField>
<ResponseField name="size_bytes" type="integer">Content size.</ResponseField>
<ResponseField name="created_at" type="string">When this version was pushed.</ResponseField>

## 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).

<ParamField body="content" type="string" required>The `SKILL.md` text. Version 1 of a new slug.</ParamField>
<ParamField body="slug" type="string">Unique slug within the org — lowercase letters, digits and dashes. Required unless you send `name`.</ParamField>
<ParamField body="name" type="string">The same thing spelled `name`. Send one of `slug` or `name`.</ParamField>
<ParamField body="description" type="string">Short description. Defaults to `null`.</ParamField>
<ParamField body="sha256" type="string">SHA-256 hex digest of `content`. Optional: the server hashes the bytes itself and refuses a digest that does not match.</ParamField>

<CodeGroup>
  ```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..." }'
  ```
</CodeGroup>

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

## Retrieve & list

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

See [Pagination](/docs/api/pagination).

## 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`.

<ParamField body="content" type="string" required>The full `SKILL.md` text.</ParamField>
<ParamField body="sha256" type="string">SHA-256 hex digest of `content`. Optional: the server hashes the bytes itself and refuses a digest that does not match.</ParamField>
<ParamField body="description" type="string">Replace the skill's description as part of this push.</ParamField>

```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.

<Warning>
  Agents referencing the slug — pinned (`refund-policy@3`) or floating (`refund-policy`) — fail to resolve it at session start once it is gone. Check what depends on a skill before deleting it.
</Warning>

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

## Pin a version

An [agent](/docs/api/agents)'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. |

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.
</Note>
