m2-market/contracts/ledger-api.md

139 lines
3.4 KiB
Markdown

# 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:
```json
{
"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`.
```json
{
"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.
```json
{
"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.
```json
{
"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`.
```json
{
"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`.