API
Versions operations
Immutable published versions, bundles and install descriptors.
Immutable published versions, bundles and install descriptors. Paths are relative to https://reuseforge.com/api/v1.
| Operation | Method | Path |
|---|---|---|
| Install descriptor | GET | /items/{owner}/{slug}/install |
| List versions | GET | /items/{owner}/{slug}/versions |
| Publish a version | POST | /items/{owner}/{slug}/versions |
| Get a version | GET | /items/{owner}/{slug}/versions/{version} |
| Yank or restore a version | PATCH | /items/{owner}/{slug}/versions/{version} |
| Download a version | GET | /items/{owner}/{slug}/versions/{version}/bundle |
Install descriptor
GET /items/{owner}/{slug}/install Enabled on reuseforge.com
A machine-readable recipe for installing a version into a specific agent environment: files to write, config to merge, commands to show, and the reuseforge.lock entry. Free items work anonymously. Paid items return 402 payment_required with an order_hint until your account is entitled (an order for the item is delivered, disputed or released and not refunded). A quarantined version answers 403 version_quarantined (see get_version_bundle). The descriptor itself contains no file contents: fetch them with get_version_bundle. Fetching it while signed in records an install, which makes you eligible to review free items.
| Property | Value |
|---|---|
| Operation ID | get_install_descriptor |
| Authentication | None required. A token raises your rate limits and shows your private items. |
| Rate tier | read: 60/min per IP anonymous, 600/min per token with a token |
| Python SDK | client.get_install_descriptor(...) |
| JavaScript SDK | client.getInstallDescriptor(...) |
| CLI | rf items descriptor |
| MCP tool | get_install_descriptor |
| Deployments | reuseforge.com: enabled. Python node: implemented. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
owner | path | string | yes | Handle of the owning user or organization. |
slug | path | string | yes | Item slug, unique per owner. |
target | query | InstallTarget | no | Where the item will be installed. |
version | query | string | no | Exact version or latest (default). Default "latest". |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The descriptor. | application/json: InstallDescriptor |
| 400 | The request is malformed (bad JSON, unknown or mistyped parameter, invalid cursor). | Problem. Codes: invalid_request, invalid_json, invalid_parameter, invalid_cursor, idempotency_key_required |
| 402 | The item is paid and the caller is not entitled. order_hint says exactly what to order. | Problem. Codes: payment_required |
| 403 | Authenticated, but not allowed. Most often a missing scope (insufficient_scope, with required_scopes), an exceeded spend limit, or a role on the owning account that is too low. Also version_quarantined (the security scan or a moderator blocked the version you asked for) and review_removed (a moderator removed your review). | Problem. Codes: insufficient_scope, forbidden, account_suspended, email_unverified, spend_limit_exceeded, purchase_required, quota_exceeded, version_quarantined, review_removed |
| 404 | No such resource, or it is private and the caller may not know it exists. | Problem. Codes: not_found |
| 410 | The resource existed and is permanently unavailable (retracted item, expired plan). | Problem. Codes: gone, plan_expired |
| 422 | The body is well-formed JSON but invalid (validation_failed with per-field errors), or a business rule rejects it (payment_invalid, webhook_url_forbidden, idempotency_key_reuse). | Problem. Codes: validation_failed, payment_invalid, rail_unsupported, idempotency_key_reuse, webhook_url_forbidden, invalid_signature, integrity_mismatch |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
Example
curl
curl 'https://reuseforge.com/api/v1/items/acme/invoice-extractor/install'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.get_install_descriptor("acme/invoice-extractor")
print(result)JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.getInstallDescriptor("acme/invoice-extractor");
console.log(result);CLI
rf items descriptor acme/invoice-extractor
# rf items descriptor <item> [options]
# all options: rf items descriptor --helpResponse 200 OK:
{
"object": "install_descriptor",
"item": "acme/invoice-extractor",
"item_id": "itm_invoiceextr2",
"kind": "skill",
"version": "1.4.0",
"target": "claude-code",
"content_hash": "sha256:ea91565182d4a3fde4c71e4bc8ba34dd057a9717707652e8916f7559500f6c8c",
"license": "MIT",
"price_cents": 0,
"currency": "USD",
"entitlement": {
"required": false,
"granted": true,
"reason": "free",
"order_id": null
},
"bundle_url": "https://reuseforge.com/api/v1/items/acme/invoice-extractor/versions/1.4.0/bundle",
"files": [
{
"path": ".claude/skills/invoice-extractor/SKILL.md",
"source_path": "SKILL.md",
"sha256": "0d513c7b5ef7777ab855c1d1608db8916080485038901daa59524757549540a2",
"size_bytes": 2210,
"mode": "0644"
},
{
"path": ".claude/skills/invoice-extractor/src/extract.py",
"source_path": "src/extract.py",
"sha256": "75737d9d063bc4d4ac1613bc16d49e275727cf548a7e6eb029ac6087d22a24c6",
"size_bytes": 41876,
"mode": "0644"
},
{
"path": ".claude/skills/invoice-extractor/README.md",
"source_path": "README.md",
"sha256": "42c59af8a4bae559bc5a5d5d3f06f02ee30e2afcd9f2d0c39b38ccee6d0fa9b8",
"size_bytes": 4127,
"mode": "0644"
}
],
"config_patches": [],
"commands": [],
"lock_entry": {
"name": "acme/invoice-extractor",
"id": "itm_invoiceextr2",
"version": "1.4.0",
"kind": "skill",
"target": "claude-code",
"content_hash": "sha256:ea91565182d4a3fde4c71e4bc8ba34dd057a9717707652e8916f7559500f6c8c",
"license": "MIT",
"source": "reuseforge",
"order_id": null,
"price_paid_cents": 0,
"files": [
{
"path": ".claude/skills/invoice-extractor/SKILL.md",
"sha256": "0d513c7b5ef7777ab855c1d1608db8916080485038901daa59524757549540a2"
},
{
"path": ".claude/skills/invoice-extractor/src/extract.py",
"sha256": "75737d9d063bc4d4ac1613bc16d49e275727cf548a7e6eb029ac6087d22a24c6"
},
{
"path": ".claude/skills/invoice-extractor/README.md",
"sha256": "42c59af8a4bae559bc5a5d5d3f06f02ee30e2afcd9f2d0c39b38ccee6d0fa9b8"
}
]
},
"notes": [
"Restart Claude Code to load the skill."
],
"warnings": []
}List versions
GET /items/{owner}/{slug}/versions Enabled on reuseforge.com
Published versions, newest (by semantic version) first, including yanked ones (flagged).
| Property | Value |
|---|---|
| Operation ID | list_versions |
| Authentication | None required. A token raises your rate limits and shows your private items. |
| Rate tier | read: 60/min per IP anonymous, 600/min per token with a token |
| Python SDK | client.list_versions(...) |
| JavaScript SDK | client.listVersions(...) |
| CLI | rf versions list |
| MCP tool | list_versions |
| Deployments | reuseforge.com: enabled. Python node: implemented. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
owner | path | string | yes | Handle of the owning user or organization. |
slug | path | string | yes | Item slug, unique per owner. |
cursor | query | string | no | Opaque cursor from the previous page's next_cursor. |
limit | query | integer | no | Page size. Default 30. |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | A page of versions without file lists. | application/json: ItemVersionPage |
| 404 | No such resource, or it is private and the caller may not know it exists. | Problem. Codes: not_found |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
Example
curl
curl 'https://reuseforge.com/api/v1/items/acme/invoice-extractor/versions'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
for entry in client.list_versions("acme/invoice-extractor"): # iterating fetches further pages on demand
print(entry["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
for await (const entry of client.listVersions("acme/invoice-extractor")) { // iterating fetches further pages on demand
console.log(entry.id);
}CLI
rf versions list acme/invoice-extractor
# rf versions list <item> [options]
# all options: rf versions list --helpResponse 200 OK:
{
"data": [
{
"id": "ver_xinvoice2345",
"object": "item_version",
"item": "acme/invoice-extractor",
"item_id": "itm_invoiceextr2",
"version": "1.4.0",
"changelog": "Adds multi-currency totals and a --strict mode.",
"content_hash": "sha256:ea91565182d4a3fde4c71e4bc8ba34dd057a9717707652e8916f7559500f6c8c",
"size_bytes": 48213,
"file_count": 3,
"dependencies": [
{
"item": "acme/pdf-text-layer",
"range": "^2.0.0"
}
],
"yanked": false,
"yanked_reason": null,
"prerelease": false,
"archive_sha256": "a3c0f5bd09a1d7b4e2f8c61d9b7e45a0c1f3d2e8b6a49f7c05d1e3b28a6c4f90",
"manifest": null,
"created_by": {
"id": "acc_acmelabsorg2",
"type": "org",
"handle": "acme",
"display_name": "Acme Labs",
"verified": true,
"avatar_url": ""
},
"created_at": "2026-10-28T09:10:02Z"
}
],
"next_cursor": null,
"has_more": false
}Publish a version
POST /items/{owner}/{slug}/versions Enabled on reuseforge.com
Publishes an immutable version. Versions are semantic versions (MAJOR.MINOR.PATCH[-prerelease]), must be unique per item and can never be overwritten; correct a mistake by publishing a higher version and yanking the bad one. Limits: 100 files, 512 KiB per file, 2 MiB per version (see get_api_index), and a storage quota per owning account over all its items (20 MB unverified, 200 MB verified email, 1 GB verified publisher); a version that does not fit answers 413 quota_exceeded and stores nothing. The version is scanned for malware patterns before the answer; a blocked version is quarantined (see get_version_bundle). Paths are relative, use /, and may not contain ... If content_hash is sent, the server recomputes it and answers 422 on mismatch. Fires item.version.published (and item.published for the first public version).
| Property | Value |
|---|---|
| Operation ID | publish_version |
| Authentication | Bearer token holding items:publish |
| Rate tier | write: not allowed anonymous, 120/min per token with a token |
| Idempotency | Idempotency-Key header, optional. The SDKs and the CLI send one for you. |
| Python SDK | client.publish_version(...) |
| JavaScript SDK | client.publishVersion(...) |
| CLI | rf versions publish |
| MCP tool | publish_version |
| Deployments | reuseforge.com: enabled. Python node: not implemented (answers 501). |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
owner | path | string | yes | Handle of the owning user or organization. |
slug | path | string | yes | Item slug, unique per owner. |
Idempotency-Key | header | string | no | Client-chosen unique key (1 to 255 printable ASCII characters, a UUID is ideal). A repeat of the same request with the same key within 24 hours returns the first response with Idempotent-Replayed: true. The same key with a different body returns 422 idempotency_key_reuse; a repeat while the first call is still running returns 409 idempotency_in_progress. |
Request body
application/json, required, schema VersionCreate
| Field | Type | Required | Description |
|---|---|---|---|
version | string | yes | |
changelog | string | no | |
files | array of FileInput | yes | |
dependencies | array of Dependency | no | |
manifest | object | no | Contents of reuseforge.json (schema: api/schemas/reuseforge.schema.json). Validated and stored. |
content_hash | ContentHash | no | SHA-256 over the text made of one line <sha256 of file bytes> <path> per file (two spaces, \n after each line), files sorted by path in UTF-8 byte order. Identical to the output of sha256sum over the files, so it can be recomputed with standard tools. |
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 Created | The published version. | application/json: ItemVersion |
| 400 | The request is malformed (bad JSON, unknown or mistyped parameter, invalid cursor). | Problem. Codes: invalid_request, invalid_json, invalid_parameter, invalid_cursor, idempotency_key_required |
| 401 | Missing, malformed, expired or revoked credentials. | Problem. Codes: unauthorized, invalid_token, token_expired |
| 403 | Authenticated, but not allowed. Most often a missing scope (insufficient_scope, with required_scopes), an exceeded spend limit, or a role on the owning account that is too low. Also version_quarantined (the security scan or a moderator blocked the version you asked for) and review_removed (a moderator removed your review). | Problem. Codes: insufficient_scope, forbidden, account_suspended, email_unverified, spend_limit_exceeded, purchase_required, quota_exceeded, version_quarantined, review_removed |
| 404 | No such resource, or it is private and the caller may not know it exists. | Problem. Codes: not_found |
| 409 | The request conflicts with current state (duplicate resource, wrong lifecycle state, idempotency conflict). | Problem. Codes: conflict, already_exists, invalid_state, idempotency_in_progress |
| 413 | The body or a file exceeds a limit published in get_api_index. | Problem. Codes: payload_too_large |
| 422 | The body is well-formed JSON but invalid (validation_failed with per-field errors), or a business rule rejects it (payment_invalid, webhook_url_forbidden, idempotency_key_reuse). | Problem. Codes: validation_failed, payment_invalid, rail_unsupported, idempotency_key_reuse, webhook_url_forbidden, invalid_signature, integrity_mismatch |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
Example
curl
curl -X POST 'https://reuseforge.com/api/v1/items/acme/invoice-extractor/versions' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"version": "1.4.0",
"changelog": "Adds multi-currency totals and a --strict mode.",
"files": [
{
"path": "SKILL.md",
"content": "---\nname: invoice-extractor\ndescription: Extract invoice fields from PDFs.\n---\n\nRun src/extract.py on a PDF.\n",
"encoding": "utf8",
"content_type": "text/markdown",
"preview": true
},
{
"path": "src/extract.py",
"content": "import sys\n\nprint('\''extract'\'', sys.argv[1:])\n",
"encoding": "utf8",
"content_type": "text/x-python"
}
],
"dependencies": [
{
"item": "acme/pdf-text-layer",
"range": "^2.0.0"
}
],
"manifest": {
"name": "acme/invoice-extractor",
"version": "1.4.0",
"kind": "skill"
}
}'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.publish_version(
"acme/invoice-extractor",
version="1.4.0",
changelog="Adds multi-currency totals and a --strict mode.",
files=[
{
"path": "SKILL.md",
"content": "---\nname: invoice-extractor\ndescription: Extract invoice fields from PDFs.\n---\n\nRun src/extract.py on a PDF.\n",
"encoding": "utf8",
"content_type": "text/markdown",
"preview": True,
},
{
"path": "src/extract.py",
"content": "import sys\n\nprint('extract', sys.argv[1:])\n",
"encoding": "utf8",
"content_type": "text/x-python",
},
],
dependencies=[{"item": "acme/pdf-text-layer", "range": "^2.0.0"}],
manifest={"name": "acme/invoice-extractor", "version": "1.4.0", "kind": "skill"},
)
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.publishVersion("acme/invoice-extractor", {
version: "1.4.0",
changelog: "Adds multi-currency totals and a --strict mode.",
files: [
{
path: "SKILL.md",
content: "---\nname: invoice-extractor\ndescription: Extract invoice fields from PDFs.\n---\n\nRun src/extract.py on a PDF.\n",
encoding: "utf8",
content_type: "text/markdown",
preview: true,
},
{
path: "src/extract.py",
content: "import sys\n\nprint('extract', sys.argv[1:])\n",
encoding: "utf8",
content_type: "text/x-python",
},
],
dependencies: [{ item: "acme/pdf-text-layer", range: "^2.0.0" }],
manifest: { name: "acme/invoice-extractor", version: "1.4.0", kind: "skill" },
});
console.log(result.id);CLI
rf versions publish
# rf versions publish [options]
# all options: rf versions publish --helpResponse 201 Created:
{
"id": "ver_xinvoice2345",
"object": "item_version",
"item": "acme/invoice-extractor",
"item_id": "itm_invoiceextr2",
"version": "1.4.0",
"changelog": "Adds multi-currency totals and a --strict mode.",
"content_hash": "sha256:ea91565182d4a3fde4c71e4bc8ba34dd057a9717707652e8916f7559500f6c8c",
"size_bytes": 48213,
"file_count": 3,
"dependencies": [
{
"item": "acme/pdf-text-layer",
"range": "^2.0.0"
}
],
"yanked": false,
"yanked_reason": null,
"prerelease": false,
"archive_sha256": "a3c0f5bd09a1d7b4e2f8c61d9b7e45a0c1f3d2e8b6a49f7c05d1e3b28a6c4f90",
"manifest": null,
"created_by": {
"id": "acc_acmelabsorg2",
"type": "org",
"handle": "acme",
"display_name": "Acme Labs",
"verified": true,
"avatar_url": ""
},
"created_at": "2026-10-28T09:10:02Z"
}Get a version
GET /items/{owner}/{slug}/versions/{version} Enabled on reuseforge.com
One version with its file list (paths, sizes, hashes; contents only through the bundle). latest resolves to the highest non-yanked, non-quarantined stable version, or, only when the item has no stable version, the highest live prerelease. For a quarantined version (see get_version_bundle) callers who do not manage the item receive the version without files, manifest and verification.
| Property | Value |
|---|---|
| Operation ID | get_version |
| Authentication | None required. A token raises your rate limits and shows your private items. |
| Rate tier | read: 60/min per IP anonymous, 600/min per token with a token |
| Python SDK | client.get_version(...) |
| JavaScript SDK | client.getVersion(...) |
| CLI | rf versions get |
| MCP tool | get_version |
| Deployments | reuseforge.com: enabled. Python node: implemented. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
owner | path | string | yes | Handle of the owning user or organization. |
slug | path | string | yes | Item slug, unique per owner. |
version | path | string | yes | An exact semantic version or latest. |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The version. | application/json: ItemVersion |
| 404 | No such resource, or it is private and the caller may not know it exists. | Problem. Codes: not_found |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
Example
curl
curl 'https://reuseforge.com/api/v1/items/acme/invoice-extractor/versions/1.4.0'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.get_version("acme/invoice-extractor", "1.4.0")
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.getVersion("acme/invoice-extractor", "1.4.0");
console.log(result.id);CLI
rf versions get acme/invoice-extractor 1.4.0
# rf versions get <item> [version]
# all options: rf versions get --helpResponse 200 OK:
{
"id": "ver_xinvoice2345",
"object": "item_version",
"item": "acme/invoice-extractor",
"item_id": "itm_invoiceextr2",
"version": "1.4.0",
"changelog": "Adds multi-currency totals and a --strict mode.",
"content_hash": "sha256:ea91565182d4a3fde4c71e4bc8ba34dd057a9717707652e8916f7559500f6c8c",
"size_bytes": 48213,
"file_count": 3,
"files": [
{
"path": "SKILL.md",
"size_bytes": 2210,
"sha256": "0d513c7b5ef7777ab855c1d1608db8916080485038901daa59524757549540a2",
"content_type": "text/markdown",
"preview": true
},
{
"path": "src/extract.py",
"size_bytes": 41876,
"sha256": "75737d9d063bc4d4ac1613bc16d49e275727cf548a7e6eb029ac6087d22a24c6",
"content_type": "text/x-python",
"preview": false
},
{
"path": "README.md",
"size_bytes": 4127,
"sha256": "42c59af8a4bae559bc5a5d5d3f06f02ee30e2afcd9f2d0c39b38ccee6d0fa9b8",
"content_type": "text/markdown",
"preview": true
}
],
"dependencies": [
{
"item": "acme/pdf-text-layer",
"range": "^2.0.0"
}
],
"yanked": false,
"yanked_reason": null,
"prerelease": false,
"manifest": null,
"verification": {
"id": "vrf_testrun22222",
"object": "verification",
"item": "acme/invoice-extractor",
"version": "1.4.0",
"state": "passed",
"runner": "platform",
"tests_total": 64,
"tests_passed": 62,
"tests_pass_rate": 0.97,
"summary": "62 of 64 tests passed on Python 3.12.",
"created_at": "2026-10-28T09:11:00Z",
"completed_at": "2026-10-28T09:12:40Z"
},
"created_by": {
"id": "acc_acmelabsorg2",
"type": "org",
"handle": "acme",
"display_name": "Acme Labs",
"verified": true,
"avatar_url": ""
},
"created_at": "2026-10-28T09:10:02Z"
}Yank or restore a version
PATCH /items/{owner}/{slug}/versions/{version} Enabled on reuseforge.com
Yanking hides a version from latest resolution and warns installers, but never deletes content, so existing lockfiles still resolve. Only changelog, yanked and yanked_reason can change. Fires item.updated.
| Property | Value |
|---|---|
| Operation ID | update_version |
| Authentication | Bearer token holding items:publish |
| Rate tier | write: not allowed anonymous, 120/min per token with a token |
| Python SDK | client.update_version(...) |
| JavaScript SDK | client.updateVersion(...) |
| CLI | rf versions yank |
| MCP tool | update_version |
| Deployments | reuseforge.com: enabled. Python node: not implemented (answers 501). |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
owner | path | string | yes | Handle of the owning user or organization. |
slug | path | string | yes | Item slug, unique per owner. |
version | path | string | yes | An exact semantic version or latest. |
Request body
application/json, required, schema VersionUpdate
| Field | Type | Required | Description |
|---|---|---|---|
changelog | string | no | |
yanked | boolean | no | |
yanked_reason | string or null | no |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The updated version. | application/json: ItemVersion |
| 400 | The request is malformed (bad JSON, unknown or mistyped parameter, invalid cursor). | Problem. Codes: invalid_request, invalid_json, invalid_parameter, invalid_cursor, idempotency_key_required |
| 401 | Missing, malformed, expired or revoked credentials. | Problem. Codes: unauthorized, invalid_token, token_expired |
| 403 | Authenticated, but not allowed. Most often a missing scope (insufficient_scope, with required_scopes), an exceeded spend limit, or a role on the owning account that is too low. Also version_quarantined (the security scan or a moderator blocked the version you asked for) and review_removed (a moderator removed your review). | Problem. Codes: insufficient_scope, forbidden, account_suspended, email_unverified, spend_limit_exceeded, purchase_required, quota_exceeded, version_quarantined, review_removed |
| 404 | No such resource, or it is private and the caller may not know it exists. | Problem. Codes: not_found |
| 422 | The body is well-formed JSON but invalid (validation_failed with per-field errors), or a business rule rejects it (payment_invalid, webhook_url_forbidden, idempotency_key_reuse). | Problem. Codes: validation_failed, payment_invalid, rail_unsupported, idempotency_key_reuse, webhook_url_forbidden, invalid_signature, integrity_mismatch |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
Example
curl
curl -X PATCH 'https://reuseforge.com/api/v1/items/acme/invoice-extractor/versions/1.4.0' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"yanked": true,
"yanked_reason": "Crashes on invoices without a total line."
}'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.update_version(
"acme/invoice-extractor",
"1.4.0",
yanked=True,
yanked_reason="Crashes on invoices without a total line.",
)
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.updateVersion("acme/invoice-extractor", "1.4.0", { yanked: true, yanked_reason: "Crashes on invoices without a total line." });
console.log(result.id);CLI
rf versions yank acme/invoice-extractor 1.4.0
# rf versions yank <item> <version> [options]
# all options: rf versions yank --helpResponse 200 OK:
{
"id": "ver_xinvoice2345",
"object": "item_version",
"item": "acme/invoice-extractor",
"item_id": "itm_invoiceextr2",
"version": "1.4.0",
"changelog": "Adds multi-currency totals and a --strict mode.",
"content_hash": "sha256:ea91565182d4a3fde4c71e4bc8ba34dd057a9717707652e8916f7559500f6c8c",
"size_bytes": 48213,
"file_count": 3,
"dependencies": [],
"yanked": true,
"yanked_reason": "Crashes on invoices without a total line.",
"prerelease": false,
"manifest": null,
"created_by": {
"id": "acc_acmelabsorg2",
"type": "org",
"handle": "acme",
"display_name": "Acme Labs",
"verified": true,
"avatar_url": ""
},
"created_at": "2026-10-28T09:10:02Z"
}Download a version
GET /items/{owner}/{slug}/versions/{version}/bundle Enabled on reuseforge.com
All files of a version in one JSON document (at most 2 MiB of content, base64 for binary files). Clients must verify every sha256 and the content_hash. Paid versions return 402 payment_required until you are entitled, except for files flagged preview. A quarantined version (the security scan blocked it and no administrator allowed it, or an administrator blocked it) answers 403 version_quarantined to everybody except the owner, members of the owning account with write access and administrators; the check runs before the entitlement check.
| Property | Value |
|---|---|
| Operation ID | get_version_bundle |
| Authentication | None required. A token raises your rate limits and shows your private items. |
| Rate tier | read: 60/min per IP anonymous, 600/min per token with a token |
| Python SDK | client.get_version_bundle(...) |
| JavaScript SDK | client.getVersionBundle(...) |
| CLI | rf versions download |
| MCP tool | not exposed as a tool |
| Deployments | reuseforge.com: enabled. Python node: implemented. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
owner | path | string | yes | Handle of the owning user or organization. |
slug | path | string | yes | Item slug, unique per owner. |
version | path | string | yes | An exact semantic version or latest. |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The bundle. | application/json: Bundle |
| 402 | The item is paid and the caller is not entitled. order_hint says exactly what to order. | Problem. Codes: payment_required |
| 403 | Authenticated, but not allowed. Most often a missing scope (insufficient_scope, with required_scopes), an exceeded spend limit, or a role on the owning account that is too low. Also version_quarantined (the security scan or a moderator blocked the version you asked for) and review_removed (a moderator removed your review). | Problem. Codes: insufficient_scope, forbidden, account_suspended, email_unverified, spend_limit_exceeded, purchase_required, quota_exceeded, version_quarantined, review_removed |
| 404 | No such resource, or it is private and the caller may not know it exists. | Problem. Codes: not_found |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
Example
curl
curl 'https://reuseforge.com/api/v1/items/acme/invoice-extractor/versions/1.4.0/bundle'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.get_version_bundle("acme/invoice-extractor", "1.4.0")
print(result)JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.getVersionBundle("acme/invoice-extractor", "1.4.0");
console.log(result);CLI
rf versions download acme/invoice-extractor 1.4.0
# rf versions download <item> [version] [options]
# all options: rf versions download --helpResponse 200 OK:
{
"item": "acme/invoice-extractor",
"version": "1.4.0",
"content_hash": "sha256:ea91565182d4a3fde4c71e4bc8ba34dd057a9717707652e8916f7559500f6c8c",
"files": [
{
"path": "SKILL.md",
"content": "---\nname: invoice-extractor\ndescription: Extract invoice fields from PDFs.\n---\n\nRun src/extract.py on a PDF.\n",
"encoding": "utf8",
"sha256": "0d513c7b5ef7777ab855c1d1608db8916080485038901daa59524757549540a2",
"size_bytes": 2210,
"content_type": "text/markdown"
}
]
}