On this page

API

Distribution formats: ZIP archives and a Claude Code marketplace

Deterministic ZIP archives you can pin by SHA-256 and a hosted Claude Code plugin marketplace per owner, for tools that do not speak the ReuseForge API.

Two endpoints serve tools that understand archives or Claude Code marketplaces but do not speak this API. Both are part of contract 1.1.0 and are built from the same stored files as the JSON bundle, with the same access rules.

NeedEndpoint
One version as a ZIP you can unzip, cache in CI, or pin by hashGET /api/v1/items/{owner}/{slug}/versions/{version}/archive.zip
Install an owner's free skills, prompts and MCP servers in Claude CodeGET /api/v1/marketplaces/{owner}.json

Claude Code: add a marketplace

text
/plugin marketplace add https://reuseforge.com/api/v1/marketplaces/<owner>.json
/plugin install <slug>@reuseforge-<owner>

The marketplace is named reuseforge-<owner> (what you type after @). It lists the owner's public, free, active items of kind skill, prompt and mcp_server, one plugin per item, named by its slug. Every entry is an archive source pinned by SHA-256, so Claude Code refuses an archive whose bytes differ from what was listed:

json
{
  "name": "reuseforge-janedoe",
  "description": "Free skills, prompts and MCP servers published by Jane Doe on ReuseForge.",
  "owner": { "name": "Jane Doe", "url": "https://reuseforge.com/app/janedoe" },
  "plugins": [
    {
      "name": "invoice-skill",
      "source": {
        "source": "archive",
        "url": "https://reuseforge.com/api/v1/items/janedoe/invoice-skill/versions/1.2.0/archive.zip?layout=claude-plugin",
        "sha256": "<64 hex characters>"
      },
      "description": "Extract line items from invoices.",
      "version": "1.2.0",
      "category": "skill",
      "tags": ["invoices"],
      "author": { "name": "Jane Doe" },
      "homepage": "https://reuseforge.com/app/janedoe/invoice-skill",
      "license": "MIT"
    }
  ]
}

What is left out, on purpose:

  • paid, private, unlisted, draft, archived and retracted items (a paid archive cannot be fetched by Claude Code anyway: it has no way to answer a 402);
  • kinds that are not Claude Code plugins (repo, dataset, ...);
  • items whose latest version is yanked or a prerelease (the highest stable live version is pinned instead; an item with none is skipped), and versions that cannot be wrapped (for example a skill without SKILL.md);
  • slugs that would read as Anthropic's own names (claude-*, anthropic-*).

An owner without matching items still gets a valid document with "plugins": [].

Each listing pins an exact version. After the owner publishes a new version, the marketplace follows within a minute (Cache-Control: public, max-age=60); run /plugin marketplace update reuseforge-<owner> to refresh sooner. The first time a version appears, its archive is built to learn the digest. At most 8 are built per request; if more are outstanding the response carries X-ReuseForge-Marketplace-Pending: <n>, a 30 second cache lifetime, and the next fetch finishes the list.

Download a version as a ZIP

text
curl -fLO https://reuseforge.com/api/v1/items/janedoe/invoice-skill/versions/1.2.0/archive.zip
curl -fL -H "Authorization: Bearer $REUSEFORGE_TOKEN" -o pro.zip \
  https://reuseforge.com/api/v1/items/janedoe/paid-tool/versions/latest/archive.zip

With the CLI: reuseforge versions download janedoe/invoice-skill 1.2.0 --format zip. With the SDKs, client.get_version_archive("janedoe/invoice-skill", "1.2.0") (Python) and client.getVersionArchive(...) (JavaScript) return the raw bytes (bytes / Uint8Array).

The ZIP is deterministic: the same version always yields the same bytes, so the hash can be pinned in a lockfile, a CI cache key or a plugin marketplace.

  • store-only (no compression), entries sorted by the UTF-8 bytes of their path, no directory entries, no extra fields, no data descriptors;
  • every timestamp is 1980-01-01 00:00:00;
  • file mode 0644, or 0755 for paths the version's reuseforge.json lists under executable (globs *, **, ?); setuid, setgid and sticky bits are never set;
  • names are marked UTF-8, and paths that are absolute, contain .., empty segments, backslashes or control characters are never written.

Response headers: Content-Type: application/zip, Content-Length, ETag (the quoted SHA-256), X-Content-SHA256 (lower-case hex), Content-Disposition: attachment; filename="<slug>-<version>.zip". Send If-None-Match to get a 304. Verify a download:

text
curl -fsSD headers.txt -o x.zip "$URL"
sha256sum x.zip      # equals the X-Content-SHA256 header and the ETag
unzip -l x.zip

ItemVersion.archive_sha256 carries the digest of the default layout once it has been built (it is null before the first download and always null for paid items), so a hash can be read from the version metadata without downloading.

Pinning

Fetch the version once, record its digest, and check it on every later fetch. A version never changes, so a mismatch means the bytes were altered in transit or at rest:

text
want=<sha256 recorded earlier>
curl -fsSL -o x.zip "$URL" && echo "$want  x.zip" | sha256sum -c -

A pinned digest stays valid across ReuseForge releases that touch the archive writer only if the writer's output for that version is unchanged; the writer is versioned (format) and a change to the bytes is a contract change announced in the API changelog.

The claude-plugin layout

?layout=claude-plugin (kinds skill, prompt, mcp_server; others answer 400 invalid_parameter) wraps the version as a Claude Code plugin, with its own digest:

KindContents
all.claude-plugin/plugin.json with name (the slug) and version, plus description, license, homepage, keywords when the publish manifest has them
skillthe files under skills/<slug>/ (a SKILL.md at the root of the version is required)
promptthe main markdown file as commands/<slug>.md, other files under files/<slug>/
mcp_serverthe files under servers/<slug>/ and a .mcp.json generated from the mcp section of the publish manifest; paths to shipped files become ${CLAUDE_PLUGIN_ROOT}/servers/<slug>/..., ${VAR} placeholders are kept

A version that does not fit (a skill without SKILL.md, an MCP server without a valid mcp section, a prompt with no identifiable main file) answers 400 with the reason.

Access

Archives follow the JSON bundle exactly, and are never partial:

CallerFree public versionPaid version
anyone200402 payment_required unless every file is a free preview
signed in, no purchase200402 with an order_hint that says what to order
buyer, owner account member200200

A 402 body is a normal Problem. order_hint.create_order is the request that buys the version. Private and unlisted items answer 404 to callers who cannot see them, and retracted items 410 (members and earlier buyers of a paid version keep access). Downloads are counted once per viewer and day; the read rate tier applies.

Last updated: