Your app reads flags with one call: evaluate. It answers for the signed-in user and their tenant, with the tenant's changes applied, and says when to ask again. Copy-paste clients for the main frameworks are in the repository under docs/samples/feature-flags-client; they have no dependencies and are small enough to read.

The call

bash
curl -X POST "$BASE/app/$ORG/featureflags/evaluate" \
  -H "x-nostackai-token: $TOKEN" -H "content-type: application/json" \
  -d '{"keys":["new-checkout"],"context":{"attributes":{"plan":"pro"}}}'
json
{
  "flags": {
    "new-checkout": { "value": true, "variation": "on", "reason": "RULE_MATCH" }
  },
  "next_change_at": null,
  "etag": "6b1f0c2a9d4e7f31"
}
FieldMeaning
valueWhat your app uses.
reasonWhy: OFF, TARGET_MATCH, RULE_MATCH, FALLTHROUGH, or ERROR (treat as missing).
next_change_atWhen a scheduled change or window is next due, or null. Ask again then.
etagChanges whenever any flag changes; handy to see if anything is new.

The user is always the one in the token — your app cannot ask on behalf of someone else. keys is optional and narrows the answer. attributes are facts your rules can use. GET .../featureflags/evaluate?keys=a,b answers the same for a user with read-only access (no attributes).

Plain JavaScript

ts
import { FlagsClient } from './flagsClient';

const flags = new FlagsClient({
  baseUrl: 'https://<your-app-domain>',
  orgCode: 'acme',
  getToken: () => session.token,       // the signed-in user's app token
  attributes: { plan: 'pro' },          // optional facts for your rules
});
await flags.start();                    // never throws; you get defaults if it fails

if (flags.variation('new-checkout', false)) {
  showNewCheckout();
}
const unsubscribe = flags.onChange(() => rerender());

React

tsx
// once, near the root
<FlagsProvider client={flags}><App /></FlagsProvider>

// anywhere
function Checkout() {
  const newCheckout = useFeatureFlag('new-checkout', false);
  return newCheckout ? <NewCheckout /> : <OldCheckout />;
}

Vue 3

ts
// main.ts
app.provide(FLAGS_KEY, flags);

// a component
const newCheckout = useFeatureFlag('new-checkout', false);   // a ref
// <NewCheckout v-if="newCheckout" />

Angular

ts
// app.config.ts
providers: [{ provide: FLAGS_CLIENT, useValue: flags }, FeatureFlagsService]

// a component
readonly newCheckout = this.flags.flag('new-checkout', false);   // a Signal<boolean>
// @if (newCheckout()) { <app-new-checkout /> }

// or in a template
// <app-new-checkout *ifFlag="'new-checkout'; else: oldCheckout"></app-new-checkout>

From your own server

Call the evaluate endpoint from your backend with the signed-in user's token and pass the answer on, or hand your frontend the answer. Server code that runs inside nostackai (Logic Blocks, Glyphs, agents) does not need to call anything — see In Logic Blocks, Glyphs and agents.

Rules of thumb

  • Always pass a default. If flags cannot be loaded the default is used and your app keeps working.
  • Read flags once per page/session (the client does) and let onChange update the screen; don't call evaluate on every render.
  • Send only facts you need as attributes; they are used for the answer and not stored.
  • Keep the token fresh: the client calls getToken before every request.
  • Remove a flag from your code and then delete it once a feature is fully live.