Frontend telemetry
Toggly client SDKs can send compact, aggregated feature-evaluation and application-metric telemetry from browser, mobile, desktop, and WebAssembly clients. Each SDK page shows the exact configuration and API names for that platform.
Frontend telemetry is enabled by default when a public application key is
configured. Set enableTelemetry to false to opt out. A missing application
key also disables the reporter completely: it buffers no telemetry and starts
no telemetry timer, lifecycle listener, or request. Server-side rendering, builds, and trusted
server or edge clients do not create a frontend reporter.
Use the package versions below or newer compatible releases. They cover the compact reporter and the session APIs described here.
SDK quick reference
The following versions support the documented compact frontend reporter and session APIs. The examples on each SDK page show where configuration belongs and which object owns cleanup.
| SDK | Version to use | Configuration | Explicit API and owner |
|---|---|---|---|
| JavaScript | @ops-ai/feature-flags-toggly 1.9.0 | Toggly.init(...) | Static Toggly.recordUsage, recordView, incrementCounter, setGauge, and flushTelemetry; Toggly.init replaces the single browser owner |
| Angular | @ops-ai/ngx-feature-flags-toggly 2.9.0 | provideToggly(...) or NgxFeatureFlagsTogglyModule.forRoot(...) | Injected TogglyService; Angular destroys the service owner with its injector |
| React | @ops-ai/react-feature-flags-toggly 1.12.0 | new Toggly(...) or createTogglyProvider(...) | Service methods; a provider factory retains one owner until its final unmount, while directly owned services require dispose() |
| Vue | @ops-ai/vue-feature-flags-toggly 1.10.0 | app.use(toggly, options) | The app-injected service; app.unmount() releases the app owner, and standalone services require dispose() |
| Svelte | @ops-ai/svelte-feature-flags-toggly 1.11.0 | createToggly(...) | Package functions or store methods; replacing or disposing the store releases its reporter |
| SvelteKit | @ops-ai/toggly-sveltekit 0.2.0 | Browser createToggly(snapshot, options) | Store methods; keep one store across route refreshes and call dispose() when the owning layout ends |
| Solid | @ops-ai/solid-feature-flags-toggly 0.3.0 | createToggly(...) or createClient(...) | Owner methods; Solid cleanup handles reactive owners, while a lower-level client requires dispose() |
| Next.js | @ops-ai/nextjs-toggly-client 1.5.0 | TogglyProvider browser config | useToggly().telemetry; provider unmount releases the browser owner |
| Nuxt | @ops-ai/nuxt-toggly-client 1.3.0 / Core 1.13.0 | Client createToggly(...) or Nuxt module browser config | client.telemetry; replacing the browser client releases the old owner |
| React Router | @ops-ai/react-router-toggly 1.1.0 | TogglyProvider or RouterTogglyProvider config | Methods returned by useToggly(); provider unmount releases its reporter |
| Astro | @ops-ai/astro-feature-flags-toggly 1.16.0 | Integration options or initTogglyClient(...) | Client-store functions; destroyTogglyClient() releases the shared browser owner |
| Gatsby | @ops-ai/gatsby-feature-flags-toggly 1.10.0 | Gatsby plugin browser options | Package functions or useToggly().telemetry; provider replacement or unmount releases the owner |
| Docusaurus | @ops-ai/toggly-docusaurus-plugin 0.10.0 | Plugin/provider options | useToggly() methods; final provider unmount releases the browser owner |
| Generic client core | @ops-ai/toggly-client-core 0.5.1 | createTogglyClient(...) | Client methods; call dispose() when the browser owner ends |
| Flutter | feature_flags_toggly 1.12.0 | TogglyConfig | Static Toggly methods; Flutter lifecycle backgrounding flushes and Toggly.dispose() releases the owner |
| iOS | ios-sdk-v1.5.2 | TogglyConfig | Async TogglyService or Toggly.shared methods; forward .background and dispose owned services |
| Android | Android SDK 1.8.0 | TogglyConfig | TogglyService, Toggly, Compose, and Views facades; forward AppStateType.BACKGROUND and dispose owned services |
| .NET client | Toggly.FeatureManagement.Client 3.10.0 | TogglyClientOptions | TogglyClient implements IFrontendTelemetry; DisposeAsync() releases the owner |
| Blazor WebAssembly | Toggly.FeatureManagement.Blazor 3.10.0 | TogglyClientOptions | Browser session implements IFrontendTelemetry; its DI scope owns cleanup |
| Electron | @ops-ai/electron-feature-flags-toggly 1.1.0 | Main-process initToggly(...) | Main client owns one reporter shared over authorized IPC; attachTogglyLifecycle(...) handles suspend and bounded quit cleanup |
| React Native | @ops-ai/react-native-toggly 1.4.0 with Core 1.8.0 | TogglyProvider props or Core TogglyConfig | useToggly() or Core service methods; provider AppState/background and final unmount use the same Core owner |
The Next.js and Nuxt trusted server and edge reporters retain their existing telemetry gateways and APIs. The rows above describe their browser companions. Solid/SolidStart requires 0.3.0 or a newer compatible release for these APIs.
The deprecated Remix packages do not gain a separate reporter; migrate to the React Router SDK. The generated CSS/HTML SDK has no JavaScript runtime in which to report frontend telemetry. The maintained WordPress PHP integration remains a server integration and does not add this browser reporter. To measure browser interactions on CSS/HTML or WordPress pages, initialize the JavaScript SDK with a public Front-end App Key and record usage/views from page interactions. Keep the maintained PHP and Blazor Server integrations for their server work.
Configuration
Names follow each platform's conventions, but the values are consistent:
| Setting | Default | Behavior |
|---|---|---|
enableTelemetry | true with an app key | Master switch for frontend telemetry |
enableUsageTracking | true where exposed | Automatic checks and explicit usage/view events |
enableMetrics | true where exposed | Application counters and gauges |
metricsBaseUrl | https://metrics.toggly.io | Independent telemetry base URL |
telemetryFlushIntervalMs | 45000 | Base flush interval; valid values are 30,000–60,000 ms |
Each interval is jittered by ±20% so clients do not flush at the same instant. An invalid interval falls back to 45 seconds. An invalid telemetry endpoint disables that reporter and emits a bounded diagnostic; neither condition stops feature initialization or evaluation. Custom endpoints must be absolute HTTP or HTTPS URLs without credentials, query strings, or fragments.
The telemetry endpoint is independent of the definitions endpoint. Definition
query parameters, authorization headers, groups, claims, and entity attributes
are never copied onto the metrics URL. Optional compact body fields i and u
are the only identity sent with a telemetry batch; see Identity.
What is recorded
An automatic check is recorded once for each feature that an application
actually evaluates. Local and entity gates resolve first; aggregate negation is
applied afterward. all and any gates keep their normal short-circuit
behavior, so skipped keys are not counted. A later real UI recomputation counts
again, while cache hydration, refresh projection, and framework snapshot reads
do not.
Where an SDK already returns an assigned variant, the check uses that variant.
Boolean-only SDKs use enabled or disabled. Variant names contain 1–64 ASCII
letters, digits, underscores, or hyphens.
Usage and view events are always explicit. Rendering a component or returning an enabled flag does not automatically mark the feature as used or viewed.
recordUsage(featureKey, variant = "enabled")
recordView(featureKey, variant = "enabled")
incrementCounter(metricKey, value = 1)
setGauge(metricKey, value)
flushTelemetry()
Counters accept nonnegative integer increments up to 1,000,000 per call and sum within a buffered window. Gauges accept finite nonnegative values up to 1,000,000 and keep the latest value. Define and allowlist feature and business metric names in Toggly before reporting them. Metric names are application-level names; they are not attached to a feature, identity, or label set. The server's metric definition decides whether a configured metric is a counter or gauge.
Privacy and resource limits
The compact request contains only:
- the application key and environment;
- feature → variant aggregate counts for checked, used, and viewed events;
- application-level metric values; and
- optional minted instance id
i, or client-asserted identityuwheniis absent.
It contains no groups, claims, entity data, timestamps, or metric kind. Data
stays in memory and is not persisted by storage companions. HTTP 202
acknowledges transport; it does not prove that a client-generated u was
attributed. The application toggle Accept client-generated identities for metrics
defaults off. Wire formats, key types, and mint curl live in the
Metrics API Reference and
Metrics authentication.
Each request is limited to 48 KiB of uncompressed UTF-8 JSON. A reporter keeps at most 2,000 admitted entries and 256 KiB across queued and in-flight work, with at most 16 variants per feature. Oversized or invalid new events are rejected without evicting already accepted events.
Delivery and lifecycle
Clients send POST {metricsBaseUrl}/api/frontend/telemetry. Each request
attempt has a five-second timeout.
Ordinary flushes prefer gzip and fall back to JSON if compression fails before
sending. Browser hidden and page-exit flushes use uncompressed fetch with
keepalive; native SDKs forward their platform background event. Requests omit
credentials.
Only HTTP 202 acknowledges a batch. The reporter retries only explicit HTTP
429 and 503 responses, at most twice, after 30 and 60 seconds while honoring a
longer readable Retry-After. Batches expire after five minutes. Network,
timeout, abort, and other ambiguous failures are contained and are not replayed,
because the server may already have accepted that batch. A 202 response
acknowledges transport acceptance; it does not prove that an undefined or
wrongly typed metric was retained.
Each client owner has one reporter. Replacing or disposing the client releases its timers, listeners, requests, and reconnect work without relabeling queued events for a new application or environment. Synchronous disposal starts at most one bounded best-effort final envelope. If you need to drain work before teardown, await the SDK's explicit flush method first; this is still best-effort and does not guarantee server acknowledgement.
Identity
Mint only from a trusted backend that holds a Backend application key:
POST https://metrics.toggly.io/api/frontend/identities
There is no CORS on mint. Pass the returned opaque instanceId into the client
SDK (not the Backend key). Definitions then use ?i=<instanceId> and omit
u / g / claim.* on that request. Telemetry sends the same token as body
field i.
When you do not mint, definitions keep ?u= / ?g= / claim.*. Telemetry may
include body u from the client's current identity; ingest attributes that
value only if the application toggle is on.
Unknown or expired i is stripped. The packet still returns 202, and
definitions with unresolved i evaluate without extra client u/g/claims
on the same request.
Switching sessions
Pass only the minted token, never the Backend key, to a client's instanceId
option (InstanceId on .NET). Mint a replacement when your application changes
the trusted session, then apply it through the SDK's context API. Clear the old
token explicitly on logout and clear old groups/claims where those are mutable.
Blank tokens return to client-asserted u attribution; some native SDKs return
to a device identity rather than removing identity entirely.
Already admitted events retain their original i or u; rotating context
never relabels an old batch. Async evaluations capture the context they began
with, and stale results must not become a new user's feature snapshot. Await
context transitions before starting new-user interactions. The context API owns
the session boundary. A flush only attempts delivery of admitted events with
their original attribution.
The SDK guides' Session identity sections show the owning API and clearing rules. These differ: for example generic client-core, Astro, and Gatsby retain a token when only identity changes, whereas Next.js and Nuxt clear an omitted token on identity changes. Docusaurus uses provider config and SvelteKit uses new server snapshots. Do not substitute one SDK's reset method for another's.
Analytics hooks may send separate provider events; they do not create another compact reporter. Storage adapters persist feature data only, not telemetry.