Skip to content

Agents, skills & teams API

CRUD for agents, skills, and teams stored in Kumiho, with avatars and kref semantics.

This page documents the gateway endpoints that manage your agent roster, skill library, and teams — the building blocks the dashboard’s Agents, Skills, and Teams pages create and edit. All three are persisted in Kumiho graph memory, so a definition created through the API survives daemon restarts and is referenceable from workflow YAML.

Use these endpoints when you want to script the registry instead of clicking through the dashboard: bulk-import agents, sync skills from your own source, or wire team topologies into a deployment pipeline. For the dashboard equivalents, see Agents, teams & canvas and Skills, tools & integrations pages. For the conceptual model behind agents, teams, and swarms, see Agents, teams & swarms.

Every route on this page requires the pairing bearer token:

Authorization: Bearer <token>

Obtain the token through the pairing flow described in Pairing & authentication. A missing or invalid token returns 401. Requests share the gateway’s default 64 KiB JSON body cap and request timeout, except the avatar upload routes, which are documented separately below.

Agents, skills, and teams are not stored in config.toml. They live in Kumiho as items, revisions, and (for teams) bundles. Two Kumiho projects are used, both configurable under [kumiho]:

ResourceKumiho project (config key)Default projectSpace
Agentskumiho.harness_projectRevkaAgentPool
Teamskumiho.harness_projectRevkaTeams
Skillskumiho.memory_projectCognitiveMemorySkills

The full space path is /{project}/{space} — for example /Revka/AgentPool or /CognitiveMemory/Skills. The gateway creates the project and space lazily on first write, so you do not need to provision them in advance.

For each resource:

  • The item (or bundle, for teams) is the stable identity. Its name is a slug.
  • The latest published revision holds the editable metadata (identity, description, member counts, and so on).
  • An artifact holds heavier payloads — avatar image bytes, or a skill’s full markdown — referenced by a file:// location on disk.

Each write creates a new revision and tags it published, so history is preserved and never overwritten in place. See Graph model: spaces, items & provenance for the underlying model.

A kref (kref://…) is the canonical pointer to a Kumiho entity. Items use this shape:

kref://<Project>/<Space>/<slug>.<suffix>

Examples:

kref://Revka/AgentPool/senior-rust-engineer.agent
kref://CognitiveMemory/Skills/debugging-skills.skill
kref://Revka/Teams/platform-team

The slug is derived from the supplied name by lowercasing, replacing every non-alphanumeric character with a hyphen, and collapsing repeats — so "Senior Rust Engineer" becomes senior-rust-engineer. This slug is exactly the value workflow YAML expects in assign: (agents) and team: (teams), which is why responses return it separately as item_name, distinct from the human-readable name.

Revision krefs add selectors, e.g. kref://Revka/AgentPool/senior-rust-engineer.agent?r=3&a=SKILL.md. When you pass a kref in a URL path, the agent and team routes strip and re-add the kref:// prefix for you, so both kref://Revka/... and Revka/... work in the {*kref} path segment. The skill detail, update, and delete routes do not perform this normalization — for those routes, omit the kref:// prefix and pass the path segment directly (e.g. CognitiveMemory/Skills/debugging-skills.skill).

Agents are Kumiho items of kind agent in the AgentPool space. Each carries identity, soul, expertise, tone, role, type, an optional model override, and an optional system hint.

GET /api/agents
POST /api/agents
PUT /api/agents/{*kref}
DELETE /api/agents/{*kref}
POST /api/agents/deprecate
POST /api/agents/avatar
GET /api/agents?include_deprecated=false&q=rust&page=1&per_page=9
Authorization: Bearer <token>
Query paramTypeDefaultMeaning
include_deprecatedboolfalseInclude disabled agents in the result.
qstringFull-text search. When present, uses Kumiho search instead of a plain list.
pageint11-based page number.
per_pageint9Items per page. Clamped to the range 1–50.

Response:

{
"agents": [
{
"kref": "kref://Revka/AgentPool/senior-rust-engineer.agent",
"name": "Senior Rust Engineer",
"item_name": "senior-rust-engineer",
"kind": "agent",
"deprecated": false,
"created_at": "2026-06-18T00:00:00Z",
"identity": "...",
"soul": "...",
"expertise": ["Rust", "async"],
"tone": "direct",
"role": "engineer",
"agent_type": "specialist",
"model": "claude-opus-4",
"system_hint": "...",
"revision": 3,
"revision_number": 3,
"avatar_url": "/workspace/...?sig=...&expires=...",
"avatar_artifact_name": "profile-avatar",
"avatar_filename": "headshot.png",
"avatar_mime": "image/png"
}
],
"total_count": 1,
"page": 1,
"per_page": 9
}

Non-search list responses are served from a 3-second in-process cache to absorb rapid dashboard polling. The cache is invalidated on every write.

POST /api/agents
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "Senior Rust Engineer",
"identity": "A pragmatic systems engineer.",
"soul": "Calm, precise, allergic to overengineering.",
"expertise": ["Rust", "async", "tokio"],
"tone": "direct",
"role": "engineer",
"agent_type": "specialist",
"model": "claude-opus-4",
"system_hint": "Prefer the smallest correct change."
}

