SPB Git

Repository Public MIT

api.md 10.3 KB 220 lines markdown

1---2title: API reference3description: REST API overview — authentication, scopes, errors, pagination, caching, idempotency, rate limits and the upload flow.4order: 315section: Reference6---78# API reference910Base URL: `https://skills.spboucher.ai/api/v1`1112The complete, machine-readable description is the OpenAPI 3.1 document, generated from the server's route table:1314[`/api/v1/openapi.json`](/api/v1/openapi.json)1516Reading needs no account. Responses are JSON unless noted.1718## Discovery1920```bash21B=https://skills.spboucher.ai/api/v122curl -s "$B/artifacts?type=skill&compatibility=claude-code&sort=updated&per_page=10"23curl -s "$B/search?q=citation+check"24curl -s "$B/categories"                      # types with live counts, licences, keywords, totals25curl -s "$B/collections"26curl -s "$B/artifacts/<ns>/<slug>"            # metadata + latest release27curl -s "$B/artifacts/<ns>/<slug>/versions"28curl -s "$B/artifacts/<ns>/<slug>/versions/1.2.0"                 # manifest, files, dependencies, compatibility, scan29curl -s "$B/artifacts/<ns>/<slug>/versions/1.2.0/files?path=SKILL.md"30curl -s "$B/artifacts/<ns>/<slug>/versions/1.2.0/install?client=claude-code"31curl -s "$B/artifacts/<ns>/<slug>/versions/1.2.0/checksum"        # text: "<sha256>  <slug>-1.2.0.tar.gz"32curl -fsSLO "$B/artifacts/<ns>/<slug>/versions/1.2.0/download"    # application/gzip33curl -s -o shot.png "$B/artifacts/<ns>/<slug>/versions/1.2.0/media/examples/shot.png"   # a screenshot listed in the manifest `examples`34curl -s "$B/artifacts/<ns>/<slug>/history"35```3637`latest` is accepted in place of a version.3839Every release object carries `examples`: the screenshots the publisher listed in the manifest, each with the `url` the registry serves it from (release detail adds `width`, `height`, `sizeBytes` and `mime`). The `/media/` route serves only those listed paths, as `image/png`, `image/jpeg` or `image/webp`, with `X-Content-Type-Options: nosniff` and `Content-Security-Policy: default-src 'none'`; any other path of the archive answers 404, and a revoked release answers 410.4041Filters on `/artifacts`: `q`, `type`, `keyword`, `compatibility`, `license`, `namespace`, `capability`, `sort` (`relevance`, `updated`, `created`, `name`, `downloads`), `page`, `per_page`.4243Search uses PostgreSQL full-text search with prefix matching, plus trigram matching as a typo fallback for queries of five characters or more. There is no semantic search.4445## Pagination4647List responses have this shape:4849```json50{ "items": [], "page": 1, "perPage": 24, "total": 0, "totalPages": 1 }51```5253`page` starts at 1. `per_page` is 1–100, default 24. Ordering is stable: every sort ends with a unique tie-breaker.5455## Authentication5657```http58Authorization: Bearer skr_<redacted>59```6061Tokens are created in the dashboard, with the CLI, or by the operator. A token carries scopes, a list of namespaces, an expiry and optionally an IP allowlist. It is shown once and cannot be retrieved later.6263Never put a token in a URL. Authenticated responses are sent with `Cache-Control: private, no-store`.6465The dashboard uses a session cookie instead; cookie-authenticated writes need a same-origin request and a CSRF token, so scripts should use bearer tokens.6667### Scopes6869| Scope | Allows |70|---|---|71| `artifacts:read` | Read artifacts in allowed namespaces, including non-public ones |72| `artifacts:create` | Create artifacts in allowed namespaces |73| `artifacts:update` | Update summary, description, links, keywords, visibility |74| `artifacts:deprecate` | Deprecate, undeprecate, archive |75| `uploads:create` | Begin uploads and send archive bytes |76| `releases:publish` | Finalize an upload into an immutable release |77| `releases:update` | Edit release notes only |78| `releases:revoke` | Revoke a release |79| `tokens:read` | List your tokens (metadata only) |80| `tokens:write` | Create or revoke tokens, never broader than the caller |81| `audit:read` | Read your audit trail |82| `admin:moderate`, `admin:settings` | Elevated; platform administrators only, tokens limited to 24 hours |8384A write also requires that you own the namespace and, for a token, that the namespace is on the token.8586## Errors8788```json89{90  "error": {91    "code": "insufficient_scope",92    "message": "This token lacks the \"artifacts:create\" scope.",93    "details": { "required": "artifacts:create" },94    "requestId": "0b9c…"95  }96}97```9899Every response carries `X-Request-Id`. Quote it when reporting a problem.100101| Status | Typical codes |102|---|---|103| 400 | `bad_request` |104| 401 | `unauthorized`, `invalid_token`, `token_expired`, `token_revoked`, `invalid_credentials` |105| 403 | `insufficient_scope`, `namespace_forbidden`, `reauth_required`, `admin_required`, `csrf` |106| 404 | `not_found` (also returned for private artifacts you cannot see) |107| 405 | `method_not_allowed` |108| 409 | `version_exists`, `artifact_exists`, `release_immutable`, `upload_state`, `conflict` |109| 410 | `release_revoked`, `taken_down`, `upload_expired` |110| 413 | `payload_too_large` |111| 415 | `unsupported_media_type`, `not_previewable` |112| 422 | `release_rejected` (with `details.findings`), `checksum_mismatch`, `idempotency_key_reused` |113| 428 | `confirmation_required` |114| 429 | `rate_limited` (with `Retry-After`) |115| 500 | `internal_error` (no internals are disclosed; use the request id) |116117## Caching and ETags118119- Anonymous JSON reads: `Cache-Control: public, max-age=30` and a weak `ETag`. Send `If-None-Match` to get `304`.120- Downloads of public releases: `Cache-Control: public, max-age=31536000, immutable`, `ETag: "sha256-<hex>"`, `X-Checksum-Sha256`, `Repr-Digest`.121- Cross-origin `GET`, `HEAD` and `OPTIONS` are allowed from any origin. Writes are not available cross-origin from browsers.122123## Rate limits124125Limits are per token, else per signed-in user, else per IP address. Responses include `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`.126127| Kind of request | Limit |128|---|---|129| Anonymous reads | 240 per minute |130| Example images (`/media/`) | 600 per minute |131| Search | 90 per minute |132| Downloads | 120 per minute |133| Authenticated calls | 120 per minute |134| Upload begin / upload bytes | 30 per hour |135| Finalize (publish) | 30 per hour |136| Abuse reports | 5 per hour |137| Sign-in attempts | 8 per 15 minutes |138139Twenty failed bearer authentications from one address within ten minutes block further bearer authentication from that address until the window ends.140141## Idempotency142143`POST /artifacts`, `POST /artifacts/{id}/releases` and `POST /uploads/{id}/finalize` accept an `Idempotency-Key` header (8–128 characters: letters, digits, `_ . : -`).144145- Repeating a request with the same key and body returns the stored response with `Idempotent-Replayed: true`.146- Reusing a key with a different body returns `422 idempotency_key_reused`.147- Finalize is also naturally idempotent: finalizing an already published upload returns the same release with status 200.148149## Publishing flow150151Three steps after the artifact exists. The archive is a deterministic `.tar.gz` (the CLI's `skills pack` produces it).152153```bash154B=https://skills.spboucher.ai/api/v1155AUTH="Authorization: Bearer $SKILLS_TOKEN"        # set in this shell only156F=citation-check-1.0.0.tar.gz157SHA=$(shasum -a 256 "$F" | cut -d' ' -f1); SIZE=$(wc -c < "$F" | tr -d ' ')158159# 0. create the artifact (once). Private by default; public needs confirmPublic.160curl -s -X POST "$B/artifacts" -H "$AUTH" -H 'Content-Type: application/json' \161  -d '{"namespace":"yourns","slug":"citation-check","type":"skill","summary":"Checks that every citation resolves to a reference.","visibility":"public","confirmPublic":true}'162ID=$(curl -s "$B/artifacts/yourns/citation-check" -H "$AUTH" | jq -r .id)163164# 1. begin: declare version, checksum and size165UP=$(curl -s -X POST "$B/artifacts/$ID/releases" -H "$AUTH" -H 'Content-Type: application/json' \166  -H "Idempotency-Key: begin-$SHA" \167  -d "{\"version\":\"1.0.0\",\"sha256\":\"$SHA\",\"sizeBytes\":$SIZE,\"releaseNotes\":\"Initial release.\"}" | jq -r .uploadId)168169# 2. send the bytes170curl -s -X PUT "$B/uploads/$UP" -H "$AUTH" -H 'Content-Type: application/gzip' --data-binary "@$F"171172# 3. finalize: validate, scan, publish173curl -s -X POST "$B/uploads/$UP/finalize" -H "$AUTH" -H 'Content-Type: application/json' -d "{\"sha256\":\"$SHA\"}"174```175176- An upload session expires after 60 minutes and belongs to the user who began it.177- The bytes must match the declared size and SHA-256 exactly, or nothing is stored (`422 checksum_mismatch`).178- Finalize returns `201` with `release.state` equal to `published` or `quarantined`, plus the list of findings. A rejected release returns `422 release_rejected` with the blocking findings; fix and upload again under the same version.179- `GET /uploads/{id}` returns the session state.180181## Changing things afterwards182183| Request | Effect |184|---|---|185| `PATCH /artifacts/{id}` | `summary`, `description`, `repository`, `homepage`, `issues`, `keywords`, `visibility` (+ `confirmPublic` when exposing). Namespace, slug and type cannot change |186| `PATCH /releases/{id}` | `releaseNotes` only. Anything else returns `409 release_immutable` |187| `POST /artifacts/{id}/deprecate` | `{ "reason": "…", "confirm": true }` |188| `POST /artifacts/{id}/undeprecate` | — |189| `POST /artifacts/{id}/archive` | `{ "confirm": true }` |190| `POST /releases/{id}/revoke` | `{ "reason": "…", "confirm": true }` — permanent |191192## Account193194| Request | Notes |195|---|---|196| `GET /me` | Identity, credential scopes and namespaces |197| `GET /me/tokens` | Metadata only |198| `POST /me/tokens` | `{ name, scopes, namespaces?, expiresInHours?, ipAllowlist? }` → the token, once |199| `DELETE /me/tokens/{id}` | Revoke |200| `POST /me/tokens/{id}/rotate` | Replacement token, once; the old one is revoked |201| `GET /me/audit` | Your actions: `before` (cursor), `limit`, `action` (prefix) |202| `POST /validate-manifest` | Checks a manifest object against the schema without uploading |203204## Reporting205206```bash207curl -s -X POST "$B/reports" -H 'Content-Type: application/json' \208  -d '{"artifact":"@ns/slug","version":"1.2.0","category":"malware","details":"At least twenty characters describing the problem.","contact":"optional@example.org"}'209```210211Categories: `malware`, `secret-leak`, `license`, `impersonation`, `spam`, `broken`, `other`. No account needed.212213## Health214215`GET /health/live` and `GET /health/ready` (database and storage).216217## SDK218219A typed TypeScript client lives in the repository (`packages/sdk`, class `RegistryClient`). It is not yet published to npm.220
Files main
Menu

Theme