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

# Skills

> Versioned packages of instructions and files that agents load on demand

A skill packages reusable instructions and optional files for agents. You upload it once, attach its ID to agent configs, and the model calls the built-in [`skill` tool](/tools/built-in#skill) when it needs the instructions.

Skills are:

* **Lazy** — only the name and description enter the model's context until it loads the skill.
* **Versioned** — uploading the same skill again creates a new revision under the same skill ID.
* **Shared** — one skill can be granted to multiple projects and attached to many agents.

## Upload a skill

Upload a `.zip` or `.tar.gz` archive with one top-level directory:

```text theme={null}
release-notes/
├── SKILL.md
└── templates/
    └── example.md
```

`SKILL.md` starts with the skill's name and description:

```md theme={null}
---
name: release-notes
description: Use when writing release notes for Acme.
---

Follow the Acme release-note format in templates/example.md.
```

The `name` must match the top-level directory. Write the description as a trigger—“Use when…”—because it helps the model decide when to load the skill.

<Tabs>
  <Tab title="API">
    The examples assume the client, `$ORG`/`orgID`, and `$PROJ`/`projectID` setup from the [quickstart](/quickstart).

    `POST /skills` takes multipart form data: an `owner` and the `archive` itself. The CLI's `--archive` takes a `.zip` or `.tar.gz` file, or a skill directory that it packs for you:

    <CodeGroup>
      ```bash CLI theme={null}
      omnara skills create --owner-kind org --archive release-notes/ --json
      ```

      ```bash REST theme={null}
      curl "$OMNARA_API/orgs/$ORG/skills" \
        -H "Authorization: Bearer $OMNARA_TOKEN" \
        -F 'owner={"kind": "org"}' \
        -F "archive=@release-notes-skill.zip"
      ```

      ```typescript SDK theme={null}
      import { readFile } from 'node:fs/promises'

      const bytes = await readFile('release-notes-skill.zip')

      const skill = await sdk.createSkill({
        client,
        path: { orgID },
        body: {
          owner: { kind: 'org' },
          archive: new File([bytes], 'release-notes-skill.zip'),
        },
      })
      ```
    </CodeGroup>

    ```json theme={null}
    {
      "id": "skl_ik7mv4qtrwz3jehcyd5n6a2bfg",
      "org_id": "org_k7mv4qtrwz3jehcyd5n6a2bfgi",
      "owner": { "kind": "org" },
      "name": "release-notes",
      "revision_id": "skr_d5n6a2bfgik7mv4qtrwz3jehcy",
      "revision": 1,
      "description": "Use when writing release notes for Acme.",
      "skill_md": "---\nname: release-notes\ndescription: Use when writing release notes for Acme.\n---\n\nFollow the Acme release-note format in templates/example.md.",
      "created_at": "2026-08-03T20:31:04Z",
      "updated_at": "2026-08-03T20:31:04Z"
    }
    ```

    For a project- or user-owned skill, set the owner accordingly: `--owner-kind project --owner-project-id proj_…` or `--owner-kind user` with the CLI, or `{"kind": "project", "project_id": "proj_…"}` / `{"kind": "user"}` as the `owner`.
  </Tab>

  <Tab title="Dashboard">
    1. Open **Organization Skills**, **User Skills**, or a project's **Project Skills** page. The page determines who owns the skill.
    2. Click **Upload skill** and select the archive.

    Omnara reads the name and description from `SKILL.md`. Uploading the same skill name to the same owner creates a new revision.
  </Tab>
</Tabs>

The owner (`org`, `project`, or `user`) decides who manages the skill and cannot change. A project can attach only skills it owns or has been granted—even skills owned by its organization.

Uploading again with the same owner and name creates the next revision: the `skl_` ID stays the same and the `skr_` revision ID changes. Attached agents resolve the latest revision at each model call, so they do not need to be relaunched.

## List, inspect, delete

Each skills page lists the skills owned at that scope; `omnara skills list` does the same from the terminal. Fetching a skill (`omnara skills get {skill-id}` or the API) returns `skill_md`, the full instruction text of its current revision. To delete an owned skill in the dashboard, open its row menu and click **Delete**, or run `omnara skills delete {skill-id}`. Deletion removes its revisions and grants, but is blocked with `409` while an active agent config references it.

## Grant it to projects

A project can use a skill only if it owns it or holds a grant — org- and user-owned skills must be granted to each project that references them. Grants share without transferring ownership.

<Tabs>
  <Tab title="API">
    The response identifies the grant and target project:

    <CodeGroup>
      ```bash CLI theme={null}
      omnara grant skills add skl_ik7mv4qtrwz3jehcyd5n6a2bfg \
        --target-project-id proj_4qtrwz3jehcyd5n6a2bfgik7mv \
        --json
      ```

      ```bash REST theme={null}
      curl "$OMNARA_API/orgs/$ORG/skills/skl_ik7mv4qtrwz3jehcyd5n6a2bfg/grants" \
        -H "Authorization: Bearer $OMNARA_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{"target_project_id": "proj_4qtrwz3jehcyd5n6a2bfgik7mv"}'
      ```

      ```typescript SDK theme={null}
      await sdk.createSkillGrant({
        client,
        path: { orgID, skillID: 'skl_ik7mv4qtrwz3jehcyd5n6a2bfg' },
        body: { target_project_id: 'proj_4qtrwz3jehcyd5n6a2bfgik7mv' },
      })
      ```
    </CodeGroup>

    ```json theme={null}
    {
      "id": "skg_jehcyd5n6a2bfgik7mv4qtrwz3",
      "org_id": "org_k7mv4qtrwz3jehcyd5n6a2bfgi",
      "skill_id": "skl_ik7mv4qtrwz3jehcyd5n6a2bfg",
      "target_project_id": "proj_4qtrwz3jehcyd5n6a2bfgik7mv",
      "created_at": "2026-08-03T20:34:52Z"
    }
    ```

    List a skill's project grants with `omnara grant skills list {skill-id}` or `GET …/grants`; revoke one with `omnara grant skills delete {skill-id} {grant-id}` or `DELETE …/grants/{grantID}`.

    To confirm which skills a project can use (the CLI has no equivalent command):

    <CodeGroup>
      ```bash REST theme={null}
      curl "$OMNARA_API/orgs/$ORG/projects/$PROJ/skills" -H "Authorization: Bearer $OMNARA_TOKEN"
      ```

      ```typescript SDK theme={null}
      const available = await sdk.listProjectAvailableSkills({
        client,
        path: { orgID, projectID },
      })
      ```
    </CodeGroup>

    Each result reports whether the project owns the skill (`direct`) or receives it through a `grant`.
  </Tab>

  <Tab title="Dashboard">
    On the skill's row, click **Grant to project** and pick the target. Incoming grants appear on the project's **Grants** page, **Skills** tab — **Remove skill grant** revokes one.
  </Tab>
</Tabs>

## Attach it to an agent

Reference the skill ID in the config; config creation verifies the project can use it:

```yaml theme={null}
skills:
  - skl_ik7mv4qtrwz3jehcyd5n6a2bfg
```

Attaching a skill automatically enables the built-in `skill` tool. The model sees each skill's name and description; when it calls the tool, Omnara returns the `SKILL.md` instructions and attempts to install the supporting files on each attached machine. The instructions still work when no machine is attached or an installation fails.

<Info>
  Full schema and playground: [Create skill](/api-reference/endpoints/skills/create-skill) · [List skills](/api-reference/endpoints/skills/list-skills-visible-through-ownership-authority) · [Skill grants](/api-reference/endpoints/skills/create-skill-grant) · [Project skills](/api-reference/endpoints/skills/list-skills-available-to-project).
</Info>
