# Skills API

> Skills your Bash may use: upload one, give it new versions, list them.

Send `Authorization: Bearer vk_test_...` on every call.

## `GET /v1/skills`

Its own page: [GET /v1/skills](https://vestra.ai/developers/reference/api/skills/get-v1-skills.md)

Your company skills, plus the skills Bash already has. A company skill carries sha256, the hash of its files as uploaded, computed the way the SDKs compute a folder's, so an unchanged folder is never uploaded again.

Your API key, scope `setup` or `all`. On the card: [`deploy`](https://vestra.ai/developers/deploy.md). Over HTTP it is made from the contract: fill in every value.

### Request

Query:

- `limit` (`integer`): How many items per page, 1 to 100. Default 20.
- `cursor` (`string`): Where to go on from: the `next_cursor` of the page before, unchanged. Absent: the first page.
- `source` (`"company" | "built_in"`): `company` (the default): the skills you uploaded. `built_in`: the skills Bash already has.

### Answers 200

- `data` (`object[]`): The items on this page.
  - `object` (`"skill"`): Always `skill`.
  - `id` (`string | null`): The skill's id (`skl_`); null for a skill Bash already has.
  - `source` (`"company" | "built_in"`): `company`: one you uploaded. `built_in`: one Bash already has.
  - `slug` (`string`): Its name in a deployment: what `skills` takes for one Bash already has.
  - `name` (`string`): Its name, from its SKILL.md.
  - `description` (`string`): What it does, from its SKILL.md: Bash reads this to know when to use it.
  - `version` (`string | null`): The current version's content hash; null for a skill Bash already has.
  - `sha256` (`string | null`): A hash of the files as uploaded, which the SDKs and the CLI compute over a folder the same way, to upload only a folder that changed. Null for a skill Bash already has, and for one uploaded before this field.
  - `files` (`object[]`, may be absent): Its files, on GET /v1/skills/{skill_id}; absent in lists.
    - `path` (`string`): The file's path inside the skill.
    - `bytes` (`integer`): Its size, in bytes.
    - `content` (`string`): Its text.
  - `created_at` (`string | null`): When it was uploaded; null for a skill Bash already has.
  - `updated_at` (`string | null`): When its newest version was uploaded; null for a skill Bash already has.
- `next_cursor` (`string | null`): Send it as `cursor` for the next page; null on the last page.

### Code

**HTTP**

```bash
curl "$VESTRA_BASE_URL/v1/skills" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**TypeScript, Python, CLI, MCP**: No sample in your language for this route, so here it is over HTTP.

## `POST /v1/skills`

Its own page: [POST /v1/skills](https://vestra.ai/developers/reference/api/skills/post-v1-skills.md)

Upload a skill folder as a zip, or as JSON files.

Your API key, scope `setup` or `all`. On the card: [`deploy`](https://vestra.ai/developers/deploy.md). Over HTTP it is made from the contract: fill in every value.

### Request

Headers:

- `Idempotency-Key` (`string`): Send the same key to retry this call safely for 24 hours.

Body:

- `files` (`object[]`, required): The skill's files, 1 to 200, with a `SKILL.md` at the top whose frontmatter names it. Limits: 1 to 200 items.
  - `path` (`string`, required): The file's path inside the skill, such as `SKILL.md` or `scripts/fill.py`, up to 200 characters. Limits: 1 to 200 characters.
  - `content` (`string`, required): The file's text, up to 1 MB. Limits: at most 1048576 characters.

### Answers 201

- `object` (`"skill"`): Always `skill`.
- `id` (`string | null`): The skill's id (`skl_`); null for a skill Bash already has.
- `source` (`"company" | "built_in"`): `company`: one you uploaded. `built_in`: one Bash already has.
- `slug` (`string`): Its name in a deployment: what `skills` takes for one Bash already has.
- `name` (`string`): Its name, from its SKILL.md.
- `description` (`string`): What it does, from its SKILL.md: Bash reads this to know when to use it.
- `version` (`string | null`): The current version's content hash; null for a skill Bash already has.
- `sha256` (`string | null`): A hash of the files as uploaded, which the SDKs and the CLI compute over a folder the same way, to upload only a folder that changed. Null for a skill Bash already has, and for one uploaded before this field.
- `files` (`object[]`, may be absent): Its files, on GET /v1/skills/{skill_id}; absent in lists.
  - `path` (`string`): The file's path inside the skill.
  - `bytes` (`integer`): Its size, in bytes.
  - `content` (`string`): Its text.
- `created_at` (`string | null`): When it was uploaded; null for a skill Bash already has.
- `updated_at` (`string | null`): When its newest version was uploaded; null for a skill Bash already has.

### Code

**HTTP**

```bash
curl -X POST "$VESTRA_BASE_URL/v1/skills" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "files": []
  }'
```

**TypeScript, Python, CLI, MCP**: No sample in your language for this route, so here it is over HTTP.

## `GET /v1/skills/{skill_id}`

Its own page: [GET /v1/skills/{skill_id}](https://vestra.ai/developers/reference/api/skills/get-v1-skills-skill-id.md)

One skill and its files.

Your API key, scope `setup` or `all`. On the card: [`deploy`](https://vestra.ai/developers/deploy.md). Over HTTP it is made from the contract: fill in each {…} and every value.

### Request

Path:

- `skill_id` (`string`, required): The skill's id (`skl_...`).

### Answers 200

- `object` (`"skill"`): Always `skill`.
- `id` (`string | null`): The skill's id (`skl_`); null for a skill Bash already has.
- `source` (`"company" | "built_in"`): `company`: one you uploaded. `built_in`: one Bash already has.
- `slug` (`string`): Its name in a deployment: what `skills` takes for one Bash already has.
- `name` (`string`): Its name, from its SKILL.md.
- `description` (`string`): What it does, from its SKILL.md: Bash reads this to know when to use it.
- `version` (`string | null`): The current version's content hash; null for a skill Bash already has.
- `sha256` (`string | null`): A hash of the files as uploaded, which the SDKs and the CLI compute over a folder the same way, to upload only a folder that changed. Null for a skill Bash already has, and for one uploaded before this field.
- `files` (`object[]`, may be absent): Its files, on GET /v1/skills/{skill_id}; absent in lists.
  - `path` (`string`): The file's path inside the skill.
  - `bytes` (`integer`): Its size, in bytes.
  - `content` (`string`): Its text.
- `created_at` (`string | null`): When it was uploaded; null for a skill Bash already has.
- `updated_at` (`string | null`): When its newest version was uploaded; null for a skill Bash already has.

### Code

**HTTP**

```bash
curl "$VESTRA_BASE_URL/v1/skills/{skill_id}" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**TypeScript, Python, CLI, MCP**: No sample in your language for this route, so here it is over HTTP.

## `PUT /v1/skills/{skill_id}`

Its own page: [PUT /v1/skills/{skill_id}](https://vestra.ai/developers/reference/api/skills/put-v1-skills-skill-id.md)

Upload a new version.

Your API key, scope `setup` or `all`. On the card: [`deploy`](https://vestra.ai/developers/deploy.md). Over HTTP it is made from the contract: fill in each {…} and every value.

### Request

Path:

- `skill_id` (`string`, required): The skill's id (`skl_...`).

Body:

- `files` (`object[]`, required): The skill's files, 1 to 200, with a `SKILL.md` at the top whose frontmatter names it. Limits: 1 to 200 items.
  - `path` (`string`, required): The file's path inside the skill, such as `SKILL.md` or `scripts/fill.py`, up to 200 characters. Limits: 1 to 200 characters.
  - `content` (`string`, required): The file's text, up to 1 MB. Limits: at most 1048576 characters.

### Answers 200

- `object` (`"skill"`): Always `skill`.
- `id` (`string | null`): The skill's id (`skl_`); null for a skill Bash already has.
- `source` (`"company" | "built_in"`): `company`: one you uploaded. `built_in`: one Bash already has.
- `slug` (`string`): Its name in a deployment: what `skills` takes for one Bash already has.
- `name` (`string`): Its name, from its SKILL.md.
- `description` (`string`): What it does, from its SKILL.md: Bash reads this to know when to use it.
- `version` (`string | null`): The current version's content hash; null for a skill Bash already has.
- `sha256` (`string | null`): A hash of the files as uploaded, which the SDKs and the CLI compute over a folder the same way, to upload only a folder that changed. Null for a skill Bash already has, and for one uploaded before this field.
- `files` (`object[]`, may be absent): Its files, on GET /v1/skills/{skill_id}; absent in lists.
  - `path` (`string`): The file's path inside the skill.
  - `bytes` (`integer`): Its size, in bytes.
  - `content` (`string`): Its text.
- `created_at` (`string | null`): When it was uploaded; null for a skill Bash already has.
- `updated_at` (`string | null`): When its newest version was uploaded; null for a skill Bash already has.

### Code

**HTTP**

```bash
curl -X PUT "$VESTRA_BASE_URL/v1/skills/{skill_id}" \
  -H "Authorization: Bearer $VESTRA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "files": []
  }'
```

**TypeScript, Python, CLI, MCP**: No sample in your language for this route, so here it is over HTTP.

## `DELETE /v1/skills/{skill_id}`

Its own page: [DELETE /v1/skills/{skill_id}](https://vestra.ai/developers/reference/api/skills/delete-v1-skills-skill-id.md)

Remove it from every user.

Your API key, scope `setup` or `all`. On the card: [`deploy`](https://vestra.ai/developers/deploy.md). Over HTTP it is made from the contract: fill in each {…} and every value.

### Request

Path:

- `skill_id` (`string`, required): The skill's id (`skl_...`).

### Answers 200

- `object` (`"skill"`): Always `skill`.
- `id` (`string`): The deleted skill's id.
- `deleted` (`true`): Always true: it is gone.

### Code

**HTTP**

```bash
curl -X DELETE "$VESTRA_BASE_URL/v1/skills/{skill_id}" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**TypeScript, Python, CLI, MCP**: No sample in your language for this route, so here it is over HTTP.

## `GET /v1/skills/{skill_id}/versions`

Its own page: [GET /v1/skills/{skill_id}/versions](https://vestra.ai/developers/reference/api/skills/get-v1-skills-skill-id-versions.md)

Every version, each with a content hash.

Your API key, scope `setup` or `all`. On the card: [`deploy`](https://vestra.ai/developers/deploy.md). Over HTTP it is made from the contract: fill in each {…} and every value.

### Request

Path:

- `skill_id` (`string`, required): The skill's id (`skl_...`).

Query:

- `limit` (`integer`): How many items per page, 1 to 100. Default 20.
- `cursor` (`string`): Where to go on from: the `next_cursor` of the page before, unchanged. Absent: the first page.

### Answers 200

- `data` (`object[]`): The items on this page.
  - `object` (`"skill_version"`): Always `skill_version`.
  - `hash` (`string`): The version's content hash.
  - `parent_hash` (`string | null`): The version it replaced; null for the first.
  - `created_at` (`string`): When it was uploaded, as an ISO 8601 date and time in UTC.
- `next_cursor` (`string | null`): Send it as `cursor` for the next page; null on the last page.

### Code

**HTTP**

```bash
curl "$VESTRA_BASE_URL/v1/skills/{skill_id}/versions" \
  -H "Authorization: Bearer $VESTRA_API_KEY"
```

**TypeScript, Python, CLI, MCP**: No sample in your language for this route, so here it is over HTTP.
