docs · api reference
Registry HTTP API
Stable HTTP surface at https://registry.pakx.dev. JSON everywhere; bearer tokens for write paths; session cookies for dashboard CRUD. Every response carries RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset headers (and Retry-After on 429s).
Authentication
Two flavours:
- Bearer token for CLI workflows. Issue from https://registry.pakx.dev/dashboard/tokens. Pass as
Authorization: Bearer pakx_v1_…. Plaintext shown once; we store a sha256 hash. - Session cookie for dashboard routes. Set by Auth.js after GitHub OAuth. Strictly necessary; never used for tracking.
Error shape
Every non-2xx body is JSON: { "error": "<kebab-case>" }. Common codes: unauthorized, forbidden, not-found, invalid, too-many-requests, user-deleted.
Endpoints
GET/api/v1/health
publicLiveness + DB readiness probe. 200 if process up AND DB reachable, 503 otherwise. Used by /status on pakx.dev and external uptime checkers.
rate: 120 burst / 2 req per sec per IP
Response body
{
"status": "ok" | "degraded",
"uptimeSec": 4203,
"version": "abc1234",
"checks": {
"db": { "ok": true, "latencyMs": 12 }
}
}GET/api/v1/packages?q=<query>&limit=<n>
publicSearch the registry. Empty `q` returns the first page sorted by recency.
rate: 60 burst / 1 req per sec per IP
Response body
{
"packages": [
{
"id": "owner/name",
"kind": "skill" | "mcp" | "subagent" | ...,
"description": "...",
"latestVersion": "1.2.3"
}
]
}GET/api/v1/packages/{owner}/{name}
publicFull package metadata and every non-deprecated version (newest first).
rate: 120 burst / 2 req per sec per IP
Response body
{
"id": "owner/name",
"kind": "skill",
"description": "...",
"createdAt": "2026-05-21T...",
"versions": [
{ "version": "1.2.3", "sha256": "...", "sizeBytes": 4096, "publishedAt": "..." }
]
}POST/api/v1/packages
bearer tokenRegister (or look up) a package. Step 1 of the publish flow; tarball upload follows on the version route.
rate: 20 burst / 0.1 req per sec per authed user
Request body
{
"name": "kebab-or-snake-or-dot-case",
"kind": "skill" | "mcp" | "subagent" | "prompt" | "command" | "hook",
"description": "(optional, max 1024 chars)"
}Response body
{
"id": "<nanoid>",
"owner": "<github-login>",
"name": "...",
"kind": "...",
"created": true | false
}PUT/api/v1/packages/{owner}/{name}/{version}
bearer tokenUpload the version tarball. Server validates Content-Length, content type, sha256, then stores in Vercel Blob and inserts a `versions` row.
Request body
gzipped tar (application/gzip)Response body
{
"id": "<version-nanoid>",
"version": "1.2.3",
"sha256": "...",
"sizeBytes": 1234,
"tarballKey": "..."
}DELETE/api/v1/packages/{owner}/{name}/{version}
bearer tokenSoft-delete a version. Sets `deprecated_at`; the row stays so that `pakx install` pins on existing manifests keep resolving. Hard delete only via direct DB intervention.
Response body
{ "deprecated": true }GET/api/v1/whoami
bearer tokenIdentify the user owning the bearer token. The CLI hits this on `pakx login` and `pakx whoami`.
rate: 30 burst / 0.5 req per sec per IP (pre-auth); 120 burst / 2 req per sec per user (post-auth)
Response body
{
"id": "<user-nanoid>",
"login": "<github-login>",
"email": "user@example.com"
}GET/api/v1/me/export
bearer tokenGDPR-style data export. JSON download of every row tied to the caller — user row, hashed tokens, owned packages with versions, Stripe pointer. Plaintext tokens are NOT returned (we don't keep them). Tarball contents are NOT inlined; metadata + blob key only.
rate: 5 burst / 1 req per 30 sec per user
Response body
{
"schema": "https://registry.pakx.dev/schemas/me-export-v1.json",
"exportedAt": "2026-05-21T...",
"user": { "id": "...", "githubLogin": "...", "email": "...", ... },
"tokens": [{ "id": "...", "label": "...", "hashedKey": "<sha256>", ... }],
"packages": [{ "id": "...", "kind": "...", "versions": [{ "version": "...", "sha256": "..." }] }],
"stripe": { "stripeAccountId": "..." } | null
}POST/api/v1/me/delete
bearer tokenRight-to-erasure request. Revokes every active token immediately (including the one used for this request) and writes a deletion marker. Hard delete happens after a 30-day grace window — email security@pakx.dev to undo within that window.
rate: 3 burst / 3 req per day per user
Request body
{ "confirm": "<your-github-login>" }Response body
{
"requested": true,
"user": "<github-login>",
"tokensRevoked": true,
"graceDays": 30,
"scheduledFor": "2026-06-20T...",
"hint": "Contact security@pakx.dev within the grace window to undo."
}GET/api/v1/tokens
session cookieList API tokens issued by the signed-in user. Hashes only — plaintext is shown once at issue and never persisted.
Response body
{
"tokens": [
{ "id": "...", "label": "laptop", "lastUsedAt": "...", "createdAt": "..." }
]
}POST/api/v1/tokens
session cookieIssue a new API token. The response body is the only time the plaintext key is returned.
Request body
{ "label": "laptop" }Response body
{
"id": "...",
"label": "laptop",
"key": "pakx_v1_<base62>",
"createdAt": "..."
}DELETE/api/v1/tokens/{id}
session cookieRevoke an API token. Subsequent requests presenting the plaintext key fail with 401.
Response body
{ "revoked": true }See also: the federated reader covering MCP Registry + Smithery + this registry lives in crates/pakx-registry-client; the equivalent TS port that powers /explore lives at src/lib/registry.ts. Both stay in sync with the API documented above.