API
Projects operations
Crowdfunded builds with milestones and revenue units.
Crowdfunded builds with milestones and revenue units. Paths are relative to https://reuseforge.com/api/v1.
| Operation | Method | Path |
|---|---|---|
| List crowdfunded projects | GET | /projects |
| Propose a crowdfunded project | POST | /projects |
| Get a project | GET | /projects/{project_id} |
| Back a project | POST | /projects/{project_id}/backings |
| Withdraw a pledge | DELETE | /projects/{project_id}/backings/{backing_id} |
| Deliver a milestone (developer) | POST | /projects/{project_id}/milestones/{index}/delivery |
| Vote on a milestone (backer) | POST | /projects/{project_id}/milestones/{index}/votes |
| Finalize a milestone vote | POST | /projects/{project_id}/milestones/{index}/finalize |
| Vote to abort a funded project | POST | /projects/{project_id}/abort-votes |
| Transfer revenue units | POST | /projects/{project_id}/transfers |
| Attach the finished product | PUT | /projects/{project_id}/product |
| Project portfolio | GET | /portfolio |
List crowdfunded projects
GET /projects Not enabled on reuseforge.com yet
Public listing of projects with funding progress. Newest first.
| Property | Value |
|---|---|
| Operation ID | list_projects |
| 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_projects(...) |
| JavaScript SDK | client.listProjects(...) |
| CLI | rf projects list |
| MCP tool | list_projects |
| Deployments | reuseforge.com: not enabled yet (answers 501 not_implemented). Python node: implemented. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
state | query | ProjectState | no | |
proposer | query | string | no | Handle of the proposing account. |
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 projects. | application/json: ProjectPage |
| 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/projects'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
for entry in client.list_projects(): # 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.listProjects()) { // iterating fetches further pages on demand
console.log(entry.id);
}CLI
rf projects list
# rf projects list [options]
# all options: rf projects list --helpResponse 200 OK:
{
"data": [
{
"id": "prj_reconharnes2",
"object": "project",
"title": "Invoice reconciliation harness",
"spec": "A harness that matches supplier invoices to purchase orders and flags mismatches, shipped as a reusable skill.",
"deliverables": [
"reconciliation harness",
"evaluation set"
],
"proposer": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"state": "funded",
"goal_cents": 500000,
"raised_cents": 500000,
"pending_cents": 0,
"progress": 1.0,
"currency": "USD",
"created_at": "2026-10-05T08:00:00Z",
"deadline_at": "2026-11-04T08:00:00Z",
"funded_at": "2026-10-18T13:45:00Z",
"developers": [
{
"account": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"budget_bps": 10000,
"equity_bps": 2000
}
],
"milestones": [
{
"index": 0,
"name": "Ingestion pipeline",
"bps": 3000,
"amount_cents": 150000,
"state": "approved",
"evidence": "https://github.com/janedoe/invoice-recon/pull/4",
"submitted_by": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"submitted_at": "2026-10-20T09:00:00Z",
"decided_at": "2026-10-21T15:30:00Z",
"submissions": 1,
"votes": {
"approve_weight": 400000,
"reject_weight": 0,
"total_weight": 500000
}
},
{
"index": 1,
"name": "Reconciliation engine",
"bps": 4000,
"amount_cents": 200000,
"state": "submitted",
"evidence": "https://github.com/janedoe/invoice-recon/pull/9",
"submitted_by": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"submitted_at": "2026-11-02T12:00:00Z",
"decided_at": null,
"submissions": 1,
"votes": {
"approve_weight": 100000,
"reject_weight": 0,
"total_weight": 500000
}
},
{
"index": 2,
"name": "Dashboard and docs",
"bps": 3000,
"amount_cents": 150000,
"state": "pending",
"evidence": null,
"submitted_by": null,
"submitted_at": null,
"decided_at": null,
"submissions": 0,
"votes": {
"approve_weight": 0,
"reject_weight": 0,
"total_weight": 500000
}
}
],
"cap_table": [
{
"holder": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"units": 200000000000,
"ownership_bps": 2000
},
{
"holder": {
"id": "acc_samdevuser22",
"type": "user",
"handle": "samdev",
"display_name": "Sam Okafor",
"verified": false,
"avatar_url": ""
},
"units": 480000000000,
"ownership_bps": 4800
},
{
"holder": {
"id": "acc_acmelabsorg2",
"type": "org",
"handle": "acme",
"display_name": "Acme Labs",
"verified": true,
"avatar_url": ""
},
"units": 320000000000,
"ownership_bps": 3200
}
],
"total_units": 1000000000000,
"dividends_cents": 0,
"product": null,
"share_token": null
}
],
"next_cursor": null,
"has_more": false
}Propose a crowdfunded project
POST /projects Not enabled on reuseforge.com yet
Opens a project: backers fund a goal, developers are hired for the deliverables, backers receive revenue units in proportion to their pledge and developers a pre-agreed share capped at 5000 bps. Developer budget_bps must total 10000 and milestone bps must total 10000. Milestones are approved by pledge-weighted vote and paid from escrow. Revenue units may be regulated securities where you live; payments run on the simulated or test rails until the operator enables real ones.
| Property | Value |
|---|---|
| Operation ID | create_project |
| Authentication | Bearer token holding projects: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.create_project(...) |
| JavaScript SDK | client.createProject(...) |
| CLI | rf projects create |
| MCP tool | create_project |
| 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 ProjectCreate
| Field | Type | Required | Description |
|---|---|---|---|
title | string | yes | |
spec | string | no | |
deliverables | array of string | yes | |
goal_cents | integer | yes | |
deadline_days | number | no | Default 30. |
developers | array of object | yes | |
developers[].account | string | yes | Handle or account id of the developer. |
developers[].budget_bps | integer | yes | |
developers[].equity_bps | integer | yes | |
milestones | array of object | yes | |
milestones[].name | string | yes | |
milestones[].bps | integer | yes |
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 Created | The open project. | application/json: Project |
| 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 POST 'https://reuseforge.com/api/v1/projects' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"title": "Invoice reconciliation harness",
"spec": "A harness that matches supplier invoices to purchase orders and flags mismatches, shipped as a reusable skill.",
"deliverables": [
"reconciliation harness",
"evaluation set"
],
"goal_cents": 500000,
"deadline_days": 30,
"developers": [
{
"account": "janedoe",
"budget_bps": 10000,
"equity_bps": 2000
}
],
"milestones": [
{
"name": "Ingestion pipeline",
"bps": 3000
},
{
"name": "Reconciliation engine",
"bps": 4000
},
{
"name": "Dashboard and docs",
"bps": 3000
}
]
}'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.create_project(
title="Invoice reconciliation harness",
spec="A harness that matches supplier invoices to purchase orders and flags mismatches, shipped as a reusable skill.",
deliverables=["reconciliation harness", "evaluation set"],
goal_cents=500000,
deadline_days=30,
developers=[{"account": "janedoe", "budget_bps": 10000, "equity_bps": 2000}],
milestones=[
{"name": "Ingestion pipeline", "bps": 3000},
{"name": "Reconciliation engine", "bps": 4000},
{"name": "Dashboard and docs", "bps": 3000},
],
)
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.createProject({
title: "Invoice reconciliation harness",
spec: "A harness that matches supplier invoices to purchase orders and flags mismatches, shipped as a reusable skill.",
deliverables: ["reconciliation harness", "evaluation set"],
goal_cents: 500000,
deadline_days: 30,
developers: [{ account: "janedoe", budget_bps: 10000, equity_bps: 2000 }],
milestones: [
{ name: "Ingestion pipeline", bps: 3000 },
{ name: "Reconciliation engine", bps: 4000 },
{ name: "Dashboard and docs", bps: 3000 },
],
});
console.log(result.id);CLI
rf projects create --title 'Invoice reconciliation harness' --spec 'A harness that matches supplier invoices to purchase orders and flags mismatches, shipped as a reusable skill.' --goal 5000 --deadline-days 30
# rf projects create [options]
# all options: rf projects create --helpResponse 201 Created:
{
"id": "prj_reconharnes2",
"object": "project",
"title": "Invoice reconciliation harness",
"spec": "A harness that matches supplier invoices to purchase orders and flags mismatches, shipped as a reusable skill.",
"deliverables": [
"reconciliation harness",
"evaluation set"
],
"proposer": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"state": "open",
"goal_cents": 500000,
"raised_cents": 0,
"pending_cents": 0,
"progress": 0.0,
"currency": "USD",
"created_at": "2026-10-05T08:00:00Z",
"deadline_at": "2026-11-04T08:00:00Z",
"funded_at": null,
"developers": [
{
"account": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"budget_bps": 10000,
"equity_bps": 2000
}
],
"milestones": [
{
"index": 0,
"name": "Ingestion pipeline",
"bps": 3000,
"amount_cents": 150000,
"state": "pending",
"evidence": null,
"submitted_by": null,
"submitted_at": null,
"decided_at": null,
"submissions": 0,
"votes": {
"approve_weight": 0,
"reject_weight": 0,
"total_weight": 0
}
},
{
"index": 1,
"name": "Reconciliation engine",
"bps": 4000,
"amount_cents": 200000,
"state": "pending",
"evidence": null,
"submitted_by": null,
"submitted_at": null,
"decided_at": null,
"submissions": 0,
"votes": {
"approve_weight": 0,
"reject_weight": 0,
"total_weight": 0
}
},
{
"index": 2,
"name": "Dashboard and docs",
"bps": 3000,
"amount_cents": 150000,
"state": "pending",
"evidence": null,
"submitted_by": null,
"submitted_at": null,
"decided_at": null,
"submissions": 0,
"votes": {
"approve_weight": 0,
"reject_weight": 0,
"total_weight": 0
}
}
],
"cap_table": [
{
"holder": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"units": 200000000000,
"ownership_bps": 2000
}
],
"total_units": 1000000000000,
"dividends_cents": 0,
"product": null,
"share_token": null
}Get a project
GET /projects/{project_id} Not enabled on reuseforge.com yet
Funding state, milestones with vote tallies, the cap table and dividends.
| Property | Value |
|---|---|
| Operation ID | get_project |
| 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_project(...) |
| JavaScript SDK | client.getProject(...) |
| CLI | rf projects get |
| MCP tool | get_project |
| Deployments | reuseforge.com: not enabled yet (answers 501 not_implemented). Python node: implemented. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_id | path | string | yes |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The project. | application/json: Project |
| 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/projects/prj_reconharnes2'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.get_project("prj_reconharnes2")
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.getProject("prj_reconharnes2");
console.log(result.id);CLI
rf projects get prj_reconharnes2
# rf projects get <project_id>
# all options: rf projects get --helpResponse 200 OK:
{
"id": "prj_reconharnes2",
"object": "project",
"title": "Invoice reconciliation harness",
"spec": "A harness that matches supplier invoices to purchase orders and flags mismatches, shipped as a reusable skill.",
"deliverables": [
"reconciliation harness",
"evaluation set"
],
"proposer": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"state": "funded",
"goal_cents": 500000,
"raised_cents": 500000,
"pending_cents": 0,
"progress": 1.0,
"currency": "USD",
"created_at": "2026-10-05T08:00:00Z",
"deadline_at": "2026-11-04T08:00:00Z",
"funded_at": "2026-10-18T13:45:00Z",
"developers": [
{
"account": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"budget_bps": 10000,
"equity_bps": 2000
}
],
"milestones": [
{
"index": 0,
"name": "Ingestion pipeline",
"bps": 3000,
"amount_cents": 150000,
"state": "approved",
"evidence": "https://github.com/janedoe/invoice-recon/pull/4",
"submitted_by": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"submitted_at": "2026-10-20T09:00:00Z",
"decided_at": "2026-10-21T15:30:00Z",
"submissions": 1,
"votes": {
"approve_weight": 400000,
"reject_weight": 0,
"total_weight": 500000
}
},
{
"index": 1,
"name": "Reconciliation engine",
"bps": 4000,
"amount_cents": 200000,
"state": "submitted",
"evidence": "https://github.com/janedoe/invoice-recon/pull/9",
"submitted_by": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"submitted_at": "2026-11-02T12:00:00Z",
"decided_at": null,
"submissions": 1,
"votes": {
"approve_weight": 100000,
"reject_weight": 0,
"total_weight": 500000
}
},
{
"index": 2,
"name": "Dashboard and docs",
"bps": 3000,
"amount_cents": 150000,
"state": "pending",
"evidence": null,
"submitted_by": null,
"submitted_at": null,
"decided_at": null,
"submissions": 0,
"votes": {
"approve_weight": 0,
"reject_weight": 0,
"total_weight": 500000
}
}
],
"cap_table": [
{
"holder": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"units": 200000000000,
"ownership_bps": 2000
},
{
"holder": {
"id": "acc_samdevuser22",
"type": "user",
"handle": "samdev",
"display_name": "Sam Okafor",
"verified": false,
"avatar_url": ""
},
"units": 480000000000,
"ownership_bps": 4800
},
{
"holder": {
"id": "acc_acmelabsorg2",
"type": "org",
"handle": "acme",
"display_name": "Acme Labs",
"verified": true,
"avatar_url": ""
},
"units": 320000000000,
"ownership_bps": 3200
}
],
"total_units": 1000000000000,
"dividends_cents": 0,
"product": null,
"share_token": null
}Back a project
POST /projects/{project_id}/backings Not enabled on reuseforge.com yet
Pledges money to an open project. Pledges cannot exceed the remaining goal; the pledge that reaches the goal flips the project to funded. Spends money, so the token needs orders:pay and the amount counts against its spend limit. Pledges can be withdrawn with withdraw_backing until the project is funded. Idempotency-Key is required.
| Property | Value |
|---|---|
| Operation ID | back_project |
| Authentication | Bearer token holding projects:write and orders:pay (all are required) |
| Rate tier | payments: not allowed anonymous, 30/min per token with a token |
| Idempotency | Idempotency-Key header, required. Money moves here, so it is not optional. |
| Python SDK | client.back_project(...) |
| JavaScript SDK | client.backProject(...) |
| CLI | rf projects back |
| MCP tool | back_project |
| Deployments | reuseforge.com: not enabled yet (answers 501 not_implemented). Python node: implemented. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_id | path | string | yes | |
Idempotency-Key | header | string | yes | Required here because the call moves or commits money. Same semantics as the optional header; omitting it returns 400 idempotency_key_required. |
Request body
application/json, required, schema BackProject
| Field | Type | Required | Description |
|---|---|---|---|
amount_cents | integer | yes | |
rail | PaymentRail | no | Open set. Known: simulated, base-usdc, base-usdt, btc. Python nodes also use usdc-base and usdt-ethereum. |
tx_ref | string | no | |
wallet | string | no |
Another body: onchain
{
"amount_cents": 300000,
"rail": "base-usdc",
"tx_ref": "0x709b55bd3da0f5a838125bd0ee20c5bfdd7caba173912d4281cae816b79a201b",
"wallet": "0x1D6c2b7A0E5a3C47bC8D90e5F0A2c1b3d4E5f6A7"
}Responses
| Status | Meaning | Body |
|---|---|---|
| 201 Created | The backing. | application/json: Backing |
| 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 |
| 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/projects/prj_reconharnes2/backings' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"amount_cents": 300000,
"rail": "simulated"
}'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.back_project(
"prj_reconharnes2",
amount_cents=300000,
rail="simulated",
)
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.backProject("prj_reconharnes2", { amount_cents: 300000, rail: "simulated" });
console.log(result.id);CLI
rf projects back prj_reconharnes2 --amount 3000 --rail simulated
# rf projects back <project_id> [options]
# all options: rf projects back --helpResponse 201 Created:
{
"id": "bck_samreconpldg",
"object": "backing",
"project_id": "prj_reconharnes2",
"backer": {
"id": "acc_samdevuser22",
"type": "user",
"handle": "samdev",
"display_name": "Sam Okafor",
"verified": false,
"avatar_url": ""
},
"amount_cents": 300000,
"currency": "USD",
"state": "confirmed",
"units": 480000000000,
"payment": {
"rail": "simulated",
"network": null,
"asset": "USD",
"status": "confirmed",
"amount_cents": 300000,
"amount_atomic": "300000",
"address": "escrow:rb-hosted",
"memo": "bck_samreconpldg",
"uri": null,
"tx_ref": "sim_tx_5d1f08a9c3e2",
"confirmations": 1,
"required_confirmations": 1,
"expires_at": null,
"refund_address": null,
"simulated": true
},
"created_at": "2026-10-12T17:20:00Z"
}Withdraw a pledge
DELETE /projects/{project_id}/backings/{backing_id} Not enabled on reuseforge.com yet
Returns a pledge to the backer and burns its units. Only while the project is open. Idempotent.
| Property | Value |
|---|---|
| Operation ID | withdraw_backing |
| Authentication | Bearer token holding projects:write |
| Rate tier | payments: not allowed anonymous, 30/min per token with a token |
| Python SDK | client.withdraw_backing(...) |
| JavaScript SDK | client.withdrawBacking(...) |
| CLI | rf projects withdraw |
| MCP tool | withdraw_backing |
| Deployments | reuseforge.com: not enabled yet (answers 501 not_implemented). Python node: not implemented (answers 501). |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_id | path | string | yes | |
backing_id | path | string | yes |
Responses
| Status | Meaning | Body |
|---|---|---|
| 204 No Content | Withdrawn. | no body |
| 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 |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
Example
curl
curl -X DELETE 'https://reuseforge.com/api/v1/projects/prj_reconharnes2/backings/bck_h4m6t1y9d2z8' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN"Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
client.withdraw_backing("prj_reconharnes2", "bck_h4m6t1y9d2z8")JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
await client.withdrawBacking("prj_reconharnes2", "bck_h4m6t1y9d2z8");CLI
rf projects withdraw prj_reconharnes2 bck_h4m6t1y9d2z8
# rf projects withdraw <project_id> <backing_id>
# all options: rf projects withdraw --helpResponse 204 No Content with an empty body.
Deliver a milestone (developer)
POST /projects/{project_id}/milestones/{index}/delivery Not enabled on reuseforge.com yet
A developer submits evidence (a link, commit or release) for a milestone. A new submission restarts the vote. A rejected milestone may be resubmitted at most three times.
| Property | Value |
|---|---|
| Operation ID | deliver_milestone |
| Authentication | Bearer token holding projects: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.deliver_milestone(...) |
| JavaScript SDK | client.deliverMilestone(...) |
| CLI | rf projects deliver |
| MCP tool | deliver_milestone |
| Deployments | reuseforge.com: not enabled yet (answers 501 not_implemented). Python node: implemented. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_id | path | string | yes | |
index | path | integer | yes | Zero-based milestone index. |
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 DeliverMilestone
| Field | Type | Required | Description |
|---|---|---|---|
evidence | string | yes |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The project with the submitted milestone. | application/json: Project |
| 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 |
| 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/projects/prj_reconharnes2/milestones/0/delivery' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"evidence": "https://github.com/janedoe/invoice-recon/pull/9"
}'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.deliver_milestone(
"prj_reconharnes2",
0,
evidence="https://github.com/janedoe/invoice-recon/pull/9",
)
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.deliverMilestone("prj_reconharnes2", 0, { evidence: "https://github.com/janedoe/invoice-recon/pull/9" });
console.log(result.id);CLI
rf projects deliver prj_reconharnes2 0 --evidence https://github.com/janedoe/invoice-recon/pull/9
# rf projects deliver <project_id> <index> [options]
# all options: rf projects deliver --helpResponse 200 OK:
{
"id": "prj_reconharnes2",
"object": "project",
"title": "Invoice reconciliation harness",
"spec": "A harness that matches supplier invoices to purchase orders and flags mismatches, shipped as a reusable skill.",
"deliverables": [
"reconciliation harness",
"evaluation set"
],
"proposer": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"state": "funded",
"goal_cents": 500000,
"raised_cents": 500000,
"pending_cents": 0,
"progress": 1.0,
"currency": "USD",
"created_at": "2026-10-05T08:00:00Z",
"deadline_at": "2026-11-04T08:00:00Z",
"funded_at": "2026-10-18T13:45:00Z",
"developers": [
{
"account": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"budget_bps": 10000,
"equity_bps": 2000
}
],
"milestones": [
{
"index": 0,
"name": "Ingestion pipeline",
"bps": 3000,
"amount_cents": 150000,
"state": "approved",
"evidence": "https://github.com/janedoe/invoice-recon/pull/4",
"submitted_by": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"submitted_at": "2026-10-20T09:00:00Z",
"decided_at": "2026-10-21T15:30:00Z",
"submissions": 1,
"votes": {
"approve_weight": 400000,
"reject_weight": 0,
"total_weight": 500000
}
},
{
"index": 1,
"name": "Reconciliation engine",
"bps": 4000,
"amount_cents": 200000,
"state": "submitted",
"evidence": "https://github.com/janedoe/invoice-recon/pull/9",
"submitted_by": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"submitted_at": "2026-11-02T12:00:00Z",
"decided_at": null,
"submissions": 1,
"votes": {
"approve_weight": 100000,
"reject_weight": 0,
"total_weight": 500000
}
},
{
"index": 2,
"name": "Dashboard and docs",
"bps": 3000,
"amount_cents": 150000,
"state": "pending",
"evidence": null,
"submitted_by": null,
"submitted_at": null,
"decided_at": null,
"submissions": 0,
"votes": {
"approve_weight": 0,
"reject_weight": 0,
"total_weight": 500000
}
}
],
"cap_table": [
{
"holder": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"units": 200000000000,
"ownership_bps": 2000
},
{
"holder": {
"id": "acc_samdevuser22",
"type": "user",
"handle": "samdev",
"display_name": "Sam Okafor",
"verified": false,
"avatar_url": ""
},
"units": 480000000000,
"ownership_bps": 4800
},
{
"holder": {
"id": "acc_acmelabsorg2",
"type": "org",
"handle": "acme",
"display_name": "Acme Labs",
"verified": true,
"avatar_url": ""
},
"units": 320000000000,
"ownership_bps": 3200
}
],
"total_units": 1000000000000,
"dividends_cents": 0,
"product": null,
"share_token": null
}Vote on a milestone (backer)
POST /projects/{project_id}/milestones/{index}/votes Not enabled on reuseforge.com yet
Backers approve or reject a submitted milestone, weighted by pledge amount. More than half of the goal in approving weight approves at once. A backer can change their vote until the milestone is decided.
| Property | Value |
|---|---|
| Operation ID | vote_milestone |
| Authentication | Bearer token holding projects: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.vote_milestone(...) |
| JavaScript SDK | client.voteMilestone(...) |
| CLI | rf projects vote |
| MCP tool | vote_milestone |
| Deployments | reuseforge.com: not enabled yet (answers 501 not_implemented). Python node: implemented. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_id | path | string | yes | |
index | path | integer | yes | Zero-based milestone index. |
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 VoteMilestone
| Field | Type | Required | Description |
|---|---|---|---|
approve | boolean | yes |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The project with updated tallies. | application/json: Project |
| 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 |
| 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/projects/prj_reconharnes2/milestones/0/votes' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"approve": true
}'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.vote_milestone("prj_reconharnes2", 0, approve=True)
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.voteMilestone("prj_reconharnes2", 0, { approve: true });
console.log(result.id);CLI
rf projects vote prj_reconharnes2 0 --approve
# rf projects vote <project_id> <index> [options]
# all options: rf projects vote --helpResponse 200 OK:
{
"id": "prj_reconharnes2",
"object": "project",
"title": "Invoice reconciliation harness",
"spec": "A harness that matches supplier invoices to purchase orders and flags mismatches, shipped as a reusable skill.",
"deliverables": [
"reconciliation harness",
"evaluation set"
],
"proposer": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"state": "funded",
"goal_cents": 500000,
"raised_cents": 500000,
"pending_cents": 0,
"progress": 1.0,
"currency": "USD",
"created_at": "2026-10-05T08:00:00Z",
"deadline_at": "2026-11-04T08:00:00Z",
"funded_at": "2026-10-18T13:45:00Z",
"developers": [
{
"account": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"budget_bps": 10000,
"equity_bps": 2000
}
],
"milestones": [
{
"index": 0,
"name": "Ingestion pipeline",
"bps": 3000,
"amount_cents": 150000,
"state": "approved",
"evidence": "https://github.com/janedoe/invoice-recon/pull/4",
"submitted_by": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"submitted_at": "2026-10-20T09:00:00Z",
"decided_at": "2026-10-21T15:30:00Z",
"submissions": 1,
"votes": {
"approve_weight": 400000,
"reject_weight": 0,
"total_weight": 500000
}
},
{
"index": 1,
"name": "Reconciliation engine",
"bps": 4000,
"amount_cents": 200000,
"state": "submitted",
"evidence": "https://github.com/janedoe/invoice-recon/pull/9",
"submitted_by": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"submitted_at": "2026-11-02T12:00:00Z",
"decided_at": null,
"submissions": 1,
"votes": {
"approve_weight": 100000,
"reject_weight": 0,
"total_weight": 500000
}
},
{
"index": 2,
"name": "Dashboard and docs",
"bps": 3000,
"amount_cents": 150000,
"state": "pending",
"evidence": null,
"submitted_by": null,
"submitted_at": null,
"decided_at": null,
"submissions": 0,
"votes": {
"approve_weight": 0,
"reject_weight": 0,
"total_weight": 500000
}
}
],
"cap_table": [
{
"holder": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"units": 200000000000,
"ownership_bps": 2000
},
{
"holder": {
"id": "acc_samdevuser22",
"type": "user",
"handle": "samdev",
"display_name": "Sam Okafor",
"verified": false,
"avatar_url": ""
},
"units": 480000000000,
"ownership_bps": 4800
},
{
"holder": {
"id": "acc_acmelabsorg2",
"type": "org",
"handle": "acme",
"display_name": "Acme Labs",
"verified": true,
"avatar_url": ""
},
"units": 320000000000,
"ownership_bps": 3200
}
],
"total_units": 1000000000000,
"dividends_cents": 0,
"product": null,
"share_token": null
}Finalize a milestone vote
POST /projects/{project_id}/milestones/{index}/finalize Not enabled on reuseforge.com yet
Anyone may call this after the vote window has elapsed to apply the outcome (more approval than rejection approves; a tie with no votes goes to the arbiter). A no-op before the window ends or after a decision.
| Property | Value |
|---|---|
| Operation ID | finalize_milestone |
| Authentication | Bearer token holding projects: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.finalize_milestone(...) |
| JavaScript SDK | client.finalizeMilestone(...) |
| CLI | rf projects finalize |
| MCP tool | finalize_milestone |
| Deployments | reuseforge.com: not enabled yet (answers 501 not_implemented). Python node: implemented. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_id | path | string | yes | |
index | path | integer | yes | Zero-based milestone index. |
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. |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The project after finalization. | application/json: Project |
| 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 |
| 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/projects/prj_reconharnes2/milestones/0/finalize' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN" \
-H "Idempotency-Key: $(uuidgen)"Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.finalize_milestone("prj_reconharnes2", 0)
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.finalizeMilestone("prj_reconharnes2", 0);
console.log(result.id);CLI
rf projects finalize prj_reconharnes2 0
# rf projects finalize <project_id> <index>
# all options: rf projects finalize --helpResponse 200 OK:
{
"id": "prj_reconharnes2",
"object": "project",
"title": "Invoice reconciliation harness",
"spec": "A harness that matches supplier invoices to purchase orders and flags mismatches, shipped as a reusable skill.",
"deliverables": [
"reconciliation harness",
"evaluation set"
],
"proposer": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"state": "funded",
"goal_cents": 500000,
"raised_cents": 500000,
"pending_cents": 0,
"progress": 1.0,
"currency": "USD",
"created_at": "2026-10-05T08:00:00Z",
"deadline_at": "2026-11-04T08:00:00Z",
"funded_at": "2026-10-18T13:45:00Z",
"developers": [
{
"account": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"budget_bps": 10000,
"equity_bps": 2000
}
],
"milestones": [
{
"index": 0,
"name": "Ingestion pipeline",
"bps": 3000,
"amount_cents": 150000,
"state": "approved",
"evidence": "https://github.com/janedoe/invoice-recon/pull/4",
"submitted_by": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"submitted_at": "2026-10-20T09:00:00Z",
"decided_at": "2026-10-21T15:30:00Z",
"submissions": 1,
"votes": {
"approve_weight": 400000,
"reject_weight": 0,
"total_weight": 500000
}
},
{
"index": 1,
"name": "Reconciliation engine",
"bps": 4000,
"amount_cents": 200000,
"state": "submitted",
"evidence": "https://github.com/janedoe/invoice-recon/pull/9",
"submitted_by": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"submitted_at": "2026-11-02T12:00:00Z",
"decided_at": null,
"submissions": 1,
"votes": {
"approve_weight": 100000,
"reject_weight": 0,
"total_weight": 500000
}
},
{
"index": 2,
"name": "Dashboard and docs",
"bps": 3000,
"amount_cents": 150000,
"state": "pending",
"evidence": null,
"submitted_by": null,
"submitted_at": null,
"decided_at": null,
"submissions": 0,
"votes": {
"approve_weight": 0,
"reject_weight": 0,
"total_weight": 500000
}
}
],
"cap_table": [
{
"holder": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"units": 200000000000,
"ownership_bps": 2000
},
{
"holder": {
"id": "acc_samdevuser22",
"type": "user",
"handle": "samdev",
"display_name": "Sam Okafor",
"verified": false,
"avatar_url": ""
},
"units": 480000000000,
"ownership_bps": 4800
},
{
"holder": {
"id": "acc_acmelabsorg2",
"type": "org",
"handle": "acme",
"display_name": "Acme Labs",
"verified": true,
"avatar_url": ""
},
"units": 320000000000,
"ownership_bps": 3200
}
],
"total_units": 1000000000000,
"dividends_cents": 0,
"product": null,
"share_token": null
}Vote to abort a funded project
POST /projects/{project_id}/abort-votes Not enabled on reuseforge.com yet
Backers holding more than half of the pledges can abort a stalled project; unreleased funds are then refunded pro rata. Set abort: false to withdraw your vote.
| Property | Value |
|---|---|
| Operation ID | vote_abort |
| Authentication | Bearer token holding projects: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.vote_abort(...) |
| JavaScript SDK | client.voteAbort(...) |
| CLI | rf projects abort |
| MCP tool | vote_abort |
| Deployments | reuseforge.com: not enabled yet (answers 501 not_implemented). Python node: not implemented (answers 501). |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_id | path | string | yes | |
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 VoteAbort
| Field | Type | Required | Description |
|---|---|---|---|
abort | boolean | yes |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The project. | application/json: Project |
| 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 |
| 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/projects/prj_reconharnes2/abort-votes' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"abort": true
}'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.vote_abort("prj_reconharnes2", abort=True)
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.voteAbort("prj_reconharnes2", { abort: true });
console.log(result.id);CLI
rf projects abort prj_reconharnes2
# rf projects abort <project_id> [options]
# all options: rf projects abort --helpResponse 200 OK:
{
"id": "prj_reconharnes2",
"object": "project",
"title": "Invoice reconciliation harness",
"spec": "A harness that matches supplier invoices to purchase orders and flags mismatches, shipped as a reusable skill.",
"deliverables": [
"reconciliation harness",
"evaluation set"
],
"proposer": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"state": "funded",
"goal_cents": 500000,
"raised_cents": 500000,
"pending_cents": 0,
"progress": 1.0,
"currency": "USD",
"created_at": "2026-10-05T08:00:00Z",
"deadline_at": "2026-11-04T08:00:00Z",
"funded_at": "2026-10-18T13:45:00Z",
"developers": [
{
"account": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"budget_bps": 10000,
"equity_bps": 2000
}
],
"milestones": [
{
"index": 0,
"name": "Ingestion pipeline",
"bps": 3000,
"amount_cents": 150000,
"state": "approved",
"evidence": "https://github.com/janedoe/invoice-recon/pull/4",
"submitted_by": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"submitted_at": "2026-10-20T09:00:00Z",
"decided_at": "2026-10-21T15:30:00Z",
"submissions": 1,
"votes": {
"approve_weight": 400000,
"reject_weight": 0,
"total_weight": 500000
}
},
{
"index": 1,
"name": "Reconciliation engine",
"bps": 4000,
"amount_cents": 200000,
"state": "submitted",
"evidence": "https://github.com/janedoe/invoice-recon/pull/9",
"submitted_by": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"submitted_at": "2026-11-02T12:00:00Z",
"decided_at": null,
"submissions": 1,
"votes": {
"approve_weight": 100000,
"reject_weight": 0,
"total_weight": 500000
}
},
{
"index": 2,
"name": "Dashboard and docs",
"bps": 3000,
"amount_cents": 150000,
"state": "pending",
"evidence": null,
"submitted_by": null,
"submitted_at": null,
"decided_at": null,
"submissions": 0,
"votes": {
"approve_weight": 0,
"reject_weight": 0,
"total_weight": 500000
}
}
],
"cap_table": [
{
"holder": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"units": 200000000000,
"ownership_bps": 2000
},
{
"holder": {
"id": "acc_samdevuser22",
"type": "user",
"handle": "samdev",
"display_name": "Sam Okafor",
"verified": false,
"avatar_url": ""
},
"units": 480000000000,
"ownership_bps": 4800
},
{
"holder": {
"id": "acc_acmelabsorg2",
"type": "org",
"handle": "acme",
"display_name": "Acme Labs",
"verified": true,
"avatar_url": ""
},
"units": 320000000000,
"ownership_bps": 3200
}
],
"total_units": 1000000000000,
"dividends_cents": 0,
"product": null,
"share_token": null
}Transfer revenue units
POST /projects/{project_id}/transfers Not enabled on reuseforge.com yet
Moves units to another account. Allowed once the project is funded. Dividends already accrued stay with the sender. Idempotency-Key is required.
| Property | Value |
|---|---|
| Operation ID | transfer_units |
| Authentication | Bearer token holding projects:write |
| Rate tier | payments: not allowed anonymous, 30/min per token with a token |
| Idempotency | Idempotency-Key header, required. Money moves here, so it is not optional. |
| Python SDK | client.transfer_units(...) |
| JavaScript SDK | client.transferUnits(...) |
| CLI | rf projects transfer |
| MCP tool | transfer_units |
| Deployments | reuseforge.com: not enabled yet (answers 501 not_implemented). Python node: implemented. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_id | path | string | yes | |
Idempotency-Key | header | string | yes | Required here because the call moves or commits money. Same semantics as the optional header; omitting it returns 400 idempotency_key_required. |
Request body
application/json, required, schema TransferUnits
| Field | Type | Required | Description |
|---|---|---|---|
to | string | yes | Handle or account id of the recipient. |
units | integer | yes |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The project with the new cap table. | application/json: Project |
| 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 |
| 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/projects/prj_reconharnes2/transfers' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"to": "acme",
"units": 10000000000
}'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.transfer_units("prj_reconharnes2", to="acme", units=10000000000)
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.transferUnits("prj_reconharnes2", { to: "acme", units: 10000000000 });
console.log(result.id);CLI
rf projects transfer prj_reconharnes2 --to acme --units 10000000000
# rf projects transfer <project_id> [options]
# all options: rf projects transfer --helpResponse 200 OK:
{
"id": "prj_reconharnes2",
"object": "project",
"title": "Invoice reconciliation harness",
"spec": "A harness that matches supplier invoices to purchase orders and flags mismatches, shipped as a reusable skill.",
"deliverables": [
"reconciliation harness",
"evaluation set"
],
"proposer": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"state": "funded",
"goal_cents": 500000,
"raised_cents": 500000,
"pending_cents": 0,
"progress": 1.0,
"currency": "USD",
"created_at": "2026-10-05T08:00:00Z",
"deadline_at": "2026-11-04T08:00:00Z",
"funded_at": "2026-10-18T13:45:00Z",
"developers": [
{
"account": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"budget_bps": 10000,
"equity_bps": 2000
}
],
"milestones": [
{
"index": 0,
"name": "Ingestion pipeline",
"bps": 3000,
"amount_cents": 150000,
"state": "approved",
"evidence": "https://github.com/janedoe/invoice-recon/pull/4",
"submitted_by": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"submitted_at": "2026-10-20T09:00:00Z",
"decided_at": "2026-10-21T15:30:00Z",
"submissions": 1,
"votes": {
"approve_weight": 400000,
"reject_weight": 0,
"total_weight": 500000
}
},
{
"index": 1,
"name": "Reconciliation engine",
"bps": 4000,
"amount_cents": 200000,
"state": "submitted",
"evidence": "https://github.com/janedoe/invoice-recon/pull/9",
"submitted_by": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"submitted_at": "2026-11-02T12:00:00Z",
"decided_at": null,
"submissions": 1,
"votes": {
"approve_weight": 100000,
"reject_weight": 0,
"total_weight": 500000
}
},
{
"index": 2,
"name": "Dashboard and docs",
"bps": 3000,
"amount_cents": 150000,
"state": "pending",
"evidence": null,
"submitted_by": null,
"submitted_at": null,
"decided_at": null,
"submissions": 0,
"votes": {
"approve_weight": 0,
"reject_weight": 0,
"total_weight": 500000
}
}
],
"cap_table": [
{
"holder": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"units": 200000000000,
"ownership_bps": 2000
},
{
"holder": {
"id": "acc_samdevuser22",
"type": "user",
"handle": "samdev",
"display_name": "Sam Okafor",
"verified": false,
"avatar_url": ""
},
"units": 480000000000,
"ownership_bps": 4800
},
{
"holder": {
"id": "acc_acmelabsorg2",
"type": "org",
"handle": "acme",
"display_name": "Acme Labs",
"verified": true,
"avatar_url": ""
},
"units": 320000000000,
"ownership_bps": 3200
}
],
"total_units": 1000000000000,
"dividends_cents": 0,
"product": null,
"share_token": null
}Attach the finished product
PUT /projects/{project_id}/product Not enabled on reuseforge.com yet
The proposer links the completed project to the item it produced. From then on the item's sales are split across unit holders (after the platform fee). Items attached to a project accept stablecoin rails only.
| Property | Value |
|---|---|
| Operation ID | attach_product |
| Authentication | Bearer token holding projects:write |
| Rate tier | write: not allowed anonymous, 120/min per token with a token |
| Python SDK | client.attach_product(...) |
| JavaScript SDK | client.attachProduct(...) |
| CLI | rf projects attach |
| MCP tool | attach_product |
| Deployments | reuseforge.com: not enabled yet (answers 501 not_implemented). Python node: implemented. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
project_id | path | string | yes |
Request body
application/json, required, schema AttachProduct
| Field | Type | Required | Description |
|---|---|---|---|
item | ItemRef | yes | Canonical item address owner/slug. |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The project with its product. | application/json: Project |
| 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 |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
Example
curl
curl -X PUT 'https://reuseforge.com/api/v1/projects/prj_reconharnes2/product' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"item": "janedoe/invoice-rag-harness"
}'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.attach_product(
"prj_reconharnes2",
item="janedoe/invoice-rag-harness",
)
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.attachProduct("prj_reconharnes2", { item: "janedoe/invoice-rag-harness" });
console.log(result.id);CLI
rf projects attach prj_reconharnes2 --item janedoe/invoice-rag-harness
# rf projects attach <project_id> [options]
# all options: rf projects attach --helpResponse 200 OK:
{
"id": "prj_reconharnes2",
"object": "project",
"title": "Invoice reconciliation harness",
"spec": "A harness that matches supplier invoices to purchase orders and flags mismatches, shipped as a reusable skill.",
"deliverables": [
"reconciliation harness",
"evaluation set"
],
"proposer": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"state": "funded",
"goal_cents": 500000,
"raised_cents": 500000,
"pending_cents": 0,
"progress": 1.0,
"currency": "USD",
"created_at": "2026-10-05T08:00:00Z",
"deadline_at": "2026-11-04T08:00:00Z",
"funded_at": "2026-10-18T13:45:00Z",
"developers": [
{
"account": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"budget_bps": 10000,
"equity_bps": 2000
}
],
"milestones": [
{
"index": 0,
"name": "Ingestion pipeline",
"bps": 3000,
"amount_cents": 150000,
"state": "approved",
"evidence": "https://github.com/janedoe/invoice-recon/pull/4",
"submitted_by": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"submitted_at": "2026-10-20T09:00:00Z",
"decided_at": "2026-10-21T15:30:00Z",
"submissions": 1,
"votes": {
"approve_weight": 400000,
"reject_weight": 0,
"total_weight": 500000
}
},
{
"index": 1,
"name": "Reconciliation engine",
"bps": 4000,
"amount_cents": 200000,
"state": "submitted",
"evidence": "https://github.com/janedoe/invoice-recon/pull/9",
"submitted_by": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"submitted_at": "2026-11-02T12:00:00Z",
"decided_at": null,
"submissions": 1,
"votes": {
"approve_weight": 100000,
"reject_weight": 0,
"total_weight": 500000
}
},
{
"index": 2,
"name": "Dashboard and docs",
"bps": 3000,
"amount_cents": 150000,
"state": "pending",
"evidence": null,
"submitted_by": null,
"submitted_at": null,
"decided_at": null,
"submissions": 0,
"votes": {
"approve_weight": 0,
"reject_weight": 0,
"total_weight": 500000
}
}
],
"cap_table": [
{
"holder": {
"id": "acc_janedoeuser2",
"type": "user",
"handle": "janedoe",
"display_name": "Jane Doe",
"verified": false,
"avatar_url": "https://avatars.githubusercontent.com/u/1000001"
},
"units": 200000000000,
"ownership_bps": 2000
},
{
"holder": {
"id": "acc_samdevuser22",
"type": "user",
"handle": "samdev",
"display_name": "Sam Okafor",
"verified": false,
"avatar_url": ""
},
"units": 480000000000,
"ownership_bps": 4800
},
{
"holder": {
"id": "acc_acmelabsorg2",
"type": "org",
"handle": "acme",
"display_name": "Acme Labs",
"verified": true,
"avatar_url": ""
},
"units": 320000000000,
"ownership_bps": 3200
}
],
"total_units": 1000000000000,
"dividends_cents": 0,
"product": null,
"share_token": null
}Project portfolio
GET /portfolio Not enabled on reuseforge.com yet
Stakes, dividends and net result of an account across projects. Positions are public ledger data; holder defaults to you.
| Property | Value |
|---|---|
| Operation ID | get_portfolio |
| Authentication | Bearer token holding projects:read |
| Rate tier | read: 60/min per IP anonymous, 600/min per token with a token |
| Python SDK | client.get_portfolio(...) |
| JavaScript SDK | client.getPortfolio(...) |
| CLI | rf portfolio |
| MCP tool | get_portfolio |
| Deployments | reuseforge.com: not enabled yet (answers 501 not_implemented). Python node: implemented. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
holder | query | string | no | Handle of the account. Defaults to the caller. |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The portfolio. | application/json: Portfolio |
| 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 |
| 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/portfolio' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN"Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.get_portfolio()
print(result)JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.getPortfolio();
console.log(result);CLI
rf portfolio
# rf portfolio [options]
# all options: rf portfolio --helpResponse 200 OK:
{
"holder": {
"id": "acc_samdevuser22",
"type": "user",
"handle": "samdev",
"display_name": "Sam Okafor",
"verified": false,
"avatar_url": ""
},
"currency": "USD",
"invested_cents": 300000,
"dividends_cents": 0,
"net_cents": -300000,
"positions": [
{
"project_id": "prj_reconharnes2",
"title": "Invoice reconciliation harness",
"state": "funded",
"invested_cents": 300000,
"units": 480000000000,
"ownership_bps": 4800,
"dividends_cents": 0,
"net_cents": -300000
}
]
}