Skills API
Skills are reusable instruction patterns that the agent injects into its system message to handle specific tasks better. Unlike tools (executable functions), skills are prompt-based knowledge that guide the agent's behavior. Skills support semantic vector search, marketplace publishing, and usage tracking.
Base URL
All Skills API requests are served by the kaman-agent service:
/agent/api/skills
For self-hosted installations, prepend your instance URL: http://kaman.ai/agent/api/skills
Authentication
All endpoints require a valid Bearer token in the Authorization header:
Authorization: Bearer kam_your_api_key
List Skills
List all skills accessible to the authenticated client, including system skills.
Endpoint
GET /agent/api/skills
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| status | string | No | Filter by status (e.g. active, deprecated) |
| source_type | string | No | Filter by source type (e.g. manual, auto_generated, marketplace) |
| domains | string | No | Comma-separated list of domains to filter by |
| limit | number | No | Max results to return (default: 50) |
| offset | number | No | Number of results to skip for pagination (default: 0) |
Example
curl -X GET "http://kaman.ai/agent/api/skills?status=active&limit=10&offset=0" \
-H "Authorization: Bearer kam_your_api_key"
Response
{
"skills": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"client_id": "42",
"name": "SQL Query Best Practices",
"slug": "sql-query-best-practices",
"description": "Guidelines for writing efficient and safe SQL queries",
"version": 1,
"content": "When writing SQL queries, always use parameterized queries to prevent injection...",
"content_tokens": 256,
"trigger_keywords": ["sql", "query", "database"],
"task_types": ["data-query", "database"],
"domains": ["engineering", "data"],
"tools": ["executeSQL", "queryDatabase"],
"source_type": "manual",
"usage_count": 15,
"success_count": 12,
"failure_count": 3,
"status": "active",
"created_at": "2026-02-10T08:00:00.000Z",
"updated_at": "2026-02-15T14:30:00.000Z",
"last_used_at": "2026-02-15T14:30:00.000Z",
"supporting_files": [],
"is_marketplace": false,
"is_published": false,
"author": null
}
],
"totalCount": 1
}
Search Skills
Search skills using hybrid vector and keyword matching. Returns results ranked by semantic similarity to the query. Only returns active skills with an embedding.
Endpoint
POST /agent/api/skills/search
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| query | string | Yes | Natural language search query |
| limit | number | No | Max results to return (default: 10) |
| domains | string[] | No | Array of domains to filter by |
Example
curl -X POST "http://kaman.ai/agent/api/skills/search" \
-H "Authorization: Bearer kam_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"query": "how to write safe SQL queries",
"limit": 5,
"domains": ["engineering"]
}'
Response
Returns an array of skills with a similarity score (0 to 1). Results below 0.3 similarity are filtered out.
[
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "SQL Query Best Practices",
"slug": "sql-query-best-practices",
"description": "Guidelines for writing efficient and safe SQL queries",
"content": "When writing SQL queries, always use parameterized queries...",
"trigger_keywords": ["sql", "query", "database"],
"domains": ["engineering", "data"],
"status": "active",
"similarity": 0.87
}
]
Get Skill by ID
Retrieve a single skill by its UUID. Returns skills belonging to the authenticated client or system skills.
Endpoint
GET /agent/api/skills/:id
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | string (UUID) | The skill ID |
Example
curl -X GET "http://kaman.ai/agent/api/skills/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
-H "Authorization: Bearer kam_your_api_key"
Response
Returns the full skill object.
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"client_id": "42",
"name": "SQL Query Best Practices",
"slug": "sql-query-best-practices",
"description": "Guidelines for writing efficient and safe SQL queries",
"version": 1,
"content": "When writing SQL queries, always use parameterized queries...",
"content_tokens": 256,
"trigger_keywords": ["sql", "query", "database"],
"task_types": ["data-query", "database"],
"domains": ["engineering", "data"],
"tools": ["executeSQL", "queryDatabase"],
"source_type": "manual",
"source_session_id": null,
"parent_skill_id": null,
"usage_count": 15,
"success_count": 12,
"failure_count": 3,
"status": "active",
"created_at": "2026-02-10T08:00:00.000Z",
"updated_at": "2026-02-15T14:30:00.000Z",
"last_used_at": "2026-02-15T14:30:00.000Z",
"supporting_files": [],
"marketplace_plugin_id": null,
"is_marketplace": false,
"is_published": false,
"author": null
}
Create Skill
Create a new skill. A slug is auto-generated from the name, and a 384-dimensional vector embedding is computed from the name, description, and content for semantic search. Token count is estimated at ~4 characters per token.
Endpoint
POST /agent/api/skills
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Skill name (max 255 chars) |
| content | string | Yes | The skill content -- reusable instructions or patterns |
| description | string | No | Human-readable description |
| trigger_keywords | string[] | No | Keywords that trigger this skill |
| task_types | string[] | No | Task types this skill applies to |
| domains | string[] | No | Domain categories (e.g. engineering, data) |
| tools | string[] | No | Associated tool names |
| source_type | string | No | How the skill was created: manual (default), auto_generated, marketplace |
| source_session_id | string | No | Session ID that generated this skill (for auto-generated skills) |
| parent_skill_id | string (UUID) | No | ID of parent skill if this is a derived version |
| supporting_files | object[] | No | Additional files bundled with the skill |
| supporting_files[].path | string | -- | File path within the skill bundle |
| supporting_files[].filename | string | -- | File name |
| supporting_files[].content | string | -- | File content |
| supporting_files[].size | number | -- | File size in bytes |
| supporting_files[].sha | string | -- | SHA hash of the file content |
Example
curl -X POST "http://kaman.ai/agent/api/skills" \
-H "Authorization: Bearer kam_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "SQL Query Best Practices",
"description": "Guidelines for writing efficient and safe SQL queries",
"content": "When writing SQL queries:\n1. Always use parameterized queries\n2. Add LIMIT clauses to prevent runaway queries\n3. Use indexes on frequently filtered columns",
"trigger_keywords": ["sql", "query", "database"],
"task_types": ["data-query"],
"domains": ["engineering", "data"],
"tools": ["executeSQL"]
}'
Response (201 Created)
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"client_id": "42",
"name": "SQL Query Best Practices",
"slug": "sql-query-best-practices",
"description": "Guidelines for writing efficient and safe SQL queries",
"version": 1,
"content": "When writing SQL queries:\n1. Always use parameterized queries\n2. Add LIMIT clauses to prevent runaway queries\n3. Use indexes on frequently filtered columns",
"content_tokens": 41,
"trigger_keywords": ["sql", "query", "database"],
"task_types": ["data-query"],
"domains": ["engineering", "data"],
"tools": ["executeSQL"],
"source_type": "manual",
"usage_count": 0,
"success_count": 0,
"failure_count": 0,
"status": "active",
"supporting_files": [],
"is_marketplace": false,
"is_published": false,
"created_at": "2026-02-17T10:00:00.000Z",
"updated_at": "2026-02-17T10:00:00.000Z"
}
Update Skill
Update an existing skill. If name, description, or content change, the vector embedding is automatically re-generated. Token count is recalculated if content or supporting_files change.
System skills (client_id = "system") cannot be modified.
Endpoint
PUT /agent/api/skills/:id
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | string (UUID) | The skill ID |
Request Body
All fields from the create endpoint are accepted. Only provided fields are updated.
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | No | Updated skill name |
| content | string | No | Updated skill content |
| description | string | No | Updated description |
| trigger_keywords | string[] | No | Updated keywords |
| task_types | string[] | No | Updated task types |
| domains | string[] | No | Updated domains |
| tools | string[] | No | Updated tool associations |
| supporting_files | object[] | No | Updated supporting files |
| status | string | No | Updated status |
Example
curl -X PUT "http://kaman.ai/agent/api/skills/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
-H "Authorization: Bearer kam_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"content": "Updated SQL best practices:\n1. Always use parameterized queries\n2. Add LIMIT clauses\n3. Use CTEs for readability\n4. Avoid SELECT *",
"trigger_keywords": ["sql", "query", "database", "optimization"]
}'
Response
Returns the full updated skill object.
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "SQL Query Best Practices",
"content": "Updated SQL best practices:\n1. Always use parameterized queries\n2. Add LIMIT clauses\n3. Use CTEs for readability\n4. Avoid SELECT *",
"trigger_keywords": ["sql", "query", "database", "optimization"],
"updated_at": "2026-02-17T12:00:00.000Z"
}
Delete Skill (Soft Delete)
Soft-delete a skill by setting its status to deprecated. The record remains in the database but is excluded from search results and active listings.
System skills (client_id = "system") cannot be deleted.
Endpoint
DELETE /agent/api/skills/:id
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | string (UUID) | The skill ID |
Example
curl -X DELETE "http://kaman.ai/agent/api/skills/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
-H "Authorization: Bearer kam_your_api_key"
Response
{
"message": "Skill deprecated"
}
Publish Skill to Marketplace
Publish a skill to the marketplace so other clients can discover and install it. Sends an extension_saved event via Kafka with the skill manifest and marks the skill as published.
System skills cannot be published.
Endpoint
POST /agent/api/skills/:id/publish
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | string (UUID) | The skill ID |
Example
curl -X POST "http://kaman.ai/agent/api/skills/a1b2c3d4-e5f6-7890-abcd-ef1234567890/publish" \
-H "Authorization: Bearer kam_your_api_key"
Response
{
"message": "Skill published to marketplace",
"skillId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
Side Effects
- Sets
is_publishedtotrueon the skill record - Sets
authorto the authenticated user's name - Produces a Kafka
extension_savedmessage with the following payload:
{
"name": "SQL Query Best Practices",
"description": "Guidelines for writing efficient and safe SQL queries",
"type": "skill",
"published": true,
"isDraft": false,
"status": "published",
"version": "1.0.0",
"clientId": 42,
"userId": "user-uuid",
"author": "Jane Doe",
"category": "engineering",
"skill_manifest": {
"content": "When writing SQL queries...",
"triggerKeywords": ["sql", "query", "database"],
"taskTypes": ["data-query"],
"domains": ["engineering", "data"],
"tools": ["executeSQL"],
"supporting_files": []
},
"manifest": {
"skill_manifest": { "..." : "..." }
}
}
Unpublish Skill
Remove a skill from the marketplace. Sends an extension_deleted event via Kafka and marks the skill as unpublished.
System skills cannot be unpublished.
Endpoint
POST /agent/api/skills/:id/unpublish
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | string (UUID) | The skill ID |
Example
curl -X POST "http://kaman.ai/agent/api/skills/a1b2c3d4-e5f6-7890-abcd-ef1234567890/unpublish" \
-H "Authorization: Bearer kam_your_api_key"
Response
{
"message": "Skill unpublished from marketplace",
"skillId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
Side Effects
- Sets
is_publishedtofalseon the skill record - Produces a Kafka
extension_deletedmessage with:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "SQL Query Best Practices",
"type": "skill",
"clientId": 42,
"skill_manifest": {
"content": "When writing SQL queries..."
}
}
Install Marketplace Skill
Install a published skill from another client into your own workspace. Creates a copy of the skill with source_type set to marketplace and is_marketplace set to true.
Endpoint
POST /agent/api/skills/:id/install
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| id | string (UUID) | The source skill ID (from marketplace search results) |
Example
curl -X POST "http://kaman.ai/agent/api/skills/b2c3d4e5-f6a7-8901-bcde-f12345678901/install" \
-H "Authorization: Bearer kam_your_api_key"
Response (201 Created)
Returns the newly created skill copy.
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"client_id": "42",
"name": "SQL Query Best Practices",
"slug": "sql-query-best-practices",
"content": "When writing SQL queries...",
"source_type": "marketplace",
"is_marketplace": true,
"status": "active",
"created_at": "2026-02-17T10:00:00.000Z"
}
Error Responses
| Status | Error | Description |
|---|---|---|
| 400 | Cannot install your own skill | The source skill belongs to the same client |
| 404 | Published skill not found | The skill ID does not exist, is not published, or is not active |
| 409 | Skill already installed | A skill with the same name already exists for this client |
Search Marketplace Skills
Search for published skills from other clients that are available for installation. Returns results ranked by semantic similarity. Skills belonging to the requesting client and system skills are excluded.
Endpoint
POST /agent/api/skills/marketplace/search
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| query | string | Yes | Natural language search query |
| limit | number | No | Max results to return (default: 10) |
Example
curl -X POST "http://kaman.ai/agent/api/skills/marketplace/search" \
-H "Authorization: Bearer kam_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"query": "database optimization techniques",
"limit": 5
}'
Response
Returns an array of marketplace skill summaries with similarity scores. Results below 0.15 similarity are filtered out.
[
{
"skillId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "SQL Query Best Practices",
"description": "Guidelines for writing efficient and safe SQL queries",
"domains": ["engineering", "data"],
"trigger_keywords": ["sql", "query", "database"],
"content_tokens": 256,
"author": "Jane Doe",
"sourceClientId": "99",
"pluginId": null,
"similarity": 0.82
}
]
Database Schema
The kaman_skills table stores all skill records.
Columns
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
| id | uuid | No | gen_random_uuid() | Primary key |
| client_id | varchar(50) | No | -- | Owner client ID (or system for system skills) |
| name | varchar(255) | No | -- | Skill name |
| slug | varchar(255) | No | -- | URL-safe slug, auto-generated from name |
| description | text | Yes | -- | Human-readable description |
| version | integer | No | 1 | Version number |
| content | text | No | -- | The skill content (instructions/patterns) |
| content_tokens | integer | Yes | -- | Estimated token count (~4 chars per token) |
| trigger_keywords | text[] | Yes | -- | Keywords for matching |
| trigger_embedding | vector(384) | Yes | -- | Embedding for semantic search |
| task_types | text[] | Yes | -- | Applicable task types |
| domains | text[] | Yes | -- | Domain categories |
| tools | text[] | Yes | -- | Associated tool names |
| source_type | varchar(50) | No | manual | Origin: manual, auto_generated, marketplace |
| source_session_id | varchar(100) | Yes | -- | Session that generated the skill |
| parent_skill_id | uuid | Yes | -- | FK to parent skill (self-referencing) |
| usage_count | integer | No | 0 | Total invocation count |
| success_count | integer | No | 0 | Successful invocations |
| failure_count | integer | No | 0 | Failed invocations |
| status | varchar(20) | No | active | Status: active, deprecated |
| supporting_files | jsonb | No | [] | Bundled files: [{ path, filename, content, size, sha }] |
| marketplace_plugin_id | integer | Yes | -- | Linked marketplace plugin ID |
| is_marketplace | boolean | No | false | Whether installed from marketplace |
| is_published | boolean | No | false | Whether published to marketplace |
| author | varchar(255) | Yes | -- | Author name (set on publish) |
| created_at | timestamp | No | now() | Creation timestamp |
| updated_at | timestamp | No | now() | Last update timestamp |
| last_used_at | timestamp | Yes | -- | Last usage timestamp |
Unique Constraint
UNIQUE (client_id, slug, version)
Indexes
| Index | Type | Description |
|---|---|---|
| idx_kaman_skills_client | btree | Client ID lookup |
| idx_kaman_skills_embedding | hnsw (vector_cosine_ops) | Semantic vector search |
| idx_kaman_skills_status | btree (partial) | Active skills only |
| idx_kaman_skills_domains | gin | Domain array overlap queries |
| idx_kaman_skills_keywords | gin | Keyword array overlap queries |
| idx_kaman_skills_marketplace_plugin | btree (partial) | Non-null marketplace plugin IDs |
| idx_kaman_skills_published | btree (partial) | Published skills only |
Error Handling
| Status | Error | Description |
|---|---|---|
| 400 | name and content are required | Missing required fields on create |
| 400 | query is required | Missing query on search endpoints |
| 401 | Unauthorized | Missing or invalid authentication token |
| 403 | System skills cannot be modified | Attempted to update/delete/publish a system skill |
| 404 | Skill not found | Skill does not exist or is not accessible to this client |
| 409 | Skill already installed | Duplicate install attempt (same name + client) |
| 500 | Internal server error | Unexpected server-side error |
Error Response Format
{
"error": "Error message describing what went wrong"
}
Code Examples
TypeScript
const API_KEY = process.env.KAMAN_API_KEY;
const BASE_URL = 'http://kaman.ai';
// List skills with pagination
async function listSkills(limit = 50, offset = 0) {
const response = await fetch(
`${BASE_URL}/agent/api/skills?limit=${limit}&offset=${offset}`,
{
headers: { 'Authorization': `Bearer ${API_KEY}` }
}
);
return response.json(); // { skills: [...], totalCount: number }
}
// Semantic search
async function searchSkills(query: string, limit = 10) {
const response = await fetch(`${BASE_URL}/agent/api/skills/search`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ query, limit })
});
return response.json();
}
// Create a skill
async function createSkill(name: string, content: string) {
const response = await fetch(`${BASE_URL}/agent/api/skills`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name,
content,
domains: ['general'],
trigger_keywords: ['example']
})
});
return response.json();
}
// Publish to marketplace
async function publishSkill(skillId: string) {
const response = await fetch(
`${BASE_URL}/agent/api/skills/${skillId}/publish`,
{
method: 'POST',
headers: { 'Authorization': `Bearer ${API_KEY}` }
}
);
return response.json();
}
// Browse and install from marketplace
async function browseAndInstall(query: string) {
const results = await fetch(
`${BASE_URL}/agent/api/skills/marketplace/search`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ query, limit: 5 })
}
).then(r => r.json());
if (results.length > 0) {
const installed = await fetch(
`${BASE_URL}/agent/api/skills/${results[0].skillId}/install`,
{
method: 'POST',
headers: { 'Authorization': `Bearer ${API_KEY}` }
}
).then(r => r.json());
return installed;
}
}
Python
import requests
import os
class KamanSkills:
def __init__(self, base_url: str):
self.base_url = f'{base_url}/agent/api/skills'
self.headers = {
'Authorization': f'Bearer {os.getenv("KAMAN_API_KEY")}',
'Content-Type': 'application/json'
}
def list(self, limit=50, offset=0, status=None):
"""List skills with optional filters."""
params = {'limit': limit, 'offset': offset}
if status:
params['status'] = status
resp = requests.get(self.base_url, params=params, headers=self.headers)
return resp.json()
def search(self, query: str, limit=10, domains=None):
"""Semantic search for skills."""
body = {'query': query, 'limit': limit}
if domains:
body['domains'] = domains
resp = requests.post(f'{self.base_url}/search', json=body, headers=self.headers)
return resp.json()
def get(self, skill_id: str):
"""Get a skill by ID."""
resp = requests.get(f'{self.base_url}/{skill_id}', headers=self.headers)
return resp.json()
def create(self, name: str, content: str, **kwargs):
"""Create a new skill."""
body = {'name': name, 'content': content, **kwargs}
resp = requests.post(self.base_url, json=body, headers=self.headers)
return resp.json()
def update(self, skill_id: str, **kwargs):
"""Update an existing skill."""
resp = requests.put(f'{self.base_url}/{skill_id}', json=kwargs, headers=self.headers)
return resp.json()
def delete(self, skill_id: str):
"""Soft-delete a skill (sets status to deprecated)."""
resp = requests.delete(f'{self.base_url}/{skill_id}', headers=self.headers)
return resp.json()
def publish(self, skill_id: str):
"""Publish a skill to the marketplace."""
resp = requests.post(f'{self.base_url}/{skill_id}/publish', json={}, headers=self.headers)
return resp.json()
def unpublish(self, skill_id: str):
"""Unpublish a skill from the marketplace."""
resp = requests.post(f'{self.base_url}/{skill_id}/unpublish', json={}, headers=self.headers)
return resp.json()
def search_marketplace(self, query: str, limit=10):
"""Search for installable marketplace skills."""
body = {'query': query, 'limit': limit}
resp = requests.post(f'{self.base_url}/marketplace/search', json=body, headers=self.headers)
return resp.json()
def install(self, skill_id: str):
"""Install a marketplace skill."""
resp = requests.post(f'{self.base_url}/{skill_id}/install', json={}, headers=self.headers)
return resp.json()
# Example usage
client = KamanSkills('http://kaman.ai')
# Create and publish a skill
skill = client.create(
name='API Error Handling',
content='When handling API errors:\n1. Always check HTTP status codes\n2. Parse error response bodies\n3. Implement exponential backoff for retries',
domains=['engineering'],
trigger_keywords=['api', 'error', 'retry']
)
client.publish(skill['id'])
# Search and install from marketplace
marketplace_skills = client.search_marketplace('error handling patterns')
for s in marketplace_skills:
print(f"{s['name']} (similarity: {s['similarity']:.2f})")
if marketplace_skills:
installed = client.install(marketplace_skills[0]['skillId'])
Best Practices
- Write descriptive content: The skill content is injected into the agent's system message. Write clear, actionable instructions.
- Use meaningful keywords:
trigger_keywordsimprove search recall alongside vector embeddings. - Set domains for filtering: Domains allow consumers to scope skill searches to relevant areas.
- Link related tools: The
toolsarray documents which tools pair well with the skill. - Include supporting files: For complex skills, bundle schema definitions, templates, or example scripts via
supporting_files. - Track usage metrics: Monitor
usage_count,success_count, andfailure_countto identify skills that need improvement.
Next Steps
- Tools API - Execute individual functions
- Workflows API - Chain tools into workflows
- Authentication - Learn about API authentication