Appearance
Evaluation
Evaluation is the act of resolving a flag or config to a concrete value for a given context. It's a pure function: same ruleset + same context = same result, every time, with a structured reason explaining why.
The hot path
http
POST /api/v1/envs/{envId}/evaluate
Content-Type: application/json
Authorization: Bearer act_…
{
"context": { "userId": "u_42", "plan": "pro", "country": "US" },
"keys": ["new-onboarding", "checkout.max-items"]
}Response:
json
{
"environmentId": "9f7a32b5-…",
"version": 142,
"evaluations": {
"new-onboarding": {
"value": true,
"defaultValue": false,
"reason": { "kind": "rule_match", "ruleIndex": 0, "matchedValue": true }
},
"checkout.max-items": {
"value": 50,
"defaultValue": 100,
"reason": { "kind": "rule_match", "ruleIndex": 1, "matchedValue": 50 }
}
}
}evaluations is a flat map keyed by primitive key. SDKs partition the map into flags and configs at the call site using the (key → kind) info they already loaded.
Selecting keys
keys controls which primitives are evaluated:
["a", "b"]— evaluate exactly these keys. Missing keys come back asnullenvelopes."*"or omitted — evaluate every flag and config in the environment.
For SDK callers, "*" once at startup (then on every push update once SSE lands) is the canonical pattern: bulk-resolve everything, cache in process memory, refresh on push.
The reason envelope
Every evaluation comes back with a reason describing why that value was returned. Possible shapes:
json
{ "kind": "default" }…no rule matched (or no rules exist). The default value was returned.
json
{ "kind": "rule_match", "ruleIndex": 0, "matchedValue": true }…rule at index 0 matched and the rule's value was returned. matchedValue repeats the value for symmetry with the env-default case.
json
{ "kind": "rule_match", "ruleIndex": 0, "matchedValue": true,
"trace": { /* tree of which sub-conditions matched */ } }…with verboseReason: true on the request, the same envelope plus a full predicate-tree trace showing which sub-conditions evaluated to which booleans. Used by the dashboard's "Why did I get this value?" panel.
json
{ "kind": "fallback", "error": "ruleset_not_cached" }…the environment's materialized ruleset isn't cached (only happens on a fresh environment that hasn't been written to yet). The default-of-default is returned (false for boolean, "" for string flags / configs, 0 for number, null for json).
Time-travel
Pass asOf: "2026-04-15T18:00:00Z" to evaluate against the ruleset that was live at that timestamp. The audit log is the source — every change since the cutoff is unwound and the evaluator runs against the historical state.
json
{
"context": { "userId": "u_42" },
"keys": ["new-onboarding"],
"asOf": "2026-04-15T18:00:00Z"
}Useful for debugging "why did this user see X yesterday?" without reconstructing state by hand. Capped at 90 days of history.
Counterfactual preview
To answer "what would happen if I made this change?" — without making the change — there's a separate endpoint:
http
POST /api/v1/envs/{envId}/evaluate/previewYou hand it a transient ruleset and an array of contexts. It runs both the live ruleset and your hypothetical against each context and returns side-by-side {live, preview} envelopes. The transient ruleset is never persisted and the env's version is unchanged. See Agents and AI and API reference: Evaluation for details.
History
To see how a single context's resolution changed over time:
http
POST /api/v1/envs/{envId}/evaluate/historyPass a context, a since timestamp, and (optionally) a single primitiveKey. The endpoint replays the audit log within (since, until] and emits per-primitive (timestamp, version, value, reason) change-points. A capped result set sets truncated: true rather than returning a 500. Window capped at 90 days.
Versioning and SSE
Every environment carries a monotonic version that increments on every committed write. The version is in:
- The
versionfield of every/evaluateresponse. - The
ETag: W/"<version>"header onGET /api/v1/envs/{envId}/ruleset/version— a cheap version-only endpoint for fallback polling. SendIf-None-Match: W/"<N>"and you'll get304 Not Modifiedif the version hasn't moved. - The audit row's
versioncolumn.
SDKs use the version to invalidate cached evaluations: see a higher version, refetch.
Validation helpers
Two endpoints help dashboards and rule builders give inline feedback before saving:
POST /api/v1/validate/regex— does this regex compile under the portable RE2 subset? Returns{ ok, error? }. Called on every keystroke in the rule builder.POST /api/v1/envs/{envId}/validate/condition— is this condition tree valid for this primitive type? Returns per-path errors for inline display.POST /api/v1/envs/{envId}/validate/evaluate— run a single draft rule against a context, either spliced into an existing primitive's rules or in isolation. Used by "preview this rule against my test contexts" UI.
See also
- Rules and contexts — what rules look like, what context attributes are.
- API reference: Evaluation — wire shape for every endpoint above.
- Agents and AI — counterfactual preview in the agent context.