API
Quickstart
Get a token, search, plan, install and buy on ReuseForge in curl, Python, JavaScript and the rf CLI, with every step runnable against reuseforge.com or your own node.
This walks the whole loop once: a token, a search, a plan for a goal and a budget, an install, and a purchase. Reads and plans work without a token. Every snippet has four forms; pick one and the choice sticks across the site.
Sample data. The examples use acme/invoice-extractor, a sample item from the contract's own examples. Replace it with an owner/slug from your own search results. Money is always integer US cents in the API (budget_cents: 5000 is $50.00); the CLI takes dollars (--budget 50).
1. Install the tooling
You need none of it for curl. The SDKs and the CLI are not on PyPI or npm yet, so install them from a checkout of the repository:
Python
git clone https://github.com/Longfordjames/Claude-1 && cd Claude-1
pip install ./sdk/python # Python 3.9+, standard library only
python3 -c "import reuseforge; print(reuseforge.__version__)"JavaScript
git clone https://github.com/Longfordjames/Claude-1 && cd Claude-1
npm install ./sdk/js # Node 18+; ESM; also runs on Workers, Deno, Bun and browsersCLI
git clone https://github.com/Longfordjames/Claude-1 && cd Claude-1
npm install -g ./cli # Node 18+; installs rf and reuseforge
rf --version2. Get a token
Anonymous calls can search, read items and plan, at lower rate limits. Everything that changes something needs a token.
- Sign in to the web app and open Settings, API tokens (
/app/). - Name the token and choose scopes. For this guide Read is enough for steps 3 to 6, and Orders adds the buying step.
- Copy the token. It starts with
rf_and is shown once; store it like a password.
export REUSEFORGE_TOKEN=rf_... # the SDKs and the CLI read this variableThe web app issues the legacy scope names read, write, orders (and admin for administrators), which expand to the fine-grained scopes described in authentication. A token with the orders scope can spend, so it gets a default lifetime cap of $50 that you can see in whoami. For an agent, make a narrower token with the create_token operation; see agent spending limits.
Never put a token in client-side code, a repository or a prompt. A token acts as you within its scopes. Revoke it in Settings if it leaks.
3. Check who you are
get_me returns your account, the scopes the token holds and its spend limit.
curl
curl https://reuseforge.com/api/v1/me \
-H "Authorization: Bearer $REUSEFORGE_TOKEN"Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
me = client.get_me()
print(me["handle"], me["auth"]["scopes"], me["auth"]["spend_limit_cents"])JavaScript
import { ReuseForge } from '@reuseforge/sdk/node';
const client = new ReuseForge(); // token from REUSEFORGE_TOKEN, or the file `rf login` wrote
const me = await client.getMe();
console.log(me.handle, me.auth.scopes, me.auth.spend_limit_cents);CLI
rf login # paste the token once; or: echo "$REUSEFORGE_TOKEN" | rf login --with-token
rf whoami401 invalid_token means the token is wrong or revoked, 401 token_expired that it ran out (new tokens last 90 days by default). See errors.
4. Search
search_items filters public, active items. Add a token to also see your own private ones.
curl
curl -G https://reuseforge.com/api/v1/items \
--data-urlencode 'q=invoice pdf extraction' \
--data-urlencode 'kind=skill,harness' \
--data-urlencode 'max_price_cents=2000' \
--data-urlencode 'limit=5'Python
import reuseforge
client = reuseforge.Client()
for item in client.search_items(q="invoice pdf extraction", kind=["skill", "harness"], max_price_cents=2000, limit=5, max_items=5):
print(item["full_name"], item["kind"], reuseforge.dollars(item["price_cents"]))JavaScript
import { ReuseForge, dollars } from '@reuseforge/sdk';
const client = new ReuseForge();
for await (const item of client.searchItems({ q: 'invoice pdf extraction', kind: ['skill', 'harness'], max_price_cents: 2000, limit: 5, maxItems: 5 })) {
console.log(item.full_name, item.kind, dollars(item.price_cents));
}CLI
rf search 'invoice pdf extraction' --kind skill --kind harness --max-price 20 --limit 5Results are a page: {data, next_cursor, has_more}. The SDKs iterate across pages for you.
5. Plan
Give plan_goal a goal and a budget. It decomposes the goal into capabilities, finds candidates in the registry (and, with online, on GitHub, npm, PyPI and other sources) and returns the Pareto-optimal options: higher quality, lower cost, lower risk. Nothing is bought. A plan can be fetched for 24 hours (expires_at).
curl
curl -X POST https://reuseforge.com/api/v1/plans \
-H "Content-Type: application/json" \
-d '{"goal": "RAG chatbot over my PDF invoices", "budget_cents": 5000, "online": true}'Python
import reuseforge
client = reuseforge.Client()
plan = client.plan_goal(goal="RAG chatbot over my PDF invoices", budget_cents=5000, online=True)
best = plan["recommended"]
print(plan["id"], "options:", len(plan["pareto_front"]))
print("to buy:", reuseforge.dollars(best["paid_total_cents"]), "to build:", reuseforge.dollars(best["build_total_cents"]))
for step in best["steps"]:
print(step["requirement"], "->", step["action"], step["candidate"]["item"] if step.get("candidate") else "")JavaScript
import { ReuseForge, dollars } from '@reuseforge/sdk';
const client = new ReuseForge();
const plan = await client.planGoal({ goal: 'RAG chatbot over my PDF invoices', budget_cents: 5000, online: true });
const best = plan.recommended;
console.log(plan.id, 'options:', plan.pareto_front.length);
console.log('to buy:', dollars(best.paid_total_cents), 'to build:', dollars(best.build_total_cents));
for (const step of best.steps) console.log(step.requirement, '->', step.action, step.candidate?.item ?? '');CLI
rf plan 'RAG chatbot over my PDF invoices' --budget 50 --online --out plan.jsonEach step of an option says what to do for one requirement: use an existing item, buy a paid one, or build it yourself (see build_brief for a ready-made brief). plan_goal is rate limited more tightly than other reads: 5 a minute for anonymous callers and 20 a minute with a token (see rate limits). Read the explanations and sources members before you trust a result; sources says which connectors answered.
6. Install
Installing resolves the install descriptor, downloads the bundle, verifies every file hash before writing anything, and records the result in reuseforge.lock. It never pays and never runs anything the package suggests.
curl
# The descriptor says which files go where for a target; the bundle holds the file contents.
curl 'https://reuseforge.com/api/v1/items/acme/invoice-extractor/install?target=claude-code'
curl 'https://reuseforge.com/api/v1/items/acme/invoice-extractor/versions/latest/bundle'Python
import reuseforge
client = reuseforge.Client()
result = client.install("acme/invoice-extractor", target="claude-code", dest=".")
print(result["version"], [f["path"] for f in result["files"]])JavaScript
import { ReuseForge, install } from '@reuseforge/sdk/node';
const client = new ReuseForge();
const result = await install(client, 'acme/invoice-extractor', { target: 'claude-code', dest: process.cwd() });
console.log(result.version, result.files.map((f) => `${f.status} ${f.path}`));CLI
rf install acme/invoice-extractor --target claude-code
rf list # what is installed
rf verify # re-hash installed files against reuseforge.lock, offlineA paid item you do not own stops with 402 payment_required. The error carries an order_hint: the exact create_order request that buys it. The CLI prints it and exits with code 7; the SDKs raise PaymentRequiredError. Install targets are generic, claude-code, cursor, codex and mcp-json; see install descriptors for where each kind goes.
7. Buy
Paid items are bought through an order: the buyer pays into escrow, the item is delivered, and the buyer accepts (or disputes) to release the money. Payments run in test mode on a simulated rail, so no real money moves. create_order and pay_order require an Idempotency-Key; the SDKs and the CLI generate one for you and reuse it across retries.
Availability. The orders operations are in the contract and implemented by the self-hosted node, but they are not switched on at reuseforge.com yet: there they answer 501 not_implemented. Run the buying steps against your own node (see self-hosting) or wait for the hosted payments group. The availability table is generated from the source and shows the current state.
curl
API=https://reuseforge.com/api/v1 # or your node: http://127.0.0.1:8420/api/v1
AUTH="Authorization: Bearer $REUSEFORGE_TOKEN"
# 1. quote an order for the paid items of a plan
curl -X POST $API/orders -H "$AUTH" -H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" \
-d '{"plan_id": "pln_goalinvoice2", "rail": "simulated"}'
# 2. pay it into escrow (simulated rail: no body needed)
curl -X POST $API/orders/ord_tqjv5mw3k2na/pay -H "$AUTH" -H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" -d '{}'
# 3. read it: items hosted on ReuseForge are delivered automatically once the order is funded
curl $API/orders/ord_tqjv5mw3k2na -H "$AUTH"Python
import reuseforge
client = reuseforge.Client()
plan = client.plan_goal(goal="RAG chatbot over my PDF invoices", budget_cents=5000)
order = client.create_order(plan_id=plan["id"], rail="simulated") # state: awaiting_payment
print("total", reuseforge.dollars(order["total_cents"]), "fee", reuseforge.dollars(order["fee_cents"]))
order = client.pay_order(order["id"]) # state: funded
order = client.wait_for_order(order["id"], states=("funded", "settled"))
client.install("acme/invoice-extractor", target="claude-code", dest=".") # access starts at delivery, before releaseJavaScript
import { ReuseForge, dollars, install } from '@reuseforge/sdk/node';
const client = new ReuseForge();
const plan = await client.planGoal({ goal: 'RAG chatbot over my PDF invoices', budget_cents: 5000 });
let order = await client.createOrder({ plan_id: plan.id, rail: 'simulated' }); // state: awaiting_payment
console.log('total', dollars(order.total_cents), 'fee', dollars(order.fee_cents));
order = await client.payOrder(order.id); // state: funded
order = await client.waitForOrder(order.id, { states: ['funded', 'settled'] });
await install(client, 'acme/invoice-extractor', { target: 'claude-code', dest: process.cwd() });CLI
rf orders create --plan pln_goalinvoice2 --rail simulated
rf orders pay ord_tqjv5mw3k2na # shows the summary and asks "Proceed? [y/N]"
rf orders pay ord_tqjv5mw3k2na --yes --max-spend 15 # no terminal: --yes alone never pays, it needs a ceiling too
rf install acme/invoice-extractor --buy # the same in one commandThe last step, accepting, releases the escrow to the seller and cannot be undone. It needs the orders:accept scope and the body flag confirm: true, and only a signed-in person can grant that scope to a token. An autonomous agent can buy, but it cannot declare itself satisfied. If you do nothing, the acceptance window (7 days by default) ends and the money is released anyway; if something is wrong, dispute_order freezes the escrow and needs only orders:create.
curl
curl -X POST $API/orders/ord_tqjv5mw3k2na/accept -H "$AUTH" -H "Content-Type: application/json" \
-d '{"confirm": true, "note": "Tested against 400 invoices."}'Python
order = client.accept_order(order["id"], confirm=True, note="Tested against 400 invoices.") # needs orders:acceptJavaScript
order = await client.acceptOrder(order.id, { confirm: true, note: 'Tested against 400 invoices.' }); // needs orders:acceptCLI
rf orders accept ord_tqjv5mw3k2na # on a terminal: type the order id to confirm
rf orders accept ord_tqjv5mw3k2na --yes --confirm ord_tqjv5mw3k2na # without a terminalMoney rules the CLI adds on top of the API are listed in the CLI reference.
Where next
- Authentication and scopes: scopes, expiry, and how to give an agent a token that can only spend what you allow.
- Webhooks: get told when an order is funded or delivered instead of polling.
- Operation reference: every operation with parameters, errors and examples.
- Hosted MCP server: give an AI client these operations as tools.
- Publishing: put your own skill, harness or MCP server on ReuseForge.
Last updated: