Gating features in a client
A client that renders the whole product to everyone and lets the server refuse will show users buttons that do not work. Two endpoints tell a client what to show before the user taps anything.
Surfaces — what to show
GET /api/v1/feature-flags returns surfaces: the state of each
product surface for this caller, already resolved from the plan and the
rollout together.
Render surfaces as given rather than working it out from the plan.
surface_reasons and limits carry the words to show, worded for the
client named in the X-Finput-Client header, so a client never writes
its own.
Fetch this once on launch so gated surfaces render correctly on first paint instead of flickering through defaults.
The response also carries a flags object. Flag names are not a public
contract and change without notice; gate on surfaces, not on flags.
Entitlements — what the plan allows
GET /api/v1/billing/entitlements returns the resolved plan
entitlements plus current usage:
null on a cap means unlimited. Guard the usage lookup:
usage.calc_runs is not guaranteed to be present.
Re-read this after a plan change, and refresh the usage block after
each calculator run rather than counting locally — the caps are
enforced server-side and a local count will drift between devices.
Neither key set is frozen. Plans gain entitlement keys and products gain
surfaces, so read a key that is absent as its safe default — false,
0, or hidden — rather than treating the response as an exhaustive
enum.
The server enforces every gate on the request itself, whatever the
client shows: a refused request answers 403 with feature_disabled
or entitlement_required, and a spent daily allowance answers 429
with cap_hit.