Skip to content

TrackingControl

Ahmed Abbas edited this page Aug 7, 2026 · 3 revisions

Tracking Control

Two independent switches control whether the SDK sends outbound tracking events: a global config switch and a per-call override. Use them to honor consent without losing your experiment logic — bucketing, rule evaluation, and sticky persistence keep working regardless of the tracking state. Only the outbound network enqueue is silenced.

The global switch — tracking:

Set tracking: false at ConvertSdk.create to disable all outbound event tracking for the client:

CONVERT_SDK = ConvertSdk.create(
  sdk_key:  ENV.fetch("CONVERT_SDK_KEY"),
  tracking: false   # default is true
)

When tracking is false:

  • Decisioning still runs. run_experience / run_features etc. still bucket the visitor and return real results.
  • Sticky StoreData still persists. Variation assignments and visitor properties are still written to the store.
  • The outbound event enqueue is suppressed. No bucketing event is enqueued for delivery, and a debug line records each suppression (tracking disabled, event suppressed).

Conversions under the global switch

track_conversion checks the global switch before the dedup-and-mark step. So when tracking is false a conversion is neither enqueued nor marked as deduplicated — meaning a subsequent same-goal call stays unblocked once tracking is re-enabled. The return value is unchanged (self); no sentinel:

context.track_conversion("purchase")
# tracking: false -> nothing enqueued, goal NOT marked, debug line emitted, returns self

The per-call override — enable_tracking

Suppress the event for a single decision call by putting enable_tracking: false in the per-call attributes hash (symbol or string key both work — the public boundary accepts both):

variation = context.run_experience("homepage-test", { enable_tracking: false })
context.run_experiences({ enable_tracking: false })

When enable_tracking: false, bucketing, sticky persistence, audience rules, and the internal bucketing lifecycle event still fire — only the outbound enqueue is skipped (a debug line records the suppression: tracking suppressed for call). Absent or any non-false value leaves tracking on; only an explicit false suppresses.

Neither tracking switch touches the bucketing lifecycle event — it is decisioning observability, not tracking, so a host listener can react to the decision even under consent denial. Only the outbound enqueue is gated by the tracking switches. (An active preview is the one thing that does silence the lifecycle event — see below.)

How the two combine — global-off always wins

The verdict for whether an event is enqueued is the composition of both switches. The global switch is dominant: a global tracking: false suppresses delivery regardless of a per-call enable_tracking: true.

Global tracking Per-call enable_tracking Event enqueued?
true true (default) Yes
true false No
false true No (global-off wins)
false false No

The third path — preview is zero-trace, not a switch

Context#set_preview is not a consent switch, but it is the strongest suppression in the SDK, so it belongs in the same mental model. While a preview is active on a context, that context records nothing — and unlike the tracking switches, this covers persistence and the lifecycle events too:

Surface Under a tracking switch (global tracking: false or per-call enable_tracking: false) Under an active preview
Decisioning (bucketing, rules, features, segment matching) Runs Runs — only the previewed experience is forced
Sticky StoreData write Persists Skipped
bucketing lifecycle event Fires Does not fire
Outbound event enqueue Suppressed Suppressed
track_conversion The global switch suppresses the whole call before the dedup mark, so no conversion event fires either; there is no per-call switch on this method Full no-op — nothing enqueued, no conversion event, no dedup mark
update_visitor_properties Persists In-memory merge only, store write skipped
set_default_segments / run_custom_segments Persists Matching runs, store write skipped

This applies to every experience and feature evaluated on that context, not only the previewed one — so a page under preview still renders coherently while recording nothing. Preview state is per-Context: another context on the same client tracks normally.

See Code Examples for the wiring and QA & Preview for the preview-link workflow.

Consent scenarios

Scenario What to do
Consent denied for the whole client ConvertSdk.create(..., tracking: false). Decisions and stickiness still work; nothing is delivered.
Consent denied for a specific call only Keep the global tracking: true and pass enable_tracking: false on the calls you want silent.
Diagnosing "events vanish" Confirm tracking is not disabled — a tracking disabled, event suppressed / tracking suppressed for call debug line is the signal. See the missing-events decision tree.

Related pages

Clone this wiki locally