@inflexion/api) is a public, read-only, cached facade over
the protocol’s microstructure. There is no wallet, no signer, and no RPC key for
consumers. It is framework-free node:http — the same style as
the engine — which keeps the whole stack offline-test-friendly.
Its job is to give a browser dApp or a third-party integrator frictionless
public access to the data Inflexion produces, without anyone having to hold a key or
run an indexer. The chain is always the authority; the API is a convenience and data
surface that never settles, matches, or holds a wallet. It is hosted at
https://inflexion-backend.onrender.com (the REST API at /, the engine at /engine).
The routing rule: needs history or an aggregate → subgraph (consumed via this API);
needs frictionless public access with no wallet and no RPC key → this API. If you are
building a wallet-side client that already has an RPC key, prefer
the SDK directly; the API exists for the keyless public path.
The three backings
The API composes three backings, each of which degrades independently. A failure or absence in one never takes down the others — every surface reports its own honest availability.Live RPC
Reuses the SDK’s
DataClient.getCurrentLoadSurface and DepositorClient.getVaultState.
The SDK owns pricing; the API never re-derives the fair rate or load stack. Live.Subgraph
History and aggregates via GraphQL. The subgraph is deployed and indexing, so these
surfaces are served live through this API. If
SUBGRAPH_URL were unset, they would
return a typed pending body with the exact query embedded.Telemetry
Reads the engine’s append-only JSONL sinks (
DEMAND_LOG, COMPETITION_LOG) for the
two dynamic moat halves — files, not chain events, because putting unfilled interest
on-chain would break invariant I7.pending — the API is honest per-backing by construction, not all-or-nothing.
The response envelope: available:true vs typed pending
Every data endpoint returns one of exactly two shapes, and never throws. This is the
single contract a consumer must handle.
available, and on the pending
branch reads a closed enum — never a free-text error string it has to parse.
PendingReason — the closed enum
PendingReason is a closed enum. Every degraded state maps to exactly one of these
five reasons, so client code can switch on them exhaustively.
Why the subgraph routes embed the query.
subgraphPending(reason, detail, query) maps a subgraph outcome to a Pending and embeds the exact GraphQL query
that surface runs. The surface is therefore self-documenting: a consumer can see
precisely what data appears, and a degraded surface is a flip back to live, not a
rewrite. A pending surface is not a stub — it is a fully-built surface that, if its data
source were absent, would report exactly what it is waiting on.SUBGRAPH_URL were
unset — note the embedded query:
Structures ship from day one; the full DYNAMICS (pool-vs-MM spread as a forward-vol signal) mature with multiple competing MMs and volume. The dataset begins at the single redeploy; the off-chain telemetry halves begin at the first interaction logged.What Inflexion offers today is the architecture and the first public view into the microstructure of the DeFi LP volatility-risk premium — not yet a mature dataset:
- Structures are present day-one. All five non-circular behavioral signals are defined and queryable from launch.
- Dynamics mature with volume. The richer signal — the pool-vs-MM spread read as a forward-vol indicator — only sharpens once multiple market makers compete and real flow accumulates.
- The dataset begins at the single redeploy, and the off-chain telemetry halves begin at the first interaction logged. History is served live from the subgraph via the API; if a backing (subgraph URL / RPC / telemetry) is absent, surfaces degrade to a typed
pendingstate — never an error.
The three standing disclosures
Three standing disclosures are attached to the relevant surfaces and are never merged. They map one-to-one to the protocol’s separate claims. On a depositor-facing or payout-facing surface, the disclosure travels with the payload so it can never be stripped in transit.- Claim A — payout / swap / MM surfaces
- Maturity — moat-signal surfaces
CLAIM_A_DISCLOSURE rides every payout, swap, and MM surface
(/swaps, /swaps/:swapId, /mm/:address/fills). The qualifying clause is always
present — the no-bad-debt guarantee is never stated unqualified.Cross-cutting behavior
Every response on every route shares one contract.GET-only — the API is read-only
GET-only — the API is read-only
Non-
GET requests are rejected: 405 { available:false, reason:'method-not-allowed', detail:'read-only API; GET only' }. There is no write path through
this service — mutation only ever happens on-chain. OPTIONS returns 204 with CORS
headers.CORS open — the browser dApp is a different origin
CORS open — the browser dApp is a different origin
Every JSON response carries
access-control-allow-origin: *; the HTML
routes (/docs) do too. The dApp runs on a different origin and must be
able to read these surfaces directly from the browser, with no proxy.bigint-safe wire format — no precision loss
bigint-safe wire format — no precision loss
On-chain quantities (V0, MaxIL, premiums, NAV) routinely exceed
2^53 and cannot survive
a naive JSON.stringify. The API’s serializer uses a replacer that coerces every
bigint → a decimal string (jsonReplacer). Parse these fields with a
bigint-aware reader, not as JS number. See
addresses & units for the decimals (dUSDC = 6, dWETH = 18,
feeds = 8, WAD = 1e18).Caching — short, route-tuned TTLs; errors never cached
Caching — short, route-tuned TTLs; errors never cached
JSON responses send
Cache-Control: public, max-age=5; the /docs HTML sends
max-age=300. Internally a per-route TTL cache (TtlCache) memoises producers, and
errors are explicitly not cached — a transient RPC blip never poisons the cache.
Per-route TTLs:Last-resort 500 guard — the process never crashes
Last-resort 500 guard — the process never crashes
Handlers never throw by design. As a backstop, a stray bug returns
500 { available:false, reason:'internal', detail } rather than taking down the process.
An unmatched route returns 404 { available:false, reason:'not-found', detail:'no route for <path>' }.Environment — what makes each backing live
The API reads its backings from env; absence of any one degrades only that backing’s surfaces to a typedpending, never an error.
Why no silent default RPC. The live SDK clients (
DataClient + DepositorClient)
are constructed only when an RPC url is supplied. With no RPC, the
live endpoints honestly report rpc-unavailable instead of silently falling back to
a default public node and returning numbers from an unknown source. The whole stack is
fully testable offline — mock subgraph, injected fetch, injected telemetry reader,
pre-built SDK clients.A first look — /health tells you what is live
GET /health is the one call that reports which backings are wired right now. It is
[STATIC] — it never depends on a backing being up.
subgraph—trueiffSUBGRAPH_URLis configured (not a reachability proof).live—trueiff an RPC url is wired (the SDK clients exist).telemetry.demand/telemetry.competition—trueiff each JSONL sink path is set.
Self-documenting: OpenAPI + Swagger UI
The endpoint contract is hand-authored OpenAPI 3.1 (no codegen, no network), so it stays offline-CI-safe and is the single source of truth for the contract. It models theavailable / pending discriminated union once and $refs it everywhere.
The live contract is the
/openapi.json document served by this service plus this
documentation.Worked examples — what is live now
These calls work today — they ride the live RPC, subgraph, and telemetry backings. (Use the hosted basehttps://inflexion-backend.onrender.com in place of localhost:8088
to hit the live deployment.)
Where to go next
Full endpoint reference
Every path, query parameter, backing, and return shape — the complete
/markets,
/pool, /pricing, /swaps, /data/*, /mm, and /sigma catalogue.The architecture
How the engine, the subgraph, the SDK, and this API fit around the on-chain core.
The subgraph
The history/aggregate backing — the deployed, indexing source behind the embedded
queries.
The data moat
The five non-circular behavioral signals these surfaces expose.