# API Reference

The authoritative route/schema document is [`api/openapi.json`](../api/openapi.json), served by the sandbox at `/v1/openapi.json`. The API is a local prototype; there is no public API service or generated SDK.

## Session, roles and idempotency

The ordinary API authenticates opaque bearer sessions and enforces action/object role checks. The sandbox harness provisions fixed test personas only when started with `npm run sandbox`, which binds to loopback. Harness-only `/__sandbox/` routes are not part of the ordinary API contract or OpenAPI specification.

Mutations require an `Idempotency-Key` of 8–128 permitted characters. An identical request retry returns its original result; reusing the key with a different request fingerprint is rejected. Idempotency does not grant authorization or make a failed/unauthorized operation successful.

## Route groups

| Route | Typical permitted persona | Purpose |
|---|---|---|
| `GET /v1/health`, `GET /v1/openapi.json` | Public | Health and API contract |
| `GET /v1/me`, `GET /v1/me/balance` | Authenticated; balance is scoped to buyer/merchant | Session identity and own test balance |
| `POST /v1/payments`, `GET /v1/payments/{id}` | Provider creates; permitted participants read | Create/read a simulated payment |
| `POST /v1/payments/{id}/accept`, `/authorize`, `/reserve` | Buyer/merchant/provider according to transition | Accept terms, authorize and reserve coverage |
| `POST /v1/provider-messages` | Provider | Submit a signed provider message |
| `POST /v1/withdrawals` | Merchant | Move available simulated balance to external test account |
| `POST /v1/payments/{id}/claims` and `/claim/*` | Buyer, merchant, support or assigned reviewer by action | Claim evidence, response, assignment, triage, vote and appeal |
| `GET /v1/pools/{id}` | Owning pool operator | Read total, available and reserved test coverage |
| `GET /v1/reconciliation` | Scoped local pool operator | Read-only integrity report |

The exact request schemas and status/error responses are in the OpenAPI document and `src/api/contract.js`. Calls with an unknown route, invalid JSON, expired session, wrong role, invalid state, insufficient coverage or conflicting idempotency key return explicit errors; clients must not infer success from a UI state.

## Important boundary

The opt-in Gate C fixture uses trusted backend `localVault` configuration. Vault/token/wallet addresses and confirmations are included in the hashed terms; callers cannot inject or replace them in an HTTP offer. For these payments the optional read-only `execution` field reports `LOCAL_MOCK_VAULT`, chain ID 31337, pending/confirmed/manual-review evidence and the confirmed transaction hash. A signed provider message alone cannot settle the payment. Consult a fresh GET for current state; repeated POSTs return their original idempotent response.

Run `npm.cmd run relay:demo` for the separate local API/SQLite/Hardhat lifecycle. Payment, dispute and final refund/release each expose pending/confirmed evidence; financial completion waits for matching receipts. Chain-bound merchant balances are TEST-USD mirror credits, not live token-wallet balances; simulated withdrawal is disabled in this mode. The pool operator's reconciliation route additionally reads the vault. See [worker and signed recovery operations](local-vault-operations.md). The normal browser sandbox remains simulated.

There are no browser provider secrets, unrestricted financial admin controls or anonymous pool mutations. Do not expose the sandbox routes or use fixed personas as a production identity system.

