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

# Skills & Templates

> Skill marketplace, skill import, workflow templates, and crew templates.

Skills are reusable capabilities you assign to agents; templates are reusable blueprints for missions and crews. This page covers the skill marketplace (list, get, filter), bringing skills in (URL/paste import, bulk git import, and AI generation), the memory→Skills proposal bridge, plus workflow templates and crew-template blueprints with their AI-assisted wizard. A skill may bundle a system-prompt snippet, MCP server configuration, credential requirements, and tool definitions.

<Note>
  Most read endpoints accept a session or CLI token with workspace membership. Write endpoints have endpoint-specific auth: import/generate require `MANAGER`+, `DELETE /skills/{skillId}` requires `OWNER`/`ADMIN`, and some template actions (crew-template deploy, AI crew wizard) are workspace-member scoped — see each endpoint for its exact role gate.
</Note>

## Endpoints

| Method   | Endpoint                                                                     | Purpose                                 |
| -------- | ---------------------------------------------------------------------------- | --------------------------------------- |
| `GET`    | [`/api/v1/skills`](#list-skills)                                             | List/filter skills in the marketplace   |
| `GET`    | [`/api/v1/skills/{skillId}`](#get-skill)                                     | Get full skill details                  |
| `POST`   | [`/api/v1/workspaces/{workspaceId}/skills/import`](#import-skill)            | Import a skill from URL or content      |
| `POST`   | [`/api/v1/workspaces/{workspaceId}/skills/generate`](#generate-skill)        | AI-author a skill from intent           |
| `POST`   | [`/api/v1/workspaces/{workspaceId}/skills/bulk-import`](#bulk-import-skills) | Import all SKILL.md files in a git repo |
| `DELETE` | [`/api/v1/workspaces/{workspaceId}/skills/{skillId}`](#delete-skill)         | Delete a non-bundled skill              |
| `GET`    | [`/api/v1/skills/proposed`](#list-proposed-skills)                           | List memory-proposed skills             |
| `POST`   | [`/api/v1/skills/proposed/approve`](#approve-proposed-skill)                 | Approve a proposed skill                |
| `POST`   | [`/api/v1/skills/proposed/reject`](#reject-proposed-skill)                   | Reject a proposed skill                 |
| `GET`    | [`/api/v1/templates`](#list-templates)                                       | List workflow templates                 |
| `POST`   | [`/api/v1/templates`](#create-template)                                      | Create a workflow template              |
| `GET`    | [`/api/v1/templates/{templateId}`](#get-template)                            | Get a workflow template                 |
| `PATCH`  | [`/api/v1/templates/{templateId}`](#update-template)                         | Update a workflow template              |
| `DELETE` | [`/api/v1/templates/{templateId}`](#delete-template)                         | Delete a workflow template              |
| `GET`    | [`/api/v1/crew-templates`](#list-crew-templates)                             | List crew-template blueprints           |
| `GET`    | [`/api/v1/crew-templates/{slug}`](#get-crew-template)                        | Get a crew-template blueprint           |
| `POST`   | [`/api/v1/crew-templates/{slug}/deploy`](#deploy-crew-template)              | Deploy a crew blueprint                 |
| `POST`   | [`/api/v1/crew-ai-suggest`](#ai-crew-wizard)                                 | AI-suggest a crew configuration         |

***

## Skills

Skills are reusable capabilities that can be assigned to agents. They may include system prompt snippets, MCP server configurations, credential requirements, and tool definitions.

### List Skills

```
GET /api/v1/skills?workspace_id={workspaceId}
```

**Auth:** Session or CLI token + workspace membership

**Query Parameters:** (`internal/api/skills.go:99`)

| Parameter                | Type   | Description                                                                        |
| ------------------------ | ------ | ---------------------------------------------------------------------------------- |
| `category`               | string | Filter by category                                                                 |
| `source`                 | string | Filter by source                                                                   |
| `search`                 | string | Search in name, display\_name, and description                                     |
| `vendor`                 | string | Filter by vendor                                                                   |
| `maturity`               | string | Filter by maturity (`OFFICIAL`, `CURATED`, `COMMUNITY`, `EXPERIMENTAL`)            |
| `runtime`                | string | Filter by runtime                                                                  |
| `installed`              | string | Set to `"1"` to return only skills assigned to at least one agent in the workspace |
| `installed_for_agent_id` | string | Narrow the list to skills assigned to a specific agent                             |

**Response:** `200 OK`

```json theme={null}
[
  {
    "id": "skill_abc",
    "name": "github-pr-review",
    "slug": "github-pr-review",
    "display_name": "GitHub PR Review",
    "description": "Review pull requests and suggest improvements",
    "version": "1.0.0",
    "author": "Crewship",
    "category": "CODING",
    "source": "BUNDLED",
    "icon": "git-pull-request",
    "verification": "VERIFIED",
    "downloads": 1250,
    "rating_avg": 4.8,
    "rating_count": 45,
    "tags": "[\"github\", \"code-review\", \"pr\"]",
    "featured": true,
    "pricing_tier": "FREE",
    "tool_count": 3,
    "created_at": "2024-01-01T00:00:00Z",
    "updated_at": "2024-01-15T00:00:00Z"
  }
]
```

### Skill Fields

| Field                 | Type     | Description                                                                                                                                                                          |
| --------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`                  | string   | Skill ID                                                                                                                                                                             |
| `name`                | string   | Internal name                                                                                                                                                                        |
| `slug`                | string   | URL-safe identifier                                                                                                                                                                  |
| `display_name`        | string   | Human-readable name                                                                                                                                                                  |
| `description`         | string?  | Description                                                                                                                                                                          |
| `version`             | string   | Semantic version                                                                                                                                                                     |
| `author`              | string?  | Author name                                                                                                                                                                          |
| `category`            | string   | Category                                                                                                                                                                             |
| `source`              | string   | Origin source                                                                                                                                                                        |
| `icon`                | string?  | Lucide icon name                                                                                                                                                                     |
| `verification`        | string   | Verification status                                                                                                                                                                  |
| `downloads`           | integer  | Download count                                                                                                                                                                       |
| `rating_avg`          | number?  | Average rating                                                                                                                                                                       |
| `rating_count`        | integer  | Number of ratings                                                                                                                                                                    |
| `tags`                | string?  | JSON array of tag strings                                                                                                                                                            |
| `featured`            | boolean  | Whether skill is featured                                                                                                                                                            |
| `pricing_tier`        | string   | Pricing tier                                                                                                                                                                         |
| `tool_count`          | integer? | Number of tools provided                                                                                                                                                             |
| `vendor`              | string?  | Vendor name                                                                                                                                                                          |
| `homepage`            | string?  | Homepage URL                                                                                                                                                                         |
| `spdx_license`        | string?  | SPDX license identifier                                                                                                                                                              |
| `runtime`             | string   | Skill runtime                                                                                                                                                                        |
| `maturity`            | string   | `OFFICIAL`, `CURATED`, `COMMUNITY`, or `EXPERIMENTAL`                                                                                                                                |
| `scan_status`         | string   | Security scan status                                                                                                                                                                 |
| `description_quality` | string?  | Description quality rating                                                                                                                                                           |
| `installed_on`        | array?   | Per-agent install metadata (agent + crew info). Populated on the list response for skills installed on at least one agent; omitted (`omitempty`) when the skill is installed nowhere |

### Get Skill

```
GET /api/v1/skills/{skillId}?workspace_id={workspaceId}
```

Returns full skill details including content, credential requirements, and MCP configuration.

**Response:** `200 OK`

Additional fields beyond the list response:

| Field                     | Type     | Description                             |
| ------------------------- | -------- | --------------------------------------- |
| `content`                 | string?  | Skill content (system prompt, SKILL.md) |
| `credential_requirements` | string?  | JSON of required credentials            |
| `mcp_server_command`      | string?  | MCP server command                      |
| `mcp_server_image`        | string?  | MCP server Docker image                 |
| `mcp_transport`           | string?  | MCP transport type                      |
| `dependencies`            | string?  | JSON of dependencies                    |
| `license`                 | string?  | License identifier                      |
| `agent_count`             | integer  | Number of agents using this skill       |
| `security_score`          | integer? | Security score (0-100)                  |
| `allowed_domains`         | string?  | JSON of allowed domains                 |
| `changelog`               | string?  | Changelog text                          |

| Status | Condition       |
| ------ | --------------- |
| `404`  | Skill not found |

***

### Import Skill

```
POST /api/v1/workspaces/{workspaceId}/skills/import
```

Import a skill from a URL (GitHub raw content) or raw SKILL.md content.

**Auth:** `OWNER`, `ADMIN`, or `MANAGER` role

**Request Body:** Provide exactly one of `url` or `content`. (`internal/api/skills.go:351`)

| Field                  | Type    | Required    | Description                                                                       |
| ---------------------- | ------- | ----------- | --------------------------------------------------------------------------------- |
| `url`                  | string  | Conditional | URL to fetch SKILL.md from                                                        |
| `content`              | string  | Conditional | Raw SKILL.md content                                                              |
| `allow_unsafe_license` | boolean | No          | Opt-in to import skills whose SPDX license is flagged as unsafe (default `false`) |

```json theme={null}
{
  "url": "https://raw.githubusercontent.com/org/repo/main/SKILL.md"
}
```

or

```json theme={null}
{
  "content": "---\nname: my-skill\n---\n# My Custom Skill\n..."
}
```

URLs are validated against SSRF attacks (private IP ranges are blocked).

**Response:** `201 Created` -- imported skill object.

| Status | Condition                                                             |
| ------ | --------------------------------------------------------------------- |
| `400`  | Invalid JSON, no url/content, both provided, invalid URL, parse error |
| `403`  | Insufficient role                                                     |

***

### Generate Skill

```
POST /api/v1/workspaces/{workspaceId}/skills/generate
```

Authors a SKILL.md from a natural-language intent by calling Anthropic with a condensed skill-creator prompt, then inserts it with `source = 'GENERATED'`. The user can edit the body via the skills detail page afterwards. (`internal/api/skills_generate.go:94`)

**Auth:** `OWNER`, `ADMIN`, or `MANAGER` role (`canRole(role, "create")`).

<Note>
  This endpoint spends real Anthropic tokens against the workspace's API key on every call.
</Note>

**Prerequisite:** Workspace must have an `ACTIVE` credential with `provider = ANTHROPIC` and `type = API_KEY` (not `AI_CLI_TOKEN`). OAuth bearer tokens from `claude` CLI login won't work against the Messages API.

**Request Body:**

| Field    | Type   | Required | Default               | Description                                                 |
| -------- | ------ | -------- | --------------------- | ----------------------------------------------------------- |
| `slug`   | string | Yes      | --                    | Desired skill slug (will be slugified)                      |
| `prompt` | string | Yes      | --                    | Natural-language intent describing what the skill should do |
| `model`  | string | No       | `"claude-sonnet-4-6"` | Override the Anthropic model used for authoring             |

```json theme={null}
{
  "slug": "extract-pdf-tables",
  "prompt": "Use this when the user wants to extract tables from PDF documents into CSV."
}
```

**Response:** `201 Created`

```json theme={null}
{
  "skill_id": "sk_a1b2c3d4e5f60718",
  "slug": "extract-pdf-tables",
  "content": "---\nname: extract-pdf-tables\n...\n---\n# Extract PDF Tables\n...",
  "scan_status": "CLEAN",
  "scan_reason": "",
  "description_quality": "good"
}
```

| Status | Condition                                                                         |
| ------ | --------------------------------------------------------------------------------- |
| `400`  | Missing `slug`/`prompt`, invalid JSON, or missing `workspaceId` path value        |
| `403`  | Insufficient role                                                                 |
| `409`  | Insert conflict (slug already exists in this workspace)                           |
| `412`  | No active Anthropic `API_KEY` credential available in the workspace               |
| `502`  | Anthropic call failed, frontmatter missing, or parser rejected the model's output |

***

### Bulk Import Skills

```
POST /api/v1/workspaces/{workspaceId}/skills/bulk-import
```

Walks a remote git repository for `SKILL.md` files and upserts each through the same v65-aware path used by `Import`, including SPDX license gating and the content scanner. Per-skill rejections are returned in the `skipped` array rather than failing the whole batch. (`internal/api/skills_bulk_import.go:106`)

**Auth:** `OWNER`, `ADMIN`, or `MANAGER` role (`canRole(role, "create")`)

**Request Body:**

| Field                  | Type      | Required | Default      | Description                                                                   |
| ---------------------- | --------- | -------- | ------------ | ----------------------------------------------------------------------------- |
| `git_url`              | string    | Yes      | --           | HTTPS git URL. Private IPs, `localhost`, and embedded user-info are rejected. |
| `git_ref`              | string    | No       | repo default | Branch, tag, or commit to clone                                               |
| `paths`                | string\[] | No       | `[]`         | Optional sub-paths inside the repo to scan                                    |
| `vendor`               | string    | No       | repo host    | Override the `vendor` column written on each row                              |
| `allow_unsafe_license` | boolean   | No       | `false`      | Import skills whose SPDX license is flagged unsafe                            |
| `dry_run`              | boolean   | No       | `false`      | Walk and validate without writing to the database                             |

`local_path` is intentionally **not** part of the HTTP surface (it would turn the endpoint into an arbitrary host-FS read primitive).

```json theme={null}
{
  "git_url": "https://github.com/example/skills-pack.git",
  "git_ref": "main",
  "allow_unsafe_license": false,
  "dry_run": false
}
```

**Response:** `200 OK`

```json theme={null}
{
  "source": "https://github.com/example/skills-pack.git@main",
  "total_found": 12,
  "total_imported": 10,
  "imported": [
    { "skill_id": "sk_abc", "slug": "code-reviewer", "created": true },
    { "skill_id": "sk_def", "slug": "release-notes", "created": false }
  ],
  "skipped": [
    { "path": "skills/internal/SKILL.md", "slug": "internal", "reason": "license GPL-3.0 blocked; pass allow_unsafe_license=true to override" }
  ],
  "truncated": false
}
```

`truncated` is `true` when the walker hit its per-batch cap before reaching the end of the source tree.

| Status | Condition                                                                    |
| ------ | ---------------------------------------------------------------------------- |
| `400`  | Invalid JSON, missing `git_url`, or missing `workspaceId` path value         |
| `403`  | Insufficient role                                                            |
| `502`  | Validation rejected (private/internal IP, malformed URL) or git clone failed |

***

### Delete Skill

```
DELETE /api/v1/workspaces/{workspaceId}/skills/{skillId}
```

Permanently deletes a non-bundled skill (`internal/api/skills.go:519`).

<Warning>
  This is a permanent delete, not a soft delete. `BUNDLED` skills cannot be removed — they are re-seeded on every server start.
</Warning>

**Auth:** `OWNER` or `ADMIN` role (`canRole(role, "manage")`)

**Response:** `200 OK`

```json theme={null}
{ "deleted": true, "skill_id": "skill_abc" }
```

| Status | Condition                                                                             |
| ------ | ------------------------------------------------------------------------------------- |
| `403`  | Insufficient role, or attempt to delete a `BUNDLED` skill (re-seeded on server start) |
| `404`  | Skill not found                                                                       |

***

## Proposed Skills (memory → Skills bridge)

When the memory consolidator runs in proposal mode it stages auto-promoted SKILL.md files under `.memory/{crew-slug}/topics/.proposed/skill-*.md`. These endpoints are the HITL surface for reviewing those stagings. The handler is stateless against the database — disk is the source of truth, and approve/reject emit `EntryMemorySkillApproved` / `EntryMemorySkillRejected` journal entries. (`internal/api/skills_proposed_handler.go`)

**Auth (all three):** `OWNER`, `ADMIN`, or `MANAGER` role -- same threshold as canonical skill import.

### List Proposed Skills

```
GET /api/v1/skills/proposed?crew_id={crewId}
```

Lists skill files staged for review under the given crew. Returns an empty array when no `.proposed` directory exists for the crew.

**Query Parameters:**

| Parameter | Type   | Required | Description                                           |
| --------- | ------ | -------- | ----------------------------------------------------- |
| `crew_id` | string | Yes      | Crew whose `.proposed/` directory should be inspected |

**Response:** `200 OK`

```json theme={null}
[
  {
    "file_name": "skill-release-notes.md",
    "name": "release-notes",
    "description": "Use when the user asks to summarise commits between two tags.",
    "description_quality": "good",
    "category": "automation"
  }
]
```

A file that fails the SKILL.md parser is still surfaced with `description_quality` set to `"parse error: <reason>"` so an operator can manually reject it.

| Status | Condition                                 |
| ------ | ----------------------------------------- |
| `400`  | Missing `crew_id`                         |
| `401`  | No workspace context on the request       |
| `403`  | Insufficient role                         |
| `404`  | Crew not found in this workspace          |
| `503`  | Server has no crew-memory root configured |

### Approve Proposed Skill

```
POST /api/v1/skills/proposed/approve
```

Imports the staged SKILL.md through the canonical skills importer (same path as URL/paste import), then removes the staging file on success.

**Note:** The router pattern is `/skills/proposed/approve` (body-based), not a path-parameter `/{id}/approve` form.

**Request Body:**

| Field       | Type   | Required | Description                                                                          |
| ----------- | ------ | -------- | ------------------------------------------------------------------------------------ |
| `crew_id`   | string | Yes      | Crew the file lives under                                                            |
| `file_name` | string | Yes      | Bare basename returned by List (must match `skill-*.md`; path traversal is rejected) |

```json theme={null}
{ "crew_id": "crew_123", "file_name": "skill-release-notes.md" }
```

**Response:** `200 OK`

```json theme={null}
{
  "skill_id": "sk_a1b2c3d4e5f60718",
  "slug": "release-notes",
  "created": true,
  "file_name": "skill-release-notes.md"
}
```

| Status | Condition                                                                    |
| ------ | ---------------------------------------------------------------------------- |
| `400`  | Invalid JSON, missing `crew_id`/`file_name`, or unsafe `file_name`           |
| `403`  | Insufficient role                                                            |
| `404`  | Crew not found, or staged file missing                                       |
| `422`  | Staged content failed the importer (frontmatter malformed, license rejected) |
| `503`  | Server has no crew-memory root configured                                    |

### Reject Proposed Skill

```
POST /api/v1/skills/proposed/reject
```

Deletes the staging file and emits an audit entry. Idempotent: calling reject twice returns `200` with `removed: false` on the second call.

**Request Body:** Same shape as Approve (`crew_id`, `file_name`).

**Response:** `200 OK`

```json theme={null}
{ "file_name": "skill-release-notes.md", "removed": true }
```

| Status | Condition                                           |
| ------ | --------------------------------------------------- |
| `400`  | Invalid JSON, missing fields, or unsafe `file_name` |
| `403`  | Insufficient role                                   |
| `404`  | Crew not found                                      |
| `503`  | Server has no crew-memory root configured           |

***

## Skill Sources

`source` is a free-form string, not a closed enum. The values the
codebase actually writes:

| Source      | Description                                                                                            |
| ----------- | ------------------------------------------------------------------------------------------------------ |
| `BUNDLED`   | Server-seeded skill that ships with Crewship (re-seeded on every start; cannot be deleted)             |
| `GENERATED` | Authored by the [Generate](#generate-skill) endpoint                                                   |
| `CUSTOM`    | Workspace-defined skill (e.g. applied via manifest)                                                    |
| *(a URL)*   | URL-imported skills store the normalized source URL in `source`; pasted-content imports leave it empty |

## Skill Categories

The validated `category` enum (uppercase; parser/`internal/skills/parser.go`,
must match the `SkillCategory` enum in `prisma/schema.prisma`):

`CODING`, `AUTOMATION`, `DATA`, `DEVOPS`, `SUPPORT`, `SALES`, `WRITING`, `RESEARCH`, `PM`, `DESIGN`, `SECURITY`, `FINANCE`, `OPS`, `CUSTOM`

A skill whose frontmatter category is missing or unrecognised defaults to `CUSTOM`.

## Skill Runtimes

`runtime` enum: `INSTRUCTIONS` (default), `SCRIPT`, `MCP`, `HYBRID`.

## Skill Maturity

`maturity` enum: `OFFICIAL`, `CURATED`, `COMMUNITY` (default), `EXPERIMENTAL`. The Skills list sorts `OFFICIAL` → `CURATED` → `COMMUNITY` → other, then by name.

***

## Workflow Templates

Workflow templates define reusable task structures for missions.

### List Templates

```
GET /api/v1/templates?workspace_id={workspaceId}
```

**Auth:** Session or CLI token + workspace membership

**Response:** `200 OK` -- array of template objects.

### Create Template

```
POST /api/v1/templates?workspace_id={workspaceId}
```

**Auth:** `OWNER`, `ADMIN`, or `MANAGER` role

**Response:** `201 Created`

### Get Template

```
GET /api/v1/templates/{templateId}?workspace_id={workspaceId}
```

**Response:** `200 OK`

| Status | Condition          |
| ------ | ------------------ |
| `404`  | Template not found |

### Update Template

```
PATCH /api/v1/templates/{templateId}?workspace_id={workspaceId}
```

**Auth:** `OWNER`, `ADMIN`, or `MANAGER` role

**Response:** `200 OK`

### Delete Template

```
DELETE /api/v1/templates/{templateId}?workspace_id={workspaceId}
```

**Auth:** `OWNER`, `ADMIN`, or `MANAGER` role

**Response:** `204 No Content`

***

## Crew Templates (Blueprints)

Crew templates are pre-configured crew blueprints that can be deployed to create a fully set up crew with agents, skills, and credentials.

### List Crew Templates

```
GET /api/v1/crew-templates?workspace_id={workspaceId}
```

**Response:** `200 OK` -- array of crew template objects.

### Get Crew Template

```
GET /api/v1/crew-templates/{slug}?workspace_id={workspaceId}
```

**Response:** `200 OK` -- full template details including agents and configuration.

### Deploy Crew Template

```
POST /api/v1/crew-templates/{slug}/deploy?workspace_id={workspaceId}
```

Deploys the template, creating:

* A new crew with the template's configuration
* All agents defined in the template
* Auto-assignment of credentials and skills

**Auth:** Session or CLI token + workspace membership. The handler does
**not** perform a role check beyond workspace context
(`internal/api/crew_templates.go:364`).

**Request Body:**

| Field       | Type   | Required | Description                                                   |
| ----------- | ------ | -------- | ------------------------------------------------------------- |
| `crew_name` | string | Yes      | Name for the new crew                                         |
| `crew_slug` | string | No       | Slug for the new crew (derived from `crew_name` when omitted) |

```json theme={null}
{ "crew_name": "Release Crew" }
```

**Response:** `201 Created` -- the created crew and agents.

| Status | Condition                                            |
| ------ | ---------------------------------------------------- |
| `400`  | Invalid JSON, or missing `crew_name`                 |
| `404`  | Template not found                                   |
| `409`  | A crew with the derived/supplied slug already exists |

***

## AI Crew Wizard

```
POST /api/v1/crew-ai-suggest?workspace_id={workspaceId}
```

Uses AI to suggest a crew configuration based on a natural language description. Spends Anthropic tokens against the workspace's API key.

**Auth:** Session or CLI token + workspace membership. The handler does
**not** perform a role check beyond workspace context
(`internal/api/crew_ai.go:89`).

**Prerequisite:** Workspace must have an `ACTIVE` credential with
`provider = ANTHROPIC` and `type = API_KEY` (OAuth `AI_CLI_TOKEN`s from
`claude` CLI login do not work against the Messages API).

**Request Body:**

| Field         | Type   | Required | Description                                       |
| ------------- | ------ | -------- | ------------------------------------------------- |
| `description` | string | Yes      | Natural-language description (10–2000 characters) |

```json theme={null}
{ "description": "A crew that triages GitHub issues and drafts release notes." }
```

**Response:** `200 OK` -- suggested crew configuration.

| Status | Condition                                                                    |
| ------ | ---------------------------------------------------------------------------- |
| `400`  | Invalid JSON, or `description` shorter than 10 / longer than 2000 characters |
| `422`  | No active Anthropic `API_KEY` credential in the workspace                    |
| `502`  | AI suggestion call failed                                                    |

***

## Agent Skills (Per-Agent)

See the [Agents](/api-reference/agents) page for per-agent skill management:

| Endpoint                                           | Method | Description                   |
| -------------------------------------------------- | ------ | ----------------------------- |
| `GET /api/v1/agents/{agentId}/skills`              | GET    | List skills assigned to agent |
| `POST /api/v1/agents/{agentId}/skills`             | POST   | Assign skill to agent         |
| `DELETE /api/v1/agents/{agentId}/skills/{skillId}` | DELETE | Remove skill from agent       |
