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.
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.
Endpoints
| Method | Endpoint | Purpose |
|---|
GET | /api/v1/skills | List/filter skills in the marketplace |
GET | /api/v1/skills/{skillId} | Get full skill details |
POST | /api/v1/workspaces/{workspaceId}/skills/import | Import a skill from URL or content |
POST | /api/v1/workspaces/{workspaceId}/skills/generate | AI-author a skill from intent |
POST | /api/v1/workspaces/{workspaceId}/skills/bulk-import | Import all SKILL.md files in a git repo |
DELETE | /api/v1/workspaces/{workspaceId}/skills/{skillId} | Delete a non-bundled skill |
GET | /api/v1/skills/proposed | List memory-proposed skills |
POST | /api/v1/skills/proposed/approve | Approve a proposed skill |
POST | /api/v1/skills/proposed/reject | Reject a proposed skill |
GET | /api/v1/templates | List workflow templates |
POST | /api/v1/templates | Create a workflow template |
GET | /api/v1/templates/{templateId} | Get a workflow template |
PATCH | /api/v1/templates/{templateId} | Update a workflow template |
DELETE | /api/v1/templates/{templateId} | Delete a workflow template |
GET | /api/v1/crew-templates | List crew-template blueprints |
GET | /api/v1/crew-templates/{slug} | Get a crew-template blueprint |
POST | /api/v1/crew-templates/{slug}/deploy | Deploy a crew blueprint |
POST | /api/v1/crew-ai-suggest | 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
[
{
"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) |
{
"url": "https://raw.githubusercontent.com/org/repo/main/SKILL.md"
}
or
{
"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")).
This endpoint spends real Anthropic tokens against the workspace’s API key on every call.
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 |
{
"slug": "extract-pdf-tables",
"prompt": "Use this when the user wants to extract tables from PDF documents into CSV."
}
Response: 201 Created
{
"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).
{
"git_url": "https://github.com/example/skills-pack.git",
"git_ref": "main",
"allow_unsafe_license": false,
"dry_run": false
}
Response: 200 OK
{
"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).
This is a permanent delete, not a soft delete. BUNDLED skills cannot be removed — they are re-seeded on every server start.
Auth: OWNER or ADMIN role (canRole(role, "manage"))
Response: 200 OK
{ "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
[
{
"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) |
{ "crew_id": "crew_123", "file_name": "skill-release-notes.md" }
Response: 200 OK
{
"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
{ "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 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) |
{ "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_TOKENs 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) |
{ "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 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 |