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

# Skills

> Write reusable procedures as SKILL.md, version them, group them, and attach them to agents.

A skill is a reusable procedure stored as `SKILL.md`, optionally with scripts and reference files. Attach skills to an agent instead of putting every procedure in its system prompt: the agent sees each skill's short description on every turn and loads the full instructions only when a skill fits the task.

<Note>
  Creating, changing, or deleting skills and skill groups needs the organization developer or admin role. The organization user role can view them. See [Organizations and roles](/recursion/organizations-and-roles). For the API, create a key on [API keys](/recursion/api-keys) and export it as `RECURSION_API_KEY`.
</Note>

## Write SKILL.md

`SKILL.md` starts with YAML frontmatter on the first line, followed by Markdown instructions. `name` and `description` are required. Write the description as a condition for using the skill, because it is what the model routes on.

```markdown theme={"theme":"css-variables"}
---
name: release-notes
description: Use when drafting customer-facing notes from merged pull requests.
---

# Draft release notes

1. Read every merged pull request in the requested range.
2. Group the changes by product area.
3. Write one plain sentence for each change.
4. Save the result with write_artifact as release-notes.md.
```

<Accordion title="Frontmatter keys and document rules">
  | Frontmatter key | Rule |
  | - | - |
  | `name` | Required. Lowercase letters, digits, and single hyphens, at most 64 characters. The agent activates the skill by this name. |
  | `description` | Required. At most 1,024 characters. Write it as a condition for using the skill, because it is what the model routes on. |
  | `license`, `compatibility`, `metadata`, `allowed-tools` | Optional. Stored with the version. `compatibility` is at most 500 characters, and the agent sees it when it activates the skill. `allowed-tools` isn't enforced. |

  Any other key has no effect. The whole `SKILL.md` can be at most 256 KiB, and it must have instructions below the frontmatter.

  When a session loads a skill, it removes HTML comments and escapes `<`, `>`, and `&` in the name, description, and instructions. The agent reads `&lt;`, `&gt;`, and `&amp;` in their place, so write procedures that don't depend on those characters or on hidden comments. Instructions longer than 64,000 bytes of UTF-8 after this escaping are cut off, and the agent is told they were cut off. The catalog keeps your text as you wrote it.
</Accordion>

## Create a skill

Creating a skill also creates version 1. Versions are immutable.

<Tabs>
  <Tab title="Console">
    1. In the sidebar, click **Skills**.
    2. Click **Create skill**.
    3. Enter the document in **SKILL.md**, and optionally a **Display title** and a **Group**.
    4. Click **Create skill**.
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/skills' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{
        "display_title": "Release notes",
        "document": "---\nname: release-notes\ndescription: Use when drafting customer-facing notes from merged pull requests.\n---\n\n# Draft release notes\n\nRead the merged pull requests, group them by product area, and write one sentence for each change."
      }'
    ```
  </Tab>
</Tabs>

A `200` response is the catalog entry. Some fields are left out here.

```json theme={"theme":"css-variables"}
{
  "skill_id": "4e7a2c91-6d35-4f80-b8a1-9c3e5d7f2a64",
  "latest_skill_version_id": "8b1f5d73-2c94-467a-a0e6-4d9c7b3f1e52",
  "latest_version_number": 1,
  "latest_instruction_chars": 104,
  "name": "release-notes",
  "display_title": "Release notes",
  "description": "Use when drafting customer-facing notes from merged pull requests.",
  "organization_id": "org_01a08a705220724f9a2fe1bcd8638c9d",
  "created_at": "2026-09-17T09:30:11Z",
  "updated_at": "2026-09-17T09:30:11Z"
}
```

Keep `skill_id` to attach the skill and `latest_skill_version_id` to publish the next version. Next, [attach it to an agent](#attach-skills-to-an-agent).

<Accordion title="Create fields">
  | Field | Description |
  | - | - |
  | `document` | Required. The complete `SKILL.md`, frontmatter included. |
  | `display_title` | Title shown in the catalog, at most 256 characters. Defaults to the frontmatter `name`. |
  | `skill_group_id` | The [group](#organize-skills-in-groups) to file the skill under. Omit it to leave the skill ungrouped. |
  | `metadata` | Your own JSON object. The API returns it unchanged. |

  Skill names are unique in your organization. A name that another live skill already uses returns `409 conflict` on `name`.
</Accordion>

### Upload a bundle

To include scripts and reference files, upload a zip archive of the skill's directory instead. Put `SKILL.md` at the root of the archive, or inside one wrapper directory. Put runnable helpers in `scripts/`, supporting documents in `references/`, and other files in `assets/`. Files under `scripts/` are made executable in the sandbox; all other files are not.

<Tabs>
  <Tab title="Console">
    1. In **Skills**, click **Create skill**.
    2. Drop the zip on **Upload a bundle**, or pick it.
    3. Click **Review and upload**, check the file list, then click **Create skill** in the **Upload bundle** dialog.
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/skills/upload' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -F 'bundle=@release-notes.zip' \
      -F 'display_title=Release notes'
    ```
  </Tab>
