m2-market/contracts/ledger-api.md

3.4 KiB

m2-ledger API contract v1

S1 owns ledger semantics. Marketplace clients MUST use the /v1/* paths below; older /tx/install, /tx/refund, /license/*, /licenses/check, and /balance/* paths are not contractual for m2-market.

All mutation requests require Idempotency-Key or an idempotency_key body field. If both are present, they must match. All amounts are integer m2cr.

Identity

GET /v1/accounts/me

Returns the caller's resolved ledger identity:

{
  "account_id": "acct_op_chris",
  "operator_id": "op_chris",
  "tenant_id": "m2",
  "scopes": ["ledger:read", "ledger:install"]
}

Balances

GET /v1/balances/{account_or_operator_id}

Returns a balance derived from the append-only transaction log. Store and Scout do not call this directly; they use m2-market wallet.

{
  "account_id": "acct_op_chris",
  "operator_id": "op_chris",
  "tenant_id": "m2",
  "balance": 100,
  "currency": "m2cr",
  "as_of": "2026-07-02T00:00:00Z"
}

Install settlement

POST /v1/settlements/install

Fixed-price install settlement. Job-priced listings are refused by the CLI before this call and use the jobs API instead.

{
  "buyer_operator_id": "op_chris",
  "seller_operator_id": "op_m2core",
  "tenant_id": "m2",
  "listing_id": "lst_mm-pdf-report",
  "solution_id": "sol_mm-pdf-report",
  "solution_version": "1.0.0",
  "major_version": 1,
  "install_ref": "lst_mm-pdf-report-v1.0.0",
  "price": {"model": "fixed", "amount": 25, "currency": "m2cr"},
  "revenue_split": {"platform_pct": 10, "seller_pct": 90}
}

Success returns the transaction group and license grant. The CLI MUST persist tx_group_id in ~/.m2-market/state.json before apply so refund can be addressed by group id.

{
  "tx_group_id": "txg_01j2install",
  "grant_id": "gr_01j2license",
  "balance": 75,
  "license": {
    "operator_id": "op_chris",
    "listing_id": "lst_mm-pdf-report",
    "major_version": 1,
    "status": "active"
  }
}

402 insufficient_funds writes no transactions or grant. Repeated idempotency keys return the original settlement.

Refund on apply failure

POST /v1/settlements/install/{tx_group_id}/refund

Refunds by tx_group_id, not by listing ref. Refunds are compensating append-only rows and mark the related license grant refunded.

{
  "reason": "apply_failed",
  "apply_error": "verify command failed",
  "listing_id": "lst_mm-pdf-report"
}

Licenses

GET /v1/licenses/{operator_id}/{listing_id}?major_version=1

Returns the active/refunded/revoked grant for (operator_id, listing_id, major_version), or 404 for not found or forbidden.

Starter grants

POST /v1/grants/starter

Admin/service scoped. Default amount is 100 m2cr.

Jobs

POST /v1/jobs

Creates a funded job/escrow for job-priced listings. Job settlements use ledger tx_type=job.

Additional job paths:

  • POST /v1/jobs/{job_id}/start
  • POST /v1/jobs/{job_id}/milestones/{milestone_id}/submit
  • POST /v1/jobs/{job_id}/milestones/{milestone_id}/accept
  • POST /v1/jobs/{job_id}/cancel
  • POST /v1/jobs/{job_id}/dispute

Route/resource settlement

POST /v1/settlements/route

Records credit-denominated route/resource rows tied to m2-gpt metering references. It does not modify m2-gpt metering.

Audit snapshots

POST /v1/admin/snapshots

Writes deterministic balance/hash-chain snapshots to Forgejo repo m2/market-audit, not to the registry repo m2/m2-market.