Skip to main content

Evaluated-signed responses

Toggly client SDKs fetch pre-evaluated boolean flags from the worker at:

https://definitions.toggly.io/evaluated-signed/{appKey}/{environment}

Personalization is passed as query parameters on that URL (see Evaluation context below).

What you get​

  • User-side filters (targeting, percentage, user claims, browser/country/device/OS, time window) are evaluated on the worker
  • Features with Context Property filters may return an EntityGate object instead of a plain boolean when user filters pass — see Entity & page context
  • Responses can be signed (verify when your SDK supports it, e.g. Flutter)
  • The client caches the remote map in memory (and optionally persistent storage)
  • Reads resolve boolean | EntityGate locally when you pass entity context; optional post-filter gates AND device-local prerequisites at read time

See the Feature filters reference for parameter details and context requirements per filter type.

Evaluation context​

Client SDKs pass targeting data with setContext() (or equivalent), which the worker reads from the query string:

ContextQuery parameterUsed by
Identity?u= (evaluated) or ?userId= (variants)Percentage, Targeting
Groups?g= (repeatable)Targeting group rules
Claims?claim.{type}={value} (max 20 types)User Claims filter

Example:

await toggly.setContext({
identity: 'user-123',
groups: ['beta', 'admins'],
claims: { role: 'admin', plan: 'premium' },
});
// GET .../evaluated-signed/{appKey}/{env}?u=user-123&g=beta&g=admins&claim.role=admin&claim.plan=premium

Browser-based filters (Country, Browser Family, Device Type, etc.) use request headers automatically (User-Agent, Accept-Language, CF-IPCountry) — no SDK fields required.

When identity, groups, or claims are present, responses use Cache-Control: private so shared caches do not serve one user's evaluation to another.

Mixed definitions payload​

Evaluated-signed defs is a map of feature key → boolean | EntityGate:

ValueClient behavior
true / falseFinal user-side result (no entity rules, or user filters failed)
{ requirement, rules }User filters passed; evaluate rules locally against entity context
import { isEntityGate, resolveEvaluatedDefinition } from '@ops-ai/toggly-hooks-types';

const raw = flags['OrderBadge'];
if (isEntityGate(raw)) {
const on = resolveEvaluatedDefinition(raw, entityContext); // fails closed without context
}
Legacy checks

Do not use flags[key] === true or truthy checks — gate objects are objects, not booleans. Old SDKs that only check === true treat gates as off.

Percentage / rollout is user-only — entity rules are never percentage-bucketed. Entity evaluation happens per widget/check on the client after fetch.

Trust and security​

Evaluated-signed is designed for feature rollout, not as an authorization service.

Client-supplied context is not proof of identity​

For client-side apps, u / userId, g, and claim.* query parameters are chosen by the SDK in the browser. A user can edit the URL or devtools network tab and assert any identity or claim. The worker evaluates filters against what it receives — it does not validate JWTs or server sessions on these public endpoints.

Use caseSafe on client-side?Recommended approach
Gradual rollout, A/B tests, beta cohortsYessetContext() + targeting / User Claims filters
Admin UI, billing, PII, security-sensitive APIsNoBackend .NET filters with authenticated HttpContext claims
“Only employees see this button” (low risk UX)MaybeAccept spoofing risk, or enforce on server when the action runs

Sensitive data in URLs​

Evaluation context is sent on the query string. That can appear in browser history, referrer headers (if you link off-site), and edge access logs.

  • Use opaque IDs for identity (internal user GUID), not email or legal name.
  • Keep claims to coarse rollout dimensions (role, plan, beta) — not SSN, payment tokens, or full profile payloads.
  • Maximum 20 claim types per request; additional types are ignored (alphabetically first types are kept).
tip

Cache-Control: private prevents shared CDN caches from mixing evaluations between users. It does not hide query parameters from logs or the client.

Conditional requests (definitions revision)​

Responses include a definitions revision used for efficient updates:

ETag: abc123
X-Definitions-Revision: abc123

SDKs send the cached revision on subsequent requests:

If-None-Match: abc123

When definitions are unchanged, the worker returns 304 Not Modified and the SDK keeps the last-known-good flags.

The revision is the same value used on the WebSocket sync channel (sync, flags-updated). One cached revision per app/environment — not per-user. After a WebSocket sync notification, SDKs refetch with full context rather than applying anonymous evaluated payloads.

What you do not get​

  • Raw flag definitions or targeting rule DSL on the device
  • Server-side security enforcement — always protect sensitive behavior on your backend