# REST API endpoint reference

> Markdown variant of <https://www.skillzdrive.com/docs/rest-api/endpoints>.

Every v1 REST route, grouped by resource. All paths are relative to
`https://www.skillzdrive.com`. See the
[REST API overview](https://www.skillzdrive.com/docs/rest-api.md) for
authentication, response shape, and HTTP status codes.

Auth tags below:

- **Auth required** — must present a `Bearer sk_live_…` token.
- **Anonymous ✓** — works without a token (rate limited).
- **Internal only** — service-to-service; not part of the public surface.

## Skills

Search and discover skills from the public marketplace or the
caller's scoped catalog.

### `GET /api/v1/skills` — Anonymous ✓

List or search skills. With `?q=`, full-text searches
name/description/slug. Without, lists everything accessible to the
caller.

Query params:

- `q` — search query (required for anonymous).
- `limit` — max results (default 20, cap 50).
- `collection` — filter to a named collection (auth-only).

```bash
curl "https://www.skillzdrive.com/api/v1/skills?q=pdf&limit=5" \
     -H "X-Skillzdrive-Install-Id: $(uuidgen)"
```

### `GET /api/v1/skills/:slug` — Anonymous ✓

One skill by slug. Response includes `download` with the latest
content-addressed artifact URL when the skill has one (populated
after publish + backfill).

```jsonc
{
  "data": {
    "skill": {
      "id": "...",
      "slug": "pdf",
      "name": "PDF tools",
      "description": "...",
      "tags": ["document", "pdf"],
      "version": 3,
      "github_url": null,
      "category": "public"
    },
    "download": {
      "content_sha": "abc123...",
      "content_size": 45213,
      "version": 3,
      "url": "/api/v1/downloads/public/pdf/abc123....zip"
    }
  }
}
```

### `GET /api/v1/skills/discover` — Auth required

Broad discovery across marketplace + cached GitHub sources.
Paginated (10 results per page).

- `keywords` — required.
- `offset` — for pagination.

### `GET /api/v1/skills/leaderboard` — Auth required

Top public skills.

- `sort_by` — `executions` (default), `installs`, or `rating`.

## Drive

Manage the authenticated user's hosted drive — the set of skills
available to their API keys.

### `GET /api/v1/drive` — Auth required

List all accessible skills (drive ∪ team ∪ shared, filtered by the
key's `allowed_skill_ids`).

### `POST /api/v1/drive/import` — Auth required

Import a marketplace skill or GitHub URL into the drive.

```json
{ "skillSlug": "pdf" }
```

Accepts either a marketplace slug or a GitHub URL
(`https://github.com/owner/repo/tree/main/subpath`).

### `DELETE /api/v1/drive/:slug` — Auth required

Remove a skill from the drive.

### `POST /api/v1/drive/:slug/toggle` — Auth required

Flip the enabled state without removing.

### `GET /api/v1/drive/updates` — Auth required

List pending version updates for skills in the drive.

### `POST /api/v1/drive/updates/:slug/accept` — Auth required

Accept an update — may charge credits per existing pricing rules.

### `POST /api/v1/drive/updates/:slug/dismiss` — Auth required

Dismiss a pending update; no charge.

### `POST /api/v1/drive/sync/preview` — Auth required

Dry-run classification for a `skillzdrive sync` invocation. Tells
the caller, per-skill, what action would happen and how much it
would cost. Mutates no state.

### `POST /api/v1/drive/sync/execute` — Auth required

Server-side commit step. Re-validates classifications, executes
`claim_public` atomically, returns deferred upload/download
instructions for the CLI. Opens a row in `cli_sync_runs`.

```json
{
  "install_id": "uuid-from-cli",
  "kind": "first_run",
  "mode": "all",
  "classifications": [/* from sync/preview */]
}
```

`kind`: `first_run` | `daily` | `manual`.
`mode`: `all` | `public_only` | `pulls_only` | `as_many_as_credits_allow`.

Response includes a `run_id` and per-skill `results` listing the
action committed (e.g. `claim_public`, `upload_private`,
`download_install`) and any deferred upload/download instructions.

### `POST /api/v1/drive/sync/finalize` — Auth required

Closes the audit row. Sets
`users.drive_grandfather_high_water` on first run. Returns
post-sync drive count + watermark.

### `POST /api/v1/drive/diff` — Auth required

Lightweight three-way diff between local lockfile state and the
hosted drive. Drives the once-daily sync-divergence prompt.

```json
{
  "local": [{ "slug": "pdf-fill", "content_sha": "abc..." }]
}
```

Response classifies each skill as `local_only`, `drive_only`, or
`divergent` (with both shas).

## Collections

A collection is a named API key that scopes access to a subset of
the owner's drive. Partners use this pattern to give each of their
users a private slice of a master account.

### `GET /api/v1/collections` — Auth required

List the owner's collections. **Restricted:** only usable with an
all-skills key.

### `POST /api/v1/collections` — Auth required

Create a collection. Omit `skillSlugs` for an all-skills key.
Response returns the generated `sk_live_*` key exactly once.

```json
{
  "name": "acme-user-42",
  "skillSlugs": ["pdf", "docx-to-pdf"],
  "includeTeamSkills": true,
  "includeSharedSkills": true
}
```

### `PATCH /api/v1/collections/:name` — Auth required

Rename or reconfigure. Pass only the fields you want to change.

```json
{
  "newName": "acme-user-42-renamed",
  "skillSlugs": ["pdf"],
  "isPublicMarketplace": true
}
```

Setting `isPublicMarketplace: true` exposes the collection at
`/marketplace/collections/:name.json` — see the Marketplace section.

### `POST /api/v1/collections/:name/skills` — Auth required

Add a skill to the collection.

```json
{ "skillSlug": "pdf" }
```

### `DELETE /api/v1/collections/:name/skills/:slug` — Auth required

Remove a skill from the collection.

### `DELETE /api/v1/collections/:name` — Auth required

Delete the collection itself and revoke its API key. Irreversible.
**Restricted:** only usable with an all-skills key — scoped keys
cannot delete collections, including their own.

## Uploads

### `POST /api/v1/uploads/ticket` — Auth required

Mint a short-lived (10-minute, single-use) upload token. Response
includes the token itself plus an inline bash script for local
upload flows.

```json
{
  "collectionName": "acme-user-42",
  "filename": "my-skill.zip"
}
```

To upload, POST `multipart/form-data` with `file=@path/to/skill.zip`
and `Authorization: Bearer <ticket>` to
`/api/uploads/process-skill`.

### `POST /api/v1/uploads/url` — Auth required

Returns the canonical upload URL for browser-based flows.

## Downloads

### `POST /api/v1/downloads` — Auth required

Dynamically package skills into a zip and return a 24-hour signed
URL. Use for drive-wide or collection-wide exports, or one-off
bundling of a skill the caller doesn't have in their drive.

```json
{
  "scope": "drive",
  "collectionName": "acme-user-42",
  "skillSlug": "pdf"
}
```

`scope`: `drive` | `collection` | `skill`.

### `GET /api/v1/downloads/public/:slug/:sha.zip` — Anonymous ✓

**Content-addressed, immutable.** Redirects (302) to a signed
Supabase Storage URL for the exact zip bytes matching `:sha`. Cache
forever — these URLs never change.

```bash
curl -L "https://www.skillzdrive.com/api/v1/downloads/public/pdf/abc123...zip" \
     -o pdf.zip
```

## Runs

### `POST /api/v1/runs` — Auth required

Execute a skill script in the hosted E2B sandbox. Blocking
response — returns when the script finishes. Consumes credits per
the skill's `credit_cost_per_use`.

```json
{
  "skillSlug": "pdf",
  "scriptName": "extract.sh",
  "args": ["input.pdf"],
  "stdin": "",
  "sessionId": null,
  "reuseSession": false
}
```

Response:

```json
{
  "data": {
    "stdout": "...",
    "stderr": "",
    "exit_code": 0,
    "sessionId": "...",
    "duration_ms": 1420
  }
}
```

## Account

### `GET /api/v1/account/credits` — Auth required

Current credit balance, tier, monthly skill-call usage + limit, and
the 10 most recent transactions. Returns
`monthly_skill_call_limit: "unlimited"` for admin/moderator roles.

### `GET /api/v1/account/webhooks` — Auth required

List configured webhook subscriptions — see
[the dedicated Webhooks page](https://www.skillzdrive.com/docs/rest-api/webhooks.md).

### `POST /api/v1/account/webhooks` — Auth required

Create a webhook subscription.

### `DELETE /api/v1/account/webhooks/:id` — Auth required

Delete a webhook subscription.

## Marketplace

Public `.claude-plugin/marketplace.json`-compatible manifests.
Consumable by Anthropic Claude Code (`/plugin marketplace add <url>`)
and GitHub Copilot CLI
(`copilot plugin marketplace add <url>`).

### `GET /marketplace.json` — Anonymous ✓

The global public catalog. Every public+approved skill with a
downloadable source (artifact or GitHub). Cached 5 minutes.

```bash
# Then in Claude Code:
/plugin marketplace add https://www.skillzdrive.com/marketplace.json
```

### `GET /marketplace/collections/:name.json` — Anonymous ✓

Curated per-collection manifest. Returns 404 unless the collection
was published with `isPublicMarketplace: true` (see PATCH
Collections).

## Telemetry + migration

### `POST /api/v1/telemetry/anonymous` — Anonymous ✓

Record a CLI command event. Mandatory per product terms — no
opt-out — but the payload is entirely anonymized until a user logs
in and claims their install_id.

```json
{
  "install_id": "uuid-v4",
  "event_type": "install",
  "cli_version": "0.1.0",
  "os": "darwin/arm64",
  "skill_slugs": ["pdf", "docx"],
  "source_types": ["skillzdrive"],
  "metadata": { "installed": 2, "failed": 0 }
}
```

`event_type`: `install` | `sync` | `update` | `audit` | `scan` |
`init_defaults` | `login`.

### `POST /api/v1/migrations/anonymous-adopt` — Auth required

Claim an anonymous install_id for the authenticated user on first
login. Backfills any prior anonymous telemetry under that install_id
to the user's account. Idempotent. Returns 409 if another account
has already claimed that install_id.

```json
{ "install_id": "uuid-v4" }
```
