Skip to main content
Flag evaluation is entirely local — it runs inside the SDK against a cached snapshot. There is no per-evaluation network call. The network is only used to periodically refresh the snapshot in the background.

Evaluation algorithm

When you call useFlag, useFlagValue, client.IsEnabled, or any other evaluation method, the SDK runs the following steps in order:
1. Flag not in snapshot?
   → Return caller default     (reason: ERROR, code: FLAG_NOT_FOUND)

2. Wrong type requested?
   → Return caller default     (reason: ERROR, code: TYPE_MISMATCH)

3. Flag disabled?
   → Return flag default       (reason: STATIC)

4. Walk targeting rules top → bottom:
   a. All conditions match?
      → Return rule's serve value   (reason: TARGETING_MATCH, ruleId: <id>)

5. Flag-level rollout configured?
   → Deterministically bucket user
   → Boolean flag: return true/false   (reason: ROLLOUT)
   → Multi-value:  return split value  (reason: SPLIT)

6. No match
   → Return flag default       (reason: DEFAULT)
Steps 1–3 are guard checks. Steps 4–6 are the evaluation proper.

Reason codes

Every evaluation produces a reason code. Access it via the *Detail / useFlagVariant APIs.
ReasonWhen it occurs
STATICFlag is disabled — the flag’s configured default value is returned
DEFAULTFlag is enabled, no rule matched, no rollout is configured
TARGETING_MATCHA targeting rule matched; the rule’s serve value is returned
SPLITUser was bucketed into a multi-value traffic split
ROLLOUTUser was bucketed into a boolean rollout
ERROREvaluation could not complete — see errorCode for the specific failure

Error codes

Error codes appear on results with reason ERROR.
CodeCauseSDK behaviour
FLAG_NOT_FOUNDFlag key not present in the snapshot (flag disabled, archived, or wrong environment)Returns the caller-supplied default
TYPE_MISMATCHThe flag exists but its type doesn’t match the method called (e.g. GetString on a number flag)Returns the caller-supplied default
MISSING_TARGETING_KEYRollout or traffic split requires a bucketing key, but the evaluation context contains noneReturns the flag’s default value
PROVIDER_NOT_READYSDK has not yet loaded its first snapshotReturns the caller-supplied default
FLAG_NOT_FOUND is the normal outcome when a flag is disabled. Because disabled flags are excluded from the snapshot, the SDK cannot distinguish between “flag disabled” and “flag doesn’t exist” — both appear as FLAG_NOT_FOUND. For boolean flags this is always transparent, since both FLAG_NOT_FOUND and a disabled flag return false.

Deterministic bucketing

Rollout and traffic splits use a FNV-1a (32-bit) hash to assign each user a bucket position between 0 and 100,000. The hash input is:
{flagKey} + {envKey} + {targetingKey} + {salt}
The targetingKey is resolved from the evaluation context using the first available value in this priority order:
customerId → agentId → businessId → businessBranchId → walletId → customerRef → omnipayId
Because the hash is deterministic, the same user always lands in the same bucket for the same flag, across all SDK platforms, restarts, and devices. A user who receives true for a flag at 10% rollout will continue to receive true as that rollout expands to 20%, 50%, and 100%. If none of the bucketing keys are present in the context, rollout-based evaluation returns MISSING_TARGETING_KEY and falls back to the flag’s default value.

Snapshot caching

PlatformStorageWarm start
ReactIn-memoryNo — isLoading is true until the first fetch completes
React NativeAsyncStorageYes — flags available immediately from the persisted cache
.NETIn-memoryNo — the hosted service loads the snapshot at startup, before requests arrive
All SDKs refresh the snapshot on a polling interval configured server-side (default: 2 minutes). Refreshes use HTTP ETags — if the snapshot hasn’t changed, the CDN returns 304 Not Modified and no data is transferred. React Native additionally refreshes on foreground resume.

Stale snapshot behaviour

If the SDK cannot reach the CDN during a refresh attempt (network error, timeout, CDN outage), it continues using the last successfully fetched snapshot. Evaluation is uninterrupted. The client status reflects the failure:
  • isFetching returns to false
  • error is set to the fetch error
  • origin remains 'CACHE' or 'SERVER' (the source of the snapshot currently in use)
The SDK will retry on the next polling interval. As long as a snapshot has been loaded at least once, there is no degradation in flag evaluation capability during outages.