</Tabs>

The document, name, and description come from the `SKILL.md` inside the archive.

<Accordion title="Bundle rules and limits">
  If you use a wrapper directory, its name must match the skill's `name`, and every file must be inside it. Symbolic links and other non-regular files are refused.

  The multipart form also accepts `skill_group_id`, and `metadata` as a JSON string.

  | Bundle limit | Value |
  | - | - |
  | Compressed archive | 8 MiB |
  | Unpacked contents | 32 MiB |
  | One file | 4 MiB |
  | Files | 500 |
  | Directory depth below the skill root | 3 |
</Accordion>

## Attach skills to an agent

Skills are part of an agent version, in the `skills` field. Each entry is one of these:

| Entry | What the session gets |
| - | - |
| `{ "skill_id": "..." }` | The skill's latest version when the session starts. |
| `{ "skill_id": "...", "version": "<skill_version_id>" }` | That exact version, every time. `"latest"` also works and means the same as leaving `version` out. |
| `{ "type": "group", "skill_group_id": "..." }` | The group's current skills when the session starts. A skill added to the group later reaches the agent without an agent update. |

<Tabs>
  <Tab title="Console">
    1. Open the agent and stay on the **Configuration** tab.
    2. In **Skills**, pick a skill or group in **Attach a skill or group**. Reorder the cards so the most important skills come first.
    3. Click **Save new version**.

    The console attaches the latest version. To pin a version, use the API.
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/agents/5f0c2a1e-8b7d-4c3a-9e21-6d4f0b9a7c55/versions' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{
        "base_agent_version_id": "c1a94e07-2f6b-4d18-b3a5-0e7d8c6f4a21",
        "name": "Release notes writer",
        "model": "<model-id>",
        "system": "Follow the attached procedures.",
        "disabled_integration_mcp_providers": ["jira"],
        "skills": [
          {"skill_id": "4e7a2c91-6d35-4f80-b8a1-9c3e5d7f2a64"},
          {"type": "group", "skill_group_id": "6c2e9a47-1f83-45d0-b7a6-3e8c5f2d9a14"}
        ]
      }'
    ```
  </Tab>
</Tabs>

A `201` response is the agent at its new version. A version is a full replacement, so resend every other setting you want to keep. See [Update an agent](/recursion/agents#update-an-agent).

```json theme={"theme":"css-variables"}
{
  "agent_id": "5f0c2a1e-8b7d-4c3a-9e21-6d4f0b9a7c55",
  "latest_agent_version_id": "7a4e1c95-6d2f-4380-b915-3e7a0c6f2d84",
  "skills": [
    { "skill_id": "4e7a2c91-6d35-4f80-b8a1-9c3e5d7f2a64" },
    { "type": "group", "skill_group_id": "6c2e9a47-1f83-45d0-b7a6-3e8c5f2d9a14" }
  ]
}
```

Entry order matters, because descriptions past the disclosure budget are dropped from the end. Next, [preview prompt disclosure](#preview-prompt-disclosure) to check that the descriptions fit.

<Accordion title="Entry rules">
  An agent can have at most 100 entries. The API checks every id when you save the version; an unknown skill, version, or group returns `400 invalid_request` naming the entry, such as `skills[0].skill_id`.
</Accordion>

## Preview prompt disclosure

Every turn lists the session's skills within a character budget. When the descriptions don't fit, the ones at the end of the list are dropped and the model sees only those skills' names, which makes it less likely to pick them. The preview resolves groups exactly as a session start does and reports what fits.

<Tabs>
  <Tab title="Console">
    1. Open the agent and stay on the **Configuration** tab.
    2. In **Skills**, attach the skills or groups you plan to use.
    3. Read the budget meter. Cards past the line marked "Past this line the model sees only skill names." are flagged **Name only**.
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/skills/disclosure-preview' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{"skills":[{"skill_id":"4e7a2c91-6d35-4f80-b8a1-9c3e5d7f2a64"},{"type":"group","skill_group_id":"6c2e9a47-1f83-45d0-b7a6-3e8c5f2d9a14"}]}'
    ```
  </Tab>