name, identity, and soul are required; the rest are optional. On success the endpoint returns 201 Created with { "agent": { ... } }. It slugifies name into the item identifier, writes a revision holding the metadata, and tags that revision published.

PUT /api/agents/kref://Revka/AgentPool/senior-rust-engineer.agent
Authorization: Bearer <token>
Content-Type: application/json

The body is the same shape as create. An update writes a new revision (it does not edit the old one) and re-tags it published. Any existing avatar metadata is preserved across the update, so editing fields does not drop the agent’s avatar.

POST /api/agents/deprecate
Authorization: Bearer <token>
Content-Type: application/json
{ "kref": "kref://Revka/AgentPool/senior-rust-engineer.agent", "deprecated": true }

Deprecation is a soft toggle. Deprecated agents are hidden from the default list but reappear when you pass include_deprecated=true. Set "deprecated": false to re-enable.

DELETE /api/agents/kref://Revka/AgentPool/senior-rust-engineer.agent
Authorization: Bearer <token>

Returns 204 No Content. This permanently removes the item; prefer deprecation when you only want to disable an agent.

Skills are Kumiho items of kind skill in the Skills space. A skill is a markdown document plus lightweight metadata. To keep list responses small, the gateway splits storage in two:

  • Revision metadata holds only description, domain, tags, and created_by.
  • The full markdown is written to ~/.revka/workspace/skills/<slug>.md and referenced by a SKILL.md artifact whose location is a file:// URI. Content is read from disk on demand for detail views and edits.
GET /api/skills
POST /api/skills
GET /api/skills/{*kref}
PUT /api/skills/{*kref}
DELETE /api/skills/{*kref}
POST /api/skills/deprecate
GET /api/skills?include_deprecated=false&q=debug&page=1&per_page=9
Authorization: Bearer <token>

Query params match the agents list (include_deprecated, q, page, per_page; per_page clamped to 1–50). List responses omit full content — the content field is empty (or a 200-character preview for legacy skills that stored content inline) to keep payloads under Kumiho’s gRPC limit. Use the detail endpoint to read the full markdown. Search resolves both the current skill kind and the legacy skilldef kind. Non-search results are cached for 30 seconds.

{
"skills": [
{
"kref": "kref://CognitiveMemory/Skills/debugging-skills.skill",
"name": "debugging-skills",
"item_name": "debugging-skills",
"deprecated": false,
"created_at": "2026-06-18T00:00:00Z",
"description": "Systematic debugging workflow",
"content": "",
"domain": "engineering",
"tags": ["debug"],
"revision_number": 2
}
],
"total_count": 1,
"page": 1,
"per_page": 9
}
GET /api/skills/kref://CognitiveMemory/Skills/debugging-skills.skill
Authorization: Bearer <token>

Returns { "skill": { ... } } with the content field populated. Content is resolved in priority order: the local file via the artifact location, then artifact metadata, then revision metadata (the latter two cover ClawHub and very old installs).

POST /api/skills
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "Debugging skills",
"description": "Systematic debugging workflow",
"content": "# Debugging\n\nReproduce, minimise, hypothesise...",
"domain": "engineering",
"tags": ["debug"]
}

name, description, content, and domain are required; tags is optional. The create flow slugifies name, writes the revision metadata, stores the markdown to disk as the SKILL.md artifact, then tags the revision published. Returns 201 Created.

PUT /api/skills/kref://CognitiveMemory/Skills/debugging-skills.skill
Authorization: Bearer <token>
Content-Type: application/json

Same body as create. The update writes a new revision, rewrites the markdown file, attaches a fresh SKILL.md artifact, and re-publishes.

POST /api/skills/deprecate Body: { "kref": "...", "deprecated": true }
DELETE /api/skills/{*kref} Returns 204 No Content

Deprecation behaves like agents — a soft toggle controlling default-list visibility. Delete is permanent.

Teams are Kumiho bundles in the Teams space. A bundle groups agent members; directed edges between members express the delegation topology. Edge types are REPORTS_TO, SUPPORTS, and DEPENDS_ON. A team can be referenced from workflow YAML’s team: field.

GET /api/teams
POST /api/teams
GET /api/teams/{*kref}
PUT /api/teams/{*kref}
DELETE /api/teams/{*kref}
POST /api/teams/deprecate
POST /api/teams/avatar
GET /api/teams?include_deprecated=false&page=1&per_page=9
Authorization: Bearer <token>
Query paramTypeDefaultMeaning
include_deprecatedboolfalseInclude disabled teams.
pageint11-based page number.
per_pageint9Items per page. Clamped to 1–50.

