Privacy SDK troubleshooting
A checklist for "I'm calling
setPrivacyStatebut destinations are still firing", plus how to verify the privacy gate is active end-to-end.
"I'm calling setPrivacyState but pixels still drop cookies before consent"
Run through this checklist in order. Each step rules out a common cause.
1. Is Data Control enabled on the brand?
This is the single most common cause and the answer is usually no.
- Sign in to the Seeka admin app.
- Open Settings > Consent > Data Control.
- Check that the Enabled switch is on.
- Confirm a mode is selected (Explicit for GDPR-style opt-in, Implicit for opt-out).
If the switch is off and no session override is in effect, setPrivacyState is recorded but the SDK's isPluginLoadable short-circuits to true for every destination. Confirm the effective mode with window.Converge.privacy.mode (step 4) - if it reports 'disabled', either flip the switch or set a session override, then redeploy or hard-refresh and re-test.
2. Are you on the right brand config?
The Data Control setting is per converge instance (per brand). If your install loads more than one brand (multi-instance), make sure the brand you expect to be gating is the one with Data Control enabled.
In the admin app, the brand selector in the top bar must match the brand id used in your install snippet.
3. Are you calling setPrivacyState on the right SDK object?
Multi-instance installs expose a scoped wrapper as the page global (window.Converge). The scoped wrapper's privacy.setPrivacyState and privacy.setConsentManager fan out to every instance, so the call below works regardless of how many brands the install loads - use the global your install snippet already created, do not construct a new SDK:
await window.Converge.privacy.setPrivacyState({ grantedIabPurposeIds: [7], deniedIabPurposeIds: [] });privacy.isPluginLoadable and privacy.isIabPurposesGranted throw on the scoped wrapper because they are per-instance queries. If you need to query state for a specific brand on a multi-instance install, reach in via window.Converge.instances[scopeKey].privacy.
4. Is your mode actually Explicit?
If your brand is set to Implicit, destinations load by default. They only stop loading once the user explicitly denies a relevant IAB purpose. For GDPR / UK / EU / AU opt-in requirements, you want Explicit.
Verify by inspecting window.Converge.privacy.mode in the browser DevTools console after the SDK has loaded. It should report 'explicit' (or 'implicit'). If it reports 'disabled', see step 1.
Multi-instance caveat: when the install loads more than one brand, window.Converge is the scoped wrapper. From SDK v1.12.0 its privacy.mode reports the strictest mode any installed instance is enforcing, and privacy.state the last consent set through the wrapper (on older versions both are fixed placeholders: 'disabled'/null). For a specific brand's values, inspect the instance: window.Converge.instances[scopeKey].privacy.mode (list the keys with Object.keys(window.Converge.instances)).
5. Did you call setPrivacyState before the destinations had a chance to load?
In Explicit mode the SDK gates destinations at SDK init time, so timing of setPrivacyState does not matter for preventing the initial load - everything stays blocked until you grant.
In Implicit mode the SDK loads destinations at init by default. If your CMP makes its decision asynchronously (e.g. fetching banner state from a remote service), there is a window between SDK init and the consent decision where destinations are already loaded. Once you call setPrivacyState({ deniedIabPurposeIds: [7] }) they will be disabled going forward, but any cookies already dropped in that window remain in the user's browser.
If your jurisdiction does not tolerate that window, switch to Explicit.
6. Are you setting consent for the right purpose?
Today the only ad-tech consent purpose Seeka destinations gate on is IAB purpose ID 7 - Measure advertising performance. If your CMP grants other purposes (1, 2, 9, etc.) but not 7, ad-tech destinations stay blocked.
If your CMP normalises consent into a different purpose mapping, decode and remap before calling setPrivacyState, or pass the TCF v2 string directly via tcfConsentString and let Seeka decode.
"I set window.Seeka.privacy.mode but the session mode didn't change"
The session-scoped override is read once, when the SDK constructs. Work through:
- Set it before the brand script loads. The
window.Seeka.privacyassignment must execute before the Seeka script tag does (or be passed viaSeekaInstall({ privacyOverride }), which writes it before injecting the script). Setting it afterwards - from a tag manager rule, a consent banner callback, or an async bundle that runs after Seeka - does nothing, silently: the SDK cannot warn about a value that arrived after it already resolved the mode. - It can only tighten.
disabled < implicit < explicit. An override weaker than the brand's configured mode never applies - the brand mode is the floor. You cannot use the override to turn data control off for a session. - Check the value. Valid values are exactly
'disabled','implicit','explicit'(case-insensitive, whitespace tolerated). Anything else is ignored and the brand mode applies; a warning is logged, but it is only visible with debug logging enabled (see section D below). - Verifying on a multi-instance install?
window.Converge.privacy.modereports the strictest instance mode from SDK v1.12.0 (a fixed'disabled'placeholder before that) - for a specific brand readwindow.Converge.instances[scopeKey].privacy.mode, see step 4 above.
How to verify the gate is working
A. Inspect the SDK state in DevTools
After the SDK has loaded, in the browser console:
window.Converge.privacy.mode
// Should be 'explicit' or 'implicit', not 'disabled'
window.Converge.privacy.state
// null = no consent set yet
// { grantedIabPurposeIds: [...], deniedIabPurposeIds: [...], tcfConsentString: '...' } once setOn a multi-instance install the wrapper's mode is the strictest across instances and state is the last consent set through the wrapper (v1.12.0+; fixed placeholders before that) - for brand-specific values target an instance (window.Converge.instances[scopeKey].privacy.mode), see step 4.
B. Check plugin enabled state
Each Seeka destination plugin lives on window.Converge.lib.analytics.plugins. The analytics library tracks an enabled flag per plugin. In Explicit mode before consent:
const plugins = window.Converge.lib.analytics.getState('plugins');
// Look for converge-plugin-pinterest-tag-* / converge-plugin-facebook-pixel-* / converge-plugin-tiktok-pixel-*
// Their `.enabled` should be false until you call setPrivacyState with the granted purposes.C. Watch the network tab
On a fresh page load in Explicit mode with no consent yet:
https://s.pinimg.com/ct/core.jsshould NOT load.https://connect.facebook.net/en_US/fbevents.jsshould NOT load.https://analytics.tiktok.com/i18n/pixel/events.jsshould NOT load.https://sc-static.net/scevent.min.jsshould NOT load.
If any of these load before you've called setPrivacyState with the relevant purpose granted, the gate is not active. Restart at step 1.
After you call setPrivacyState({ grantedIabPurposeIds: [7], deniedIabPurposeIds: [] }), those scripts should load on the next event tick.
D. Watch the debug log
The privacy log lines below are emitted at verbose/information level, and the SDK's default minimum log level is warning - so enabling debug alone is not enough to see them. Either append ?debugLevel=verbose to the page URL (this both enables debug logging and lowers the level for the session), or set both keys in the SDK config:
debug: { isEnabled: true, minimumLevel: 'verbose' }With that in place, the privacy SDK emits log lines on every gating decision:
Privacy - State - Request change- yoursetPrivacyStatecall was received.Privacy - State - Changed- the state passed normalisation and triggered re-evaluation.Privacy - Plugin <name> is disabled. Enabling due to privacy state- a plugin was newly granted.Privacy - Plugin <name> is enabled. Disabling due to privacy state- a plugin was newly denied.Privacy - Missing IAB consent purpose IDs on privacy state. Loading plugin <name> blocked due to data control mode explicit- working as intended in Explicit mode before consent.
If you do not see the blocked due to data control mode line for ad-tech plugins on initial load in Explicit mode, the mode is not what you think it is. Re-check step 1.
"I set consent server-side but browser destinations still fire"
The browser SDK only reads server-side consent on the next ingest cycle, not on the current page load. If you push consent server-to-server while the user is mid-session, the change does not retroactively block destinations already loaded on this page.
Call setPrivacyState in the browser at the same time as the server-side push if you need immediate effect.
"I changed the brand's Data Control mode in admin but the SDK still behaves like the old mode"
The brand init script is cached. Force a fresh fetch:
- Append
?_cache=0to a page load. - Or clear
sessionStorage.removeItem('sk_config_cache')and reload.
Still stuck?
Capture the following and reach out to Seeka support:
- The brand id you are installing.
- The output of
window.Converge.privacy.modeandwindow.Converge.privacy.stateafter page load. - A network HAR of a fresh page load showing the offending destination request.
- The exact
setPrivacyStatecall you are making (timing relative to page load matters).