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