The list endpoint returns summary fields only (member_count, member_names, edge_count) from bundle metadata — it does not enrich each team with full member detail. Fetch a single team to get the resolved member and edge lists. Results are cached for 30 seconds.

GET /api/teams/kref://Revka/Teams/platform-team
Authorization: Bearer <token>

Returns the full team with members and edges resolved:

{
"team": {
"kref": "kref://Revka/Teams/platform-team",
"name": "Platform Team",
"description": "Owns the runtime and deploy pipeline.",
"deprecated": false,
"created_at": "2026-06-18T00:00:00Z",
"members": [
{
"kref": "kref://Revka/AgentPool/senior-rust-engineer.agent",
"name": "senior-rust-engineer",
"role": "engineer",
"agent_type": "specialist",
"identity": "...",
"expertise": ["Rust"],
"avatar_url": "/workspace/...?sig=..."
}
],
"edges": [
{
"from_kref": "kref://Revka/AgentPool/junior.agent",
"to_kref": "kref://Revka/AgentPool/senior-rust-engineer.agent",
"edge_type": "REPORTS_TO"
}
],
"avatar_url": null,
"avatar_artifact_name": null,
"avatar_filename": null,
"avatar_mime": null
}
}
POST /api/teams
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "Platform Team",
"description": "Owns the runtime and deploy pipeline.",
"members": [
"kref://Revka/AgentPool/senior-rust-engineer.agent",
"kref://Revka/AgentPool/junior.agent"
],
"edges": [
{
"from_kref": "kref://Revka/AgentPool/junior.agent",
"to_kref": "kref://Revka/AgentPool/senior-rust-engineer.agent",
"edge_type": "REPORTS_TO"
}
]
}

name is required; description, members (agent krefs), and edges are optional. The gateway creates the bundle, adds each member, and creates the edges. On success it returns 201 Created with the fully built team.

Before persisting, the gateway validates the team graph and rejects an invalid one with 400 and a validation_errors array. The checks are:

CodeTriggered when
self_edgeAn edge has the same from_kref and to_kref.
dangling_refAn edge endpoint is not in the members list.
reciprocal_dependsTwo DEPENDS_ON edges point both ways between the same pair.
cycleThe execution edges (DEPENDS_ON, SUPPORTS, FEEDS_INTO) form a dependency cycle.
{
"error": "Team graph is invalid",
"validation_errors": [
{ "code": "cycle", "message": "Dependency cycle detected among 2 member(s). ..." }
]
}
PUT /api/teams/kref://Revka/Teams/platform-team
Authorization: Bearer <token>
Content-Type: application/json

Same body as create. The update re-validates the graph, writes a new metadata revision, then reconciles state: it adds members that are newly listed, removes members that are gone, deletes all existing edges between members, and recreates the edges from the request. Existing avatar metadata is preserved.

POST /api/teams/deprecate Body: { "kref": "...", "deprecated": true }
DELETE /api/teams/{*kref} Returns 204 No Content

deprecate returns { "success": true }. Delete first removes the edges between members and the bundle membership, then deletes the bundle itself.

Agents and teams support an avatar image. Skills do not.

POST /api/agents/avatar
POST /api/teams/avatar
Authorization: Bearer <token>
Content-Type: multipart/form-data

Multipart fields:

FieldRequiredMeaning
krefyesThe agent item kref or team bundle kref.
fileyesThe image bytes.

Constraints:

  • Max size: 5 MiB per file. Larger uploads return 413 Payload Too Large.
  • Allowed formats: PNG, JPEG, and WebP, detected by content (magic bytes), not just the declared MIME type. SVG is explicitly rejected. An unsupported type returns 415 Unsupported Media Type.
  • These two routes run on a dedicated router with a 60-second timeout, separate from the gateway’s default JSON body cap.

On success the endpoint returns the updated agent or team object. The image bytes are written under the workspace directory (artifacts/<project>/<space>/<slug>/avatars/<uuid>.<ext>), recorded as a Kumiho artifact named profile-avatar, and the file path is stored in revision metadata. The returned avatar_url is an HMAC-signed, time-limited URL served from the workspace asset endpoint — agent avatar URLs are signed for 24 hours — so browsers can render the image without exposing arbitrary filesystem access. See TLS, rate limiting, WebAuthn & static serving for how signed workspace URLs work.

Example with curl:

Terminal window
curl -X POST https://<gateway>/api/agents/avatar \
-H "Authorization: Bearer $TOKEN" \
-F "kref=kref://Revka/AgentPool/senior-rust-engineer.agent" \

These routes share the gateway’s central Kumiho error mapping. A 404 from Kumiho on a list call is treated as “space not yet created” — the gateway provisions the project and space and returns an empty list rather than an error. Other upstream failures (including 5xx) are surfaced as a consistent gateway error shape, typically 503 with a kumiho_upstream_unavailable reason, so the dashboard renders one predictable error state.