How to read a TSR response
Every route under /standard/v1/... returns a flat JSON object (not the data/error/meta envelope used elsewhere in the REST API; see Response envelope). A handful of fields repeat across almost every route, and they're what tells you whether a number is safe to act on. Read this once and the rest of the OpenAPI spec gets much faster to skim.
Freshness: serving_state, lag_blocks, as_of_block
as_of_block: the Robinhood Chain block height the response reflects. Two calls with differentas_of_blockvalues aren't necessarily comparable, especially across the compositedecisions/*routes, where each part can land on a slightly different block (seeparts_as_of_blocksbelow).serving_state:fresh(the indexer is caught up to chain head),lagging(behind but still serving its last good read, usually with aWarningresponse header), orwithheld(too stale to serve at all, so you getHTTP 503instead of a wrong number).lag_blocks: how many blocks behind head the served data is. Zero or small onfresh, larger onlagging.
Check serving_state before treating a number as current, the same way you'd check a timestamp on any cached value.
Composite routes: one response, several as-of-blocks
The decisions/branch, decisions/exit, and decisions/charter routes each bundle several underlying routes into one call. Every part is reported under parts.<name> with its own status (ok, not_ready, or error), so one part failing never fails the whole response: you get partial data plus a clear reason for the gap, not a single opaque 500.
The top-level as_of_block on a composite is the minimum as_of_block across every part that succeeded: the one block height every part in the response is simultaneously valid through. Each part's own as_of_block is also broken out under parts_as_of_blocks, so you can see how wide the actual spread is if you need finer granularity than the conservative top-level figure.
How sure the API is: basis
basis says where a value came from, in decreasing order of directness:
-
measured: read directly off a contract call or event log. The strongest claim the API makes. -
derived: computed from one or more measured values (the exit-pressure fee curve, a backing ratio, a buyback capacity). -
constant: a fixed parameter read once from chain and cached (a floor, a cap, a rate). -
inferred_from_bytecode: the underlying rule isn't in a verified source file for the contract (none of the 12 Standard Reserve contracts have verified source as of 2026-09-15), so the rule was pinned by running the real bytecode against controlled inputs on a fork, rather than read out of Solidity. It's the deployed code actually executing, but only over the cases exercised. Seedocs/tsr/BYTECODE_SEMANTICS_2026-09-15.mdin thesre-servicesrepo for the method and confidence level behind each one. Any field built this way carries abasis: inferred_from_bytecode(or_basis/assumptions[]entry prefixedbasis inferred_from_bytecode:) so it's never confused for ameasuredread of the same contract.Three rules the API depends on were pinned this way:
- Launch-tax decay (
GET /v1/tax/schedule): the excess over the floor rate halves every 4 minutes and the result is truncated, not rounded; it isn't a whole-rate decay. - Policy multiplier step rule (
GET /v1/policy/outlook): a zero net-flow signal holds the multiplier unchanged (it's neither a raise nor a cut), and no cut can fire at the very first epoch settlement regardless of signal. - License auction next-day opening price (
GET /v1/auctions/current,/v1/auctions/days):max(start_multiplier × yesterday's last sale, today's floor price)if a sale happened the prior day, otherwisestart_multiplier × today's floor price.
All three are pending confirmation against the contracts' own on-chain behavior at the first real epoch settlement (~2026-09-18 01:00Z); until then they're the best available reconstruction, not a verified read.
- Launch-tax decay (
formula_version gates
Several derived numbers (the exit-pressure fee curve most importantly) are gated on a formula_version or exit_formula_version value in the response. This exists because the exit-fee mechanics were rewritten mid-launch once the mainnet formula was validated against the contract's own previewResolutionFeeWad. The version string tells you which formula produced the number you're looking at, so a cached client-side assumption about the shape of the math doesn't silently go stale. Don't hardcode the formula version and compare it as a boolean "is this new enough"; treat it as an opaque label and re-read the field it accompanies each time.
Null means "couldn't read it," not zero
An unreadable or not-yet-settled value is JSON null. It is never coerced to 0 to fill a gap. GET /v1/policy/current is the clearest example: before the Central Bank contract settles an epoch, regime and issuance_raw are null (not "expansion" or 0) and the response instead offers regime_provisional and issuance_per_day_raw as clearly-labeled live estimates, each with its own _source field naming where it came from ("constants" for a live read, "epochs" once the epoch has settled). If you see a 0 in one of these fields, the contract measured a real zero, not a missing read.
Projections are labeled, never presented as predictions
Anything forward-looking carries a projection field naming exactly what it holds constant, plus an assumptions[] array spelling out the assumption in full sentences:
current_sign_holds(GET /v1/policy/outlook): what the multiplier and issuance rate would be if this epoch's net-flow sign doesn't change before it closes.current_stream_rate_holds(GET /v1/issuance/runway): when the issuance budget runs out if the current per-second rate doesn't change.no_further_exits(GET /v1/exit/fee-forecast): how the system-wide exit fee would drift down if nobody exits between now and then.
None of these are forecasts of what will happen. They're "if nothing changes" projections computed from the same measured and derived fields as everything else, so the label and the held-constant assumption are exactly as important as the number itself. Use "current-pace estimate" or "days of issuance" when you describe them; never ROI, APY, payback period, or "opportunity."
_raw fields are 18-decimal integers, as strings
Any field ending in _raw is a uint256-range integer serialized as a JSON string (so it survives round-tripping through parsers that treat all JSON numbers as 64-bit floats), scaled by 1e18 (a "WAD", the standard 18-decimal fixed-point convention this protocol and most Solidity DeFi code use). Divide by 1e18 to get the token or ETH amount as a decimal. A sibling field without the _raw suffix, when present, is the same value already divided down for convenience and should be treated as approximate for display, not for arithmetic.
Estimates carry assumptions[]
Anything computed by simulating a swap through the STANDARD/ETH pool (realizable_eth_raw on an exit quote, the ETH cost of a Branch license, price impact) is marked estimate: true and comes with an assumptions[] array spelling out the approximation: a single-range constant-liquidity treatment of Uniswap v4's concentrated liquidity (it doesn't model the swap crossing into an adjacent initialized tick), and which side of the swap the buy or sell tax was applied to. These are approximations of what a live quote from the contracts would return, not a substitute for one. Don't build an execution path that skips the real transaction simulation because this number looked close enough.