Tracking Control
Consent: global tracking switch and per-call enable_tracking override
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:
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_featuresetc. 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
bucketingevent is enqueued for delivery, and adebugline 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 selfThe per-call override — enable_tracking
enable_trackingSuppress 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
bucketinglifecycle 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
- Configuration Options — the
trackingconfig option - Code Examples — per-call suppression and
set_previewin context - QA & Preview — the preview-link and debug-token workflow
- Tracking Conversions — the shared conversion concept doc
Updated 7 days ago