Skip to main content
The public REST API (@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.
The deliberate split matters: the live RPC surfaces (pool load-surface, pool vault state, pricing preview), the subgraph history/aggregate surfaces, and the telemetry surfaces (latent demand, quote competition) are all live. Because each backing degrades independently, if any one backing were absent only its surfaces would go pending — the API is honest per-backing by construction, not all-or-nothing.
Limit → Why → Mitigation: the SDK is the only place pricing is computed.
  • Limit. The API will not let you compute a fair rate from its own endpoints — it only forwards what the SDK returns.
  • Why. The Φ-sum fair rate is computed in exactly one place: the Stylus FairValueOracle, surfaced through the SDK. Re-deriving it in the API would create a second implementation that could drift from the on-chain truth — exactly the off-chain-reimplementation failure mode the protocol forbids.
  • Mitigation. The API forwards the SDK row, which already carries fairPremium and poolPremium. One source of truth, no drift. See the published FairValue pillar and the Phi-sum.

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.
There is no third shape. A consumer branches on 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.
A worked example of the pending body a subgraph surface would emit if 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 pending state — 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_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.
LPs are always paid with no bad debt in FULL mode — qualified: capped payoff + solvent USDC + oracle/settlement liveness.The no-bad-debt guarantee is exact only under the full clause:
  • FULL collateralization — the underwriter has posted exactly MaxIL per swap, locked for the life of the position.
  • Capped payoff — the payout is min(realized_IL, MaxIL). The cap is load-bearing: it is what makes payout ≤ collateral hold by construction.
  • Solvent USDC — the collateral numeraire (dUSDC) holds its peg and remains redeemable.
  • Oracle / settlement liveness — the price oracle stays live and settle() runs within the protocol’s settlement window.
  • No rehypothecation breach — locked collateral stays instantly liquid; it is never routed to utilization-gated venues.
Stated without these qualifiers, the guarantee is not correct.
Claim A and Claim B are separate claims — never combine them. LP no-bad-debt (Claim A, qualified) and depositor/MM capital risk (Claim B, both tranches) are distinct. The API enforces the separation at the surface level: /pool is a Claim-B surface; /swaps and /mm are Claim-A surfaces. Merging them would imply depositor capital carries the LP’s no-bad-debt guarantee — it does not. See no-bad-debt and the tranches.

Cross-cutting behavior

Every response on every route shares one contract.
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.
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.
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).
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:
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 typed pending, 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 — true iff SUBGRAPH_URL is configured (not a reachability proof).
  • live — true iff an RPC url is wired (the SDK clients exist).
  • telemetry.demand / telemetry.competition — true iff each JSONL sink path is set.
The reading above is the current state: live RPC surfaces, the subgraph, and both telemetry halves are all up. (On the free hosting tier the backend sleeps after ~15 min idle, so the first request after a quiet period incurs a cold start.)

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 the available / 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 base https://inflexion-backend.onrender.com in place of localhost:8088 to hit the live deployment.)
Signal 4 is the only intrinsically split surface. /data/demand-requests returns the realized half from the subgraph (live) and the latent half from the engine’s DEMAND_LOG (live), each with its own available flag — so even if one backing were absent the other half would still serve. This is the clearest illustration of independent per-backing degradation.

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.
For the live deployment addresses these surfaces read against, see the deployments reference. For the full status posture — every pending state with its why and path-forward — see status & caveats.