curl https://acme.skills.example/v1/healthz

API reference

Tenant-scoped REST. Bearer auth on every /v1/* except /v1/healthz. Tenant resolution by subdomain or X-Skill-Pool-Tenant header. Mutations write an audit_events row in the same transaction.

Common

Auth. Authorization: Bearer <token> issued for the resolved tenant.

Tenant resolution. Either subdomain (acme.skill-pool.example.com) or X-Skill-Pool-Tenant: acme header.

Error shape.

{
  "error": "tenant_resolution_failed",
  "message": "missing Host header"
}

Codes: not_found · unauthorized · forbidden · bad_request · tenant_resolution_failed · not_implemented · internal_error.

Health

GET /v1/healthz — no auth, no tenant. Always returns 200 so the load balancer doesn't pull a node on a transient blip. Page on deps.db.status == "down" from your monitoring.

{
  "status": "ok",                  // "ok" | "degraded"
  "version": "0.1.0",
  "deps": {
    "db":       { "status": "up",   "latency_ms": 3  },
    "storage":  { "status": "up",   "latency_ms": 12 },
    "embedder": { "status": "off",  "note": "fastembed feature not compiled" }
  }
}

Skills · Agents · Commands

One catalog table holds all three, discriminated by ?kind=skill|agent|command (default skill). Schema, validation, dependency graph, embedding column, decay model — all shared.

MethodPathNotes
GET/v1/skillsList · supports query · tags · limit · kind · semantic · min_similarity
POST/v1/skillsPublish · multipart · tar+gz body + JSON metadata · secret scan on server
GET/v1/skills/{slug}Metadata
GET/v1/skills/{slug}/bundle.tar.gz302 → signed S3 URL (or stream from fs/MinIO)
GET/v1/skills/{slug}/skill-mdThe raw SKILL.md text
GET/v1/skills/{slug}/detailPortal-shaped detail (version history, usage)
DELETE/v1/skills/{slug}/versions/{version}Archive (soft-delete; partial unique index allows republish)

Semantic search (Phase 5+). ?semantic=axum middleware ranks by cosine similarity of the description embedding. Returns 400 if no embedder is compiled in.

GET /v1/skills?semantic=axum+middleware&min_similarity=0.7

[
  {
    "slug": "axum-handler",
    "version": "1.0.0",
    "description": "Pattern for axum tenant-scoped extractors",
    "tags": ["rust"],
    "status": "published",
    "created_at": "2026-05-18T12:00:00Z",
    "similarity": 0.94
  }
]

Plugins (Layer 3)

MethodPathNotes
GET/v1/pluginsList tenant's plugins
POST/v1/pluginsPublish · resolves nested {slug,version} entries · 409 on duplicate version
GET/v1/plugins/{slug}Detail · sourcing mode · refresh state
POST/v1/plugins/{slug}/refreshMirror-mode only · trigger immediate pull
DELETE/v1/plugins/{slug}/versions/{version}Archive

Projects · Plans (Layer 2)

A project binds a repo (git remote URL) to a curated set of skills. A plan is a curator-authored, versioned bundle that a project can follow.

MethodPathNotes
GET/v1/projectsList · auto-discover by git remote
POST/v1/projectsCreate
GET/v1/projects/{slug}/planCurrent plan resolved for the project
POST/v1/plansPublish a plan version
GET/v1/plans/{slug}/historyImmutable version log

Drafts (retrospective capture)

MethodPathNotes
POST/v1/draftsCapturer daemon posts a Sonnet-drafted SKILL.md + frontmatter
GET/v1/draftsCurator inbox (paginated)
POST/v1/drafts/{id}/approvePromote draft to a new skill version
POST/v1/drafts/{id}/rejectDiscard with reason

Decay (lifecycle)

GET /v1/tenant/skills/decay — admin-only. Lists candidates matching last_used > 6mo ∧ uses < 3. The web portal's "graveyard" view uses this.

Tenants · Tokens

MethodPathNotes
POST/v1/tenant/tokensMint a scoped token · read · publish · admin
GET/v1/tenant/membersList · paged · filterable by role
POST/v1/tenant/ssoConfigure OIDC / SAML 2.0 · per-tenant
GET/v1/tenant/themeLogo · palette · font · favicon · custom domain
POST/scim/v2/Users · /GroupsSCIM v2 user provisioning

Audit

GET /v1/tenant/audit — paginated, filterable by actor, action, resource, time range.

GET /v1/tenant/audit/export.jsonl — signed JSONL export for SIEM ingestion. Every mutation in the system is in here.

Public read surfaces (no auth)

These are the only routes Claude Code's installer touches. Tenant routing is by Host header / subdomain / custom domain.

PathConsumerNotes
/.claude-plugin/marketplace.json/plugin marketplace add <url>Generated server-side from the tenant's plugin rows
/git/plugins/<slug>.git/*/plugin install <name>Smart-HTTP git endpoint backed by stored bytes

MCP transport

Claude can call search_skills and install_skill inline over JSON-RPC at /mcp. Authenticates with the same Bearer token. Useful when you want Claude to manage its own toolbox without shelling out to the CLI.

$ full docs/api.md → server/src/routes/