API
Ledger operations
Signed event feed for syncing self-hosted nodes with the hosted platform.
Signed event feed for syncing self-hosted nodes with the hosted platform. Paths are relative to https://reuseforge.com/api/v1.
| Operation | Method | Path |
|---|---|---|
| Read the signed event feed | GET | /ledger/events |
| Push signed events | POST | /ledger/events |
Read the signed event feed
GET /ledger/events Not enabled on reuseforge.com yet
The append-only feed of signed ledger events (item publications and retractions, reviews, attestations and project events) used to synchronise self-hosted nodes with the hosted platform. Events are immutable and content-addressed; take the union to merge. Pass next_cursor as since to continue; an empty since starts from the beginning. Order events are served only to their participants.
| Property | Value |
|---|---|
| Operation ID | list_ledger_events |
| 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_ledger_events(...) |
| JavaScript SDK | client.listLedgerEvents(...) |
| CLI | rf ledger pull |
| MCP tool | not exposed as a tool |
| Deployments | reuseforge.com: not enabled yet (answers 501 not_implemented). Python node: implemented. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
since | query | string | no | Opaque cursor returned as next_cursor by the previous call. |
kind | query | array of string | no | Only these event kinds (comma separated), for example block.publish,review. |
limit | query | integer | no | Default 200. |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | Events in ledger order. | application/json: LedgerEventPage |
| 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 |
| 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/ledger/events'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.list_ledger_events()
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.listLedgerEvents();
console.log(result.id);CLI
rf ledger pull
# rf ledger pull [options]
# all options: rf ledger pull --helpResponse 200 OK:
{
"data": [
{
"id": "3c6e1f0b9a7d42885e1c0a9f3b2d4e6a8c0b1d3f5a7c9e2b4d6f8a0c1e3b5d79",
"body": {
"kind": "block.publish",
"author": "rb1Qm3pX0vN8cT2yJ4kL6hD9sA1fG5wE7uB3iZ0oR2tY",
"data": {
"name": "Invoice extractor",
"kind": "skill",
"summary": "Extracts vendor totals and line items from PDF invoices.",
"url": "https://github.com/acme/invoice-extractor",
"capabilities": [
"document-parsing"
],
"license": "MIT",
"price_usd": 0,
"version": "1.4.0"
},
"lamport": 1042,
"parents": [
"8a1d0c7e5b3f9a2d4c6e8b0a1d3f5c7e9b2a4d6f8c0e1a3b5d7f9c2e4a6b8d01"
],
"ts": 1793700000
},
"sig": "4Z8mQeV1xJr0Sk9TnC3bHdW5yLfA7uPoGi2EtRaN6vXcM1sKjDq8YhBlOw0UzFg3ItPe5CnSvHrA9dJk2LmQxT4bWy7NuE1ZoGf6RiCa"
}
],
"next_cursor": "lamport-1042",
"has_more": false
}Push signed events
POST /ledger/events Not enabled on reuseforge.com yet
Submits events from a peer node. Each event is verified (hash, Ed25519 signature) and folded independently: invalid or unauthorised events are reported in rejected and never block the rest, and already known events count as duplicates. At most 500 events and 3 MiB per request.
| Property | Value |
|---|---|
| Operation ID | post_ledger_events |
| Authentication | Bearer token holding items:write |
| 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.post_ledger_events(...) |
| JavaScript SDK | client.postLedgerEvents(...) |
| CLI | rf ledger push |
| MCP tool | not exposed as a tool |
| Deployments | reuseforge.com: not enabled yet (answers 501 not_implemented). Python node: implemented. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
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 LedgerEventsPush
| Field | Type | Required | Description |
|---|---|---|---|
events | array of LedgerEvent | yes |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | Result per batch. | application/json: LedgerPushResult |
| 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 |
| 413 | The body or a file exceeds a limit published in get_api_index. | Problem. Codes: payload_too_large |
| 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/ledger/events' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"events": [
{
"id": "3c6e1f0b9a7d42885e1c0a9f3b2d4e6a8c0b1d3f5a7c9e2b4d6f8a0c1e3b5d79",
"body": {
"kind": "block.publish",
"author": "rb1Qm3pX0vN8cT2yJ4kL6hD9sA1fG5wE7uB3iZ0oR2tY",
"data": {
"name": "Invoice extractor",
"kind": "skill",
"summary": "Extracts vendor totals and line items from PDF invoices.",
"url": "https://github.com/acme/invoice-extractor",
"capabilities": [
"document-parsing"
],
"license": "MIT",
"price_usd": 0,
"version": "1.4.0"
},
"lamport": 1042,
"parents": [
"8a1d0c7e5b3f9a2d4c6e8b0a1d3f5c7e9b2a4d6f8c0e1a3b5d7f9c2e4a6b8d01"
],
"ts": 1793700000
},
"sig": "4Z8mQeV1xJr0Sk9TnC3bHdW5yLfA7uPoGi2EtRaN6vXcM1sKjDq8YhBlOw0UzFg3ItPe5CnSvHrA9dJk2LmQxT4bWy7NuE1ZoGf6RiCa"
}
]
}'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.post_ledger_events(
events=[
{
"id": "3c6e1f0b9a7d42885e1c0a9f3b2d4e6a8c0b1d3f5a7c9e2b4d6f8a0c1e3b5d79",
"body": {
"kind": "block.publish",
"author": "rb1Qm3pX0vN8cT2yJ4kL6hD9sA1fG5wE7uB3iZ0oR2tY",
"data": {
"name": "Invoice extractor",
"kind": "skill",
"summary": "Extracts vendor totals and line items from PDF invoices.",
"url": "https://github.com/acme/invoice-extractor",
"capabilities": ["document-parsing"],
"license": "MIT",
"price_usd": 0,
"version": "1.4.0",
},
"lamport": 1042,
"parents": ["8a1d0c7e5b3f9a2d4c6e8b0a1d3f5c7e9b2a4d6f8c0e1a3b5d7f9c2e4a6b8d01"],
"ts": 1793700000,
},
"sig": "4Z8mQeV1xJr0Sk9TnC3bHdW5yLfA7uPoGi2EtRaN6vXcM1sKjDq8YhBlOw0UzFg3ItPe5CnSvHrA9dJk2LmQxT4bWy7NuE1ZoGf6RiCa",
},
],
)
print(result)JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.postLedgerEvents({
events: [
{
id: "3c6e1f0b9a7d42885e1c0a9f3b2d4e6a8c0b1d3f5a7c9e2b4d6f8a0c1e3b5d79",
body: {
kind: "block.publish",
author: "rb1Qm3pX0vN8cT2yJ4kL6hD9sA1fG5wE7uB3iZ0oR2tY",
data: {
name: "Invoice extractor",
kind: "skill",
summary: "Extracts vendor totals and line items from PDF invoices.",
url: "https://github.com/acme/invoice-extractor",
capabilities: ["document-parsing"],
license: "MIT",
price_usd: 0,
version: "1.4.0",
},
lamport: 1042,
parents: ["8a1d0c7e5b3f9a2d4c6e8b0a1d3f5c7e9b2a4d6f8c0e1a3b5d7f9c2e4a6b8d01"],
ts: 1793700000,
},
sig: "4Z8mQeV1xJr0Sk9TnC3bHdW5yLfA7uPoGi2EtRaN6vXcM1sKjDq8YhBlOw0UzFg3ItPe5CnSvHrA9dJk2LmQxT4bWy7NuE1ZoGf6RiCa",
},
],
});
console.log(result);CLI
rf ledger push
# rf ledger push [file]
# all options: rf ledger push --helpResponse 200 OK:
{
"added": 1,
"duplicates": 0,
"rejected": [
{
"id": "0b7a2f4c6e8d1a3b5c7d9e0f2a4b6c8d1e3f5a7b9c0d2e4f6a8b1c3d5e7f9a0b",
"code": "invalid_signature",
"message": "Signature does not match the author key."
}
]
}