REST API
Everything the skillsvault CLI does, it does over this API — and so can you. Use it to publish skills from CI, mirror your registry, fetch and verify the signed policy bundle, or stream audit events into your own systems. All endpoints are JSON in / JSON out and authenticate with an agent key.
Base URL
The base URL is your organisation's skillsvault control-plane origin (for example https://skillsvault.io). Every path below is relative to it. The :orgSlug path segment is your organisation's slug.
Authentication
Send your agent key on every request (except GET /api/bundle/key, which is public). Either header works:
X-Skillsvault-Key: skillsvault_ak_…
Authorization: Bearer skillsvault_ak_…
A key is scoped to exactly one organisation. Create or revoke keys in Settings → Agent keys; see Install & connect for the full lifecycle.
401— the key is missing or unknown.403— the key is valid but used against a different org than the one in the path.
Conventions
Requests and responses are JSON unless noted. Send Content-Type: application/json on writes. A minimal authenticated call looks like:
curl https://skillsvault.io/api/skills/acme \
-H "X-Skillsvault-Key: skillsvault_ak_live_xxx"
Error responses carry a JSON body describing the problem; see Errors for the status codes you should handle.
GET /api/skills/:orgSlug
List the organisation's approved skills and their current state — the same manifest the CLI uses to decide what to install.
Auth: agent key.
Response
{
"org": "acme",
"skills": [
{
"name": "acme/hubspot-crm-entry",
"version": "1.3.0",
"status": "active",
"compliance": "compliant",
"content_hash": "sha256:77de…"
}
]
}
status is one of active, deprecated, or banned; compliance is compliant or non_compliant.
curl https://skillsvault.io/api/skills/acme \
-H "X-Skillsvault-Key: skillsvault_ak_live_xxx"
Status: 200 · 401 · 403.
GET /api/skills/:orgSlug/content
Fetch the full content of one skill's current version — its SKILL.md and every bundled file.
Auth: agent key.
Query
| Param | Required | Description |
|---|---|---|
skill | yes | The namespaced skill name, e.g. acme/hubspot-crm-entry. |
Response
{
"name": "acme/hubspot-crm-entry",
"version": "1.3.0",
"status": "active",
"skill_md": "---\nname: hubspot-crm-entry\n…",
"files": {
"scripts/run.py": "…",
"references/fields.md": "…"
}
}
files maps each relative path to its text content.
curl "https://skillsvault.io/api/skills/acme/content?skill=acme/hubspot-crm-entry" \
-H "X-Skillsvault-Key: skillsvault_ak_live_xxx"
Status: 200 · 400 (missing ?skill) · 401 · 403 · 404 (skill not found).
POST /api/skills/:orgSlug/publish
Publish a skill folder as a new immutable version — the API behind skillsvault publish. See Authoring & publishing and the manifest reference.
Auth: agent key.
Body
| Field | Required | Description |
|---|---|---|
name | yes | Namespaced namespace/skill, lowercase. |
version | yes | Semantic version, e.g. 1.4.0. |
skill_md | yes | The full SKILL.md text. |
description | no | Discovery description (mirrors the manifest / frontmatter). |
intent | no | Machine intent (kebab-case). |
targets | no | Array of systems/processes the skill touches. |
files | no | Map of relative path → file content for bundled scripts/, references/, assets/. |
manifest | no | The parsed skillsvault.json object. |
{
"name": "acme/hubspot-crm-entry",
"version": "1.4.0",
"description": "Create and update HubSpot contacts from a deal brief.",
"intent": "crm-data-entry",
"targets": ["hubspot"],
"skill_md": "---\nname: hubspot-crm-entry\n…",
"files": { "scripts/run.py": "…" },
"manifest": { "skillsvault_schema": 1, "name": "acme/hubspot-crm-entry", "…": "…" }
}
Response
{
"ok": true,
"skill": "acme/hubspot-crm-entry",
"version": "1.4.0",
"content_hash": "sha256:9a1b…"
}
curl -X POST https://skillsvault.io/api/skills/acme/publish \
-H "X-Skillsvault-Key: skillsvault_ak_live_xxx" \
-H "Content-Type: application/json" \
-d @publish.json
Status: 200 · 400 (bad JSON) · 401 · 403 · 422 (missing required field or invalid name).
GET /api/bundle/:orgSlug
Fetch the signed policy bundle — the snapshot of approved skills and active policies that the gate evaluates locally.
Auth: agent key.
Response — a signed envelope:
{
"alg": "Ed25519",
"payload": "{\"bundle_version\":1750848000, …}",
"signature": "base64…"
}
payload is a JSON string: verify signature over its exact bytes using the org's public key, then parse it. The decoded payload contains:
| Field | Meaning |
|---|---|
bundle_version | Monotonic version of this bundle. |
defaults | Fallback actions (unmanaged, managed_missing_manifest) and staleness settings. |
namespaces | The managed namespaces. |
manifest_index | Map of skill → current_version, content_hash, status, targets. |
policies | Ordered list of policy_id, name, matcher_type, match_value, action, reason. |
curl https://skillsvault.io/api/bundle/acme \
-H "X-Skillsvault-Key: skillsvault_ak_live_xxx"
Status: 200 · 401 · 403 · 404 (org not found).
GET /api/bundle/key
Return the organisation's Ed25519 public signing key, PEM-encoded, as text/plain. Use it to verify the signature on a bundle from GET /api/bundle/:orgSlug. This endpoint is public — no agent key required.
curl https://skillsvault.io/api/bundle/key
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA…
-----END PUBLIC KEY-----
Status: 200.
POST /api/audit/ingest
Append one or more audit events — the API the gate uses to record every decision. See Audit log.
Auth: agent key.
Body
{
"events": [
{
"skill_name": "acme/teamleader-crm-entry",
"skill_version": "2.0.0",
"harness": "claude_code",
"machine_name": "lab-mbp",
"decision": "blocked",
"policy_id": "pol_7731",
"policy_name": "Teamleader retired",
"reason": "Use acme/hubspot-crm-entry instead.",
"occurred_at": "2026-06-25T10:03:12Z"
}
]
}
decision is one of allowed, warned, or blocked. occurred_at, policy_id, policy_name, and reason are optional.
Response
{ "received": 1 }
curl -X POST https://skillsvault.io/api/audit/ingest \
-H "X-Skillsvault-Key: skillsvault_ak_live_xxx" \
-H "Content-Type: application/json" \
-d @events.json
Status: 200 · 400 (bad JSON) · 401.
POST /api/devices/heartbeat
Report a machine's installed skill inventory and CLI version, and learn whether the CLI is out of date. Sent automatically by the CLI on connect and after skillsvault pull.
Auth: agent key.
Body
{
"harness": "claude_code",
"machine_name": "lab-mbp",
"plugin_version": "0.1.0",
"installed": [
{ "skill": "acme/hubspot-crm-entry", "version": "1.3.0", "status": "active" }
]
}
All fields are optional and default sensibly (harness → claude_code, machine_name → unknown, installed → []).
Response
{
"ok": true,
"installed": 1,
"outdated": false,
"latest_version": "0.1.0",
"release_url": null
}
Status: 200 · 400 (bad JSON) · 401.
CLI login (device flow)
Connecting a machine with skillsvault login uses a browser device-authorization flow: the CLI calls POST /api/cli/device/start to obtain a device_code and a short user_code, opens the verification page, then polls POST /api/cli/device/poll with the device_code until you approve it in the browser — at which point it receives a real agent key. Integrators normally just run skillsvault login (or skillsvault configure with a key) rather than calling these endpoints directly.
Errors
| Status | Meaning |
|---|---|
400 | Malformed request (bad JSON, or a missing required query parameter). |
401 | Missing or unknown agent key. |
403 | Valid key used against a different organisation than the path. |
404 | Org, skill, or resource not found. |
422 | Validation failed — a required field is missing or a name is malformed. |