</Tabs>

```json theme={"theme":"css-variables"}
{
  "budget_chars": 8000,
  "used_chars": 291,
  "entries": [
    { "name": "release-notes", "description_included": true },
    { "name": "release-checklist", "description_included": true }
  ],
  "warnings": []
}
```

An entry with `description_included: false` can still be activated, but the model sees only its name. Move important skills earlier, or shorten descriptions, until they fit.

<Accordion title="Preview details">
  Here the group holds one skill, `release-checklist`, so the response lists it after `release-notes`. `warnings` also names entries that could not be resolved, such as a deleted skill or group. `context_window_tokens` is optional; omit it to use a conservative default of 8,000 characters, which is also what the console's meter uses.
</Accordion>

## Publish a new version

Get the skill first, then send its current `latest_skill_version_id` as `base_skill_version_id`. The request is a full replacement: a `display_title` you leave out resets to the frontmatter `name`, and `metadata` you leave out is cleared. You cannot change the group here; use [Move a skill to a group](#move-a-skill-to-a-group).

<Tabs>
  <Tab title="Console">
    1. In **Skills**, open the skill.
    2. Edit **SKILL.md**, or drop a new zip on **Upload a bundle**.
    3. Click **Save new version**. For a bundle, confirm in the **Upload bundle** dialog.
  </Tab>

  <Tab title="cURL — document">
    ```bash theme={"theme":"css-variables"}
    curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/skills/4e7a2c91-6d35-4f80-b8a1-9c3e5d7f2a64/versions' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{
        "base_skill_version_id": "8b1f5d73-2c94-467a-a0e6-4d9c7b3f1e52",
        "document": "---\nname: release-notes\ndescription: Use when drafting customer-facing notes from merged pull requests.\n---\n\n# Draft release notes\n\nCheck every pull request before writing the notes."
      }'
    ```
  </Tab>

  <Tab title="cURL — bundle">
    ```bash theme={"theme":"css-variables"}
    curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/skills/4e7a2c91-6d35-4f80-b8a1-9c3e5d7f2a64/versions/upload' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -F 'base_skill_version_id=8b1f5d73-2c94-467a-a0e6-4d9c7b3f1e52' \
      -F 'bundle=@release-notes.zip' \
      -F 'display_title=Release notes'
    ```
  </Tab>
</Tabs>

The JSON `createSkillVersion` operation returns the new version with `201`; the multipart `publishSkillVersionBundle` action returns it with `200`.

```json theme={"theme":"css-variables"}
{
  "skill_id": "4e7a2c91-6d35-4f80-b8a1-9c3e5d7f2a64",
  "skill_version_id": "13d8a6f4-5b27-49e1-8c30-7a2f9d6b4e85",
  "version_number": 2,
  "name": "release-notes",
  "description": "Use when drafting customer-facing notes from merged pull requests.",
  "created_at": "2026-09-18T14:02:37Z"
}
```

Agents that follow the latest version pick up version 2 in their next session. Sessions already running keep the version they started with. If another writer published first, the API returns `409 revision_conflict` and saves nothing. Get the skill again, apply your change, and retry.

## Find a skill

List the catalog, or get one skill by id. Neither includes the instructions; [get a version](#list-and-get-versions) for those.

<Tabs>
  <Tab title="Console">
    1. In the sidebar, click **Skills**.
    2. Search by name, description, or id, or expand a group in the catalog.
    3. Click the skill to open its latest version.
  </Tab>

  <Tab title="cURL — list">
    ```bash theme={"theme":"css-variables"}
    curl 'https://api.recursion.labelbox.com/managed-agents/v1/skills?search=release&limit=100' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>

  <Tab title="cURL — get">
    ```bash theme={"theme":"css-variables"}
    curl 'https://api.recursion.labelbox.com/managed-agents/v1/skills/4e7a2c91-6d35-4f80-b8a1-9c3e5d7f2a64' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>
</Tabs>

```json theme={"theme":"css-variables"}
{
  "skills": [
    {
      "skill_id": "4e7a2c91-6d35-4f80-b8a1-9c3e5d7f2a64",
      "latest_skill_version_id": "8b1f5d73-2c94-467a-a0e6-4d9c7b3f1e52",
      "name": "release-notes",
      "description": "Use when drafting customer-facing notes from merged pull requests."
    }
  ]
}
```

The list is newest first and leaves out deleted skills. Follow `next_page_token` to read the whole catalog; it is absent on the last page. A get returns one skill in the same shape as the create response.

<Accordion title="List parameters">
  | Query parameter | Description |
  | - | - |
  | `search` | 2 to 256 characters. Matches part of the name, title, or description, ignoring case, or a complete skill id. |
  | `skill_group_id` | Only skills in this group. An unknown group returns `404 not_found`. |
  | `ungrouped` | `true` for only skills in no group. Cannot be combined with `skill_group_id`. |
  | `limit` | Page size. Defaults to 100, at most 500. |
  | `page_token` | The `next_page_token` from the previous page. Send the same filters and `limit` with it. |
</Accordion>

## List and get versions

`listSkillVersions` returns every version, newest first, without pages and without instructions. `getSkillVersion` returns one version with its `instructions`.

<Tabs>
  <Tab title="Console">
    1. In **Skills**, open the skill.
    2. Use **Compare with** under **Changes** to see what changed between versions.
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl 'https://api.recursion.labelbox.com/managed-agents/v1/skills/4e7a2c91-6d35-4f80-b8a1-9c3e5d7f2a64/versions/13d8a6f4-5b27-49e1-8c30-7a2f9d6b4e85' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>
</Tabs>

Every version has a bundle, and `bundle.entries` always includes `SKILL.md`. A skill created from a document alone has a bundle that holds only `SKILL.md`. Some fields are left out of this sample.

```json theme={"theme":"css-variables"}
{
  "skill_id": "4e7a2c91-6d35-4f80-b8a1-9c3e5d7f2a64",
  "skill_version_id": "13d8a6f4-5b27-49e1-8c30-7a2f9d6b4e85",
  "version_number": 2,
  "name": "release-notes",
  "description": "Use when drafting customer-facing notes from merged pull requests.",
  "instructions": "# Draft release notes\n\nCheck that every pull request is merged before writing the notes. Run scripts/collect.sh to list them.",
  "entrypoint_path": "SKILL.md",
  "file_manifest": ["scripts/collect.sh", "references/style.md"],
  "bundle": {
    "bytes": 1843,
    "entries": [
      { "path": "SKILL.md", "bytes": 214, "mode": 420 },
      { "path": "references/style.md", "bytes": 2210, "mode": 420 },
      { "path": "scripts/collect.sh", "bytes": 512, "mode": 493 }
    ]
  },
  "created_at": "2026-09-18T14:02:37Z"
}
```

A version with bundled files lists them in `bundle.entries`. `mode` is `493` (octal 0755) for files under `scripts/` and `420` (octal 0644) for everything else.

<Accordion title="Download a version's files">
  `getSkillVersionContent` returns the version's files as a gzipped tarball, exactly as a session receives them: `SKILL.md` plus any bundled files.

  ```bash theme={"theme":"css-variables"}
  curl 'https://api.recursion.labelbox.com/managed-agents/v1/skills/4e7a2c91-6d35-4f80-b8a1-9c3e5d7f2a64/versions/13d8a6f4-5b27-49e1-8c30-7a2f9d6b4e85/content' \
    -H "Authorization: Bearer $RECURSION_API_KEY" \
    -o release-notes-v2.tar.gz
  ```
</Accordion>

## Organize skills in groups

A skill group is a folder in the catalog and a way to attach a whole set of skills to an agent in one entry. The model never sees the group. Filing a skill in a group never creates a skill version.

### Create a group

<Tabs>
  <Tab title="Console">
    1. In the sidebar, click **Skills**.
    2. Click **New group**.
    3. Fill in **Name**, and optionally **Display title** and **Description**.
    4. Click **Save group**.
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/skill-groups' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{
        "name": "release-workflows",
        "display_title": "Release workflows",
        "description": "Procedures used while preparing a release."
      }'
    ```
  </Tab>
</Tabs>

A `201` response is the group. Keep `skill_group_id` to file skills under it and to attach it to an agent.

```json theme={"theme":"css-variables"}
{
  "skill_group_id": "6c2e9a47-1f83-45d0-b7a6-3e8c5f2d9a14",
  "organization_id": "org_01a08a705220724f9a2fe1bcd8638c9d",
  "name": "release-workflows",
  "display_title": "Release workflows",
  "description": "Procedures used while preparing a release.",
  "skill_count": 0,
  "created_at": "2026-09-17T09:41:52Z",
  "updated_at": "2026-09-17T09:41:52Z"
}
```

<Accordion title="Group fields">
  `name` is required, follows the same rule as a skill name, and must be unique among your organization's groups; a taken name returns `409 conflict` on `name`. `display_title` is at most 256 characters and defaults to `name`. `description` is for people browsing the catalog, at most 1,024 characters.
</Accordion>

### Move a skill to a group

`updateSkill` files a skill under a group. Send an empty `skill_group_id` to take the skill out of its group. The skill's versions don't change.

<Tabs>
  <Tab title="Console">
    1. In **Skills**, open the skill.
    2. In the header, pick a group in **Group**, or **Ungrouped**. The change saves at once.
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -X PATCH 'https://api.recursion.labelbox.com/managed-agents/v1/skills/4e7a2c91-6d35-4f80-b8a1-9c3e5d7f2a64' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{"skill_group_id":"6c2e9a47-1f83-45d0-b7a6-3e8c5f2d9a14"}'
    ```
  </Tab>
</Tabs>

<Accordion title="List, get, and update groups">
  `listSkillGroups` returns `skill_groups` newest first, with `limit` (default 100, at most 500) and `page_token`. `getSkillGroup` returns one group. `updateSkillGroup` changes only the fields you send, so renaming a group is safe: agents attach it by id.

  In the console, open the group's menu in the catalog in **Skills**, click **Edit group**, change the fields, and click **Save group**.

  ```bash theme={"theme":"css-variables"}
  curl -X PATCH 'https://api.recursion.labelbox.com/managed-agents/v1/skill-groups/6c2e9a47-1f83-45d0-b7a6-3e8c5f2d9a14' \
    -H "Authorization: Bearer $RECURSION_API_KEY" \
    -H 'Content-Type: application/json' \
    -d '{"display_title":"Release procedures"}'
  ```
</Accordion>

<Accordion title="List the skills in a group">
  `listSkillsInGroup` returns the group's skills in the order a group attachment expands them. That order decides whose description is dropped first when the disclosure budget runs out. It takes `limit` and `page_token` like `listSkills`, and the response has the same shape as the list in [Find a skill](#find-a-skill). In the console, expand the group in the catalog in **Skills**.

  ```bash theme={"theme":"css-variables"}
  curl 'https://api.recursion.labelbox.com/managed-agents/v1/skill-groups/6c2e9a47-1f83-45d0-b7a6-3e8c5f2d9a14/skills' \
    -H "Authorization: Bearer $RECURSION_API_KEY"
  ```
</Accordion>

<Accordion title="Delete a group">
  In the console, open the group's menu in the catalog in **Skills**, click **Archive group**, then confirm.

  ```bash theme={"theme":"css-variables"}
  curl -X DELETE 'https://api.recursion.labelbox.com/managed-agents/v1/skill-groups/6c2e9a47-1f83-45d0-b7a6-3e8c5f2d9a14' \
    -H "Authorization: Bearer $RECURSION_API_KEY"
  ```

  A delete returns `{ "deleted": true }`. The group's skills stay in the catalog. Agents that attach the group keep the entry, and their next session runs without the group's skills.
</Accordion>

## Delete a skill

A delete is a soft delete. The skill leaves the catalog and stops resolving into new sessions, and its versions can no longer be read through the API. A session that already started keeps the version it loaded. Agents that reference the skill keep the entry, and their next session runs without it. A deleted skill cannot be restored.

<Tabs>
  <Tab title="Console">
    1. In **Skills**, open the skill.
    2. At the bottom of the page, click **Archive skill**, then confirm **Archive skill**.
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -X DELETE 'https://api.recursion.labelbox.com/managed-agents/v1/skills/4e7a2c91-6d35-4f80-b8a1-9c3e5d7f2a64' \
      -H "Authorization: Bearer $RECURSION_API_KEY"
    ```
  </Tab>
</Tabs>

```json theme={"theme":"css-variables"}
{ "deleted": true }
```

## How an agent uses a skill

1. When a session starts, it resolves the agent's skills and skill groups and fixes the version of each skill for the whole session.
2. Every turn lists each skill's name and, while the budget allows, its description. See [Preview prompt disclosure](#preview-prompt-disclosure).
3. When a skill fits, the agent calls `activate_skill` with the skill's `name`. The full instructions and the list of bundled files enter the conversation and stay in effect for the rest of the session.
4. Bundled files are placed under `/workspace/.managed-agents/skills` in the sandbox. The agent reads or runs a file only when the instructions point it there.

`activate_skill` is offered only when the session has at least one usable skill.

## What can go wrong

The most common problems:

* **The agent never activates a skill.** Its description was dropped by the disclosure budget, or doesn't say when to use the skill. Run [Preview prompt disclosure](#preview-prompt-disclosure), move the skill earlier, and write the description as a condition.
* **A new skill version doesn't reach a running session.** Sessions fix skill versions when they start. Start a new session. Pinned entries never move; update `version` to change them.
* **`400 invalid_request` on `document` or `bundle`.** The frontmatter, instructions, or archive layout is wrong, or the document or bundle is over a limit. Read the full cause below.

<Accordion title="Every skills problem">
  | Symptom or code | Cause | Fix |
  | - | - | - |
  | The agent never activates a skill | Its description was dropped by the disclosure budget, or doesn't say when to use the skill. | Run [Preview prompt disclosure](#preview-prompt-disclosure), move the skill earlier, and write the description as a condition. |
  | A session runs without a skill the agent attaches | The skill or group was deleted after the agent attached it. [Preview prompt disclosure](#preview-prompt-disclosure) names it in `warnings`. | Remove the entry in a new agent version, or attach a replacement. |
  | A new skill version doesn't reach a running session | Sessions fix skill versions when they start. | Start a new session. Pinned entries never move; update `version` to change them. |
  | `400 invalid_request` on `document` or `bundle` | The frontmatter is missing, `name` or `description` is missing or malformed, there are no instructions below the frontmatter, the bundle has no `SKILL.md` at the root or one directory down, the wrapper directory's name doesn't match `name`, or the document or bundle is over a limit. | Fix the frontmatter or the archive layout, and check the [limits](#limits). |
  | `400 invalid_request` on a list filter | `skill_group_id` and `ungrouped` are combined, `search` is shorter than 2 characters, or `page_token` was sent with different filters. | Send one filter, and reuse the same filters with a page token. |
  | `400 invalid_request` on `skills` or `skills[n]` | An agent has more than 100 entries, or an entry names a skill, version, or group that doesn't exist, is deleted, or belongs to another organization. | Use groups to shorten the list, and check the ids in the named entry. |
  | `404 not_found` | A skill, version, or group doesn't exist, is deleted, or belongs to another organization. | Check the id. |
  | `409 conflict` on `name` | Another skill or group already uses the name. | Pick another name, or publish a new version of the existing skill. |
  | `409 revision_conflict` | `base_skill_version_id` is no longer the latest version. | Get the skill again, apply your change, and retry. |
  | `413 payload_too_large` | The whole request is larger than the upload limit. | Shrink the bundle to fit the [limits](#limits). |
  | `429 rate_limit_exceeded` | Your organization is over its request rate. | Wait for the time in `Retry-After`. Retry only when the operation's retry strategy in **Endpoints** permits replay. |

  The full error catalog is on [Errors](/recursion/errors).
</Accordion>

## Limits

Skill description, bundle, entry, list page, and search limits are in [Limits](/recursion/limits), with request sizes and rates. These apply too:

| Limit | Value |
| - | - |
| Skill and group `name` | 64 characters |
| `compatibility` in frontmatter | 500 characters |
| `SKILL.md` | 256 KiB |
| Instructions a session loads | 64,000 bytes of UTF-8 after escaping, then cut off |
| Skill and group `display_title` | 256 characters |
| Group `description` | 1,024 characters |
| `listSkillGroups` and `listSkillsInGroup` page size | 500, default 100 |

## Next steps

<CardGroup cols={2}>
  <Card title="Agents" href="/recursion/agents">
    Publish an agent version with skills and groups attached.
  </Card>

  <Card title="Tools" href="/recursion/tools">
    See what else the agent can call once a skill is active.
  </Card>

  <Card title="Sessions" href="/recursion/sessions">
    Start a session and watch the agent activate a skill.
  </Card>

  <Card title="Artifacts" href="/recursion/artifacts">
    Collect the files a skill tells the agent to produce.
  </Card>
</CardGroup>
