Skip to main content
Field names below are taken directly from the backend’s own response models (api/schemas.py) — not reconstructed from the design doc — so they match what actually comes back on the wire. Example values (ids, digests, tenant ids) are from a real local run, not placeholders.

Endpoint index

GET /api/v1/skills Query params: query, runtime, versionConstraint, tags (csv), category, page, pageSize — all optional. No query returns everything the caller can see. Matches on indexed tokens (id segments, name words, tags, category, description words), not arbitrary substrings — see the ranking model below. Searching an id’s own hyphenated suffix (personal-notes) won’t match if that exact string isn’t itself a tag or name token; searching a real tag (notes) will.
Every field on items[] and page above is exhaustive — that’s the complete SearchResultItem / PageMeta shape, nothing trimmed.

Ranking model

Skill metadata

GET /api/v1/skills/{id}/versions/{version}
The 404 is deliberately identical whether the id doesn’t exist at all, or exists but is private and the caller isn’t its owner or a share grantee — see Corner cases. guardrailCertifiedLevel/guardrailLevelStatuses are a point-in-time attestation computed once at publish — null/[] means “not available for this version” (published before certification existed, or the guardrails service was unreachable at publish time), never “failed,” and it is never recomputed later even if the tenant’s guardrail policy changes afterward.

Create, validate, and publish a draft

POST /api/v1/drafts → DraftResponse
POST /api/v1/drafts/{draftId}/validate → ValidationResultResponse — no body required.
certification is a projection — what this draft would be certified as if published right now. It’s null whenever nothing was actually scanned (invalid/blocked) or the guardrails service wasn’t reachable. POST /api/v1/drafts/{draftId}/publish → DraftPublishResponse
The 409 example above is a real error message, not a paraphrase — captured live in the publishing walkthrough when re-publishing an id+version that already existed. visibility defaults to "private" when omitted — a caller has to opt in to "public" explicitly; nothing is ever published world-visible by accident.

Share a skill

POST /api/v1/skills/{id}/shares → ShareGrantResponse. Requires the skills:share scope and being either the skill’s owner or an admin of its owning tenant.
permission defaults to "read" when omitted. Revoking (DELETE /api/v1/skills/{id}/shares/{grantId}) takes effect on the very next request — grants are checked per-request, never cached.

Artifact download

POST /api/v1/skills/{id}/versions/{version}/artifact-token → ArtifactTokenResponse, then GET /api/v1/artifacts/{token} redeems it.
Response — artifact-token
The token is reusable until expiresAt, not single-use — the same posture as a presigned S3/OCI URL, which is exactly what this stands in for on the local filesystem backend (see Architecture).

Tenant guardrail policy

GET /api/v1/tenants/{tenantId}/guardrail-policy / PUT (same shape) → TenantGuardrailPolicyResponse. Readable by any tenant member, writable only by a tenant admin.
Response
Only lists which configurable (Level 2–4) checks are on — Level 1 mandatory checks are never in this list because they can’t be turned off; see the level model.

Auth

POST /api/v1/auth/login (dev-login path) → AuthResponse
POST /api/v1/auth/google takes { "idToken": "...", "tenantId": null } and returns the same AuthResponse shape. POST /api/v1/auth/refresh takes { "refreshToken": "...", "tenantId": null } and returns an access token + tenant list without a new refresh token.

Guardrails service endpoints

Separate service, separate base URL (:8028 locally) — see Architecture.
GET /healthz — real response