Consent Mode (GDPR)

A vs B has two ways to respect a visitor's cookie choice. Use either on its own, or both together.

LayerWhat it doesWhere you set it
Consent modeAll or nothing. The snippet does nothing at all until your site says the visitor agreed.Project Settings → Configuration
Consent categoriesFiner grained. Experiments can still run, while measurement stays off for a visitor who refused analytics.Your banner calls avsb.consent.set(), or we read Google's consent signal

Consent mode is checked first. If it is on and your site has not called avsb.init(), nothing else on this page applies yet, because nothing is running.

Which setting is right for you is your call

This page describes what the product does for each choice. It is not legal advice. How you classify our cookies, and which categories you ask visitors to agree to, is a decision for you and your legal advisers.

Consent mode helps you comply with GDPR, ePrivacy, and other cookie-consent laws. The snippet still loads the datafile and runs your global custom script (Project JS), but it sets no cookies, runs no experiments, and sends no tracking data until the visitor consents and your site calls avsb.init().

  1. Go to Project Settings → Configuration.
  2. Turn on Consent mode in the Advanced settings section.
  3. Click Save changes. Saving applies the setting, and it reaches your site in about a minute.

The embed code stays the same; no script-tag changes or URL parameters needed.

How it works

What runs immediately (before consent):

  • Datafile fetch: the snippet fetches your project config from the CDN. It's a static JSON file: no cookies are set and no user data is sent.
  • Global custom script (Project JS): your Project JS runs right away, so you can put your consent integration code (OneTrust, Cookiebot, and so on) there and call avsb.init() when consent is granted.
  • Stub API: window.avsb is exposed with init(), disable(), queue support, and consent.set(), so your banner can record a category choice even before the visitor grants the outer consent.

What waits for consent:

  • Cookie creation (no _avsb_visitor cookie until init())
  • Experiment evaluation and variation injection
  • Event tracking and exposure logging

Anti-flicker is the exception. It does not wait: the page is revealed straight away and never hidden again, so nobody stares at a blank page while deciding whether to accept your banner. See Anti-flicker behaviour below.

When the visitor consents, your site calls avsb.init() and the full startup runs: cookie creation, experiment evaluation, variation injection, and tracking. If they later revoke consent, call avsb.disable() to tear everything down.

Integration examples

OneTrust:

JavaScript
// Replace 'C0002' with your OneTrust category ID for analytics cookiesif (window.OneTrust) {  window.OneTrust.OnConsentChanged(({ detail: categories }) => {    if (categories && categories.includes('C0002')) {      avsb.init()    } else {      avsb.disable()    }  })}
JavaScript10 lines

Cookiebot:

JavaScript
window.addEventListener('CookiebotOnAccept', () => {  if (window.Cookiebot && window.Cookiebot.consent.statistics) {    avsb.init()  }})window.addEventListener('CookiebotOnDecline', () => {  avsb.disable()})
JavaScript8 lines

Custom consent banner:

JavaScript
// Call avsb.init() when the visitor gives consentvar acceptBtn = document.getElementById('accept-cookies')if (acceptBtn) {  acceptBtn.addEventListener('click', () => {    avsb.init()  })}// Call avsb.disable() if the visitor revokes consentvar revokeBtn = document.getElementById('revoke-cookies')if (revokeBtn) {  revokeBtn.addEventListener('click', () => {    avsb.disable()  })}
JavaScript15 lines

API reference

avsb.init()

Starts the snippet when consent mode is on. Triggers cookie creation, experiment evaluation, variation injection, and event tracking. It has no effect when consent mode is off (the snippet auto-starts), when already started, or after disable() has been called. Any track calls queued before init() are replayed once startup finishes.

avsb.disable()

Stops all tracking, clears the visitor cookie, removes injected variations, and tears down experiment triggers. After disable(), init() becomes a no-op; the visitor must reload the page to re-consent.

Behaviour by state

Statetrack.event/segment/carttrack.purchasegetVisitorId()getVariation()forceVariation()
Before init()QueuedSee note belownullnullfalse
After init()Fires immediatelyFires immediatelyReturns visitor IDReturns variation IDWorks normally
After disable()DroppedDroppednullnullfalse

track.purchase() is the one exception, and the timing matters. Picture an order confirmation page, where the pasted script may still be loading when the page fires its purchase call. That race is exactly the case the pre-load queue exists for: the call is held, and it sends once you call init(), same as the other three. A purchase call made after the script has already loaded, but before init(), is dropped instead. It needs consent to leave the browser, and by then nothing is holding it.

Anti-flicker behaviour

With consent mode on, the page is revealed as soon as the snippet sees that consent mode is enabled, and it is never hidden again. Consent might take seconds, or never come, and a visitor has to be able to read your page and your banner while they decide.

That also means there is no hide when init() runs. Hiding at that moment would blink the whole page blank at the exact moment someone accepts your banner, which is the worst possible time to look broken. The trade-off is honest: variations applied after consent can be briefly visible as they apply, because the page is already on screen. See Anti-Flicker.

Consent categories are for the common case where an experiment may still run, but a visitor who refused analytics must not be measured. They work whether or not consent mode is on.

The two categories

CategoryWhat it covers today
analyticsOur own measurement (experiment views, goals, purchases, recommendation events) and every analytics tool we forward to: Google Analytics 4, Google Tag Manager, Mixpanel, Segment, Adobe Analytics, FullStory, Contentsquare, Heap, and Amplitude.
marketingReserved for ad platforms. No tool we forward to uses it today, so setting it changes nothing yet. It is safe to send if your banner already tracks it.

A category the visitor has never answered counts as allowed. If you never call avsb.consent.set() and your page carries no Google consent signal, behaviour is exactly as it was before.

Telling us the visitor's choice

Call avsb.consent.set() from your banner whenever the visitor answers it.

JavaScript
// The visitor accepted analyticsavsb.consent?.set({ analytics: true })// The visitor refused analyticsavsb.consent?.set({ analytics: false })// Both categories at onceavsb.consent?.set({ analytics: true, marketing: false })// Read back the current answer. Categories never answered are left out.avsb.consent?.get() // => { analytics: true, marketing: false }
JavaScript11 lines

The ?. is there because the preview and visual-editor bypass runtimes build a minimal avsb object with no consent on it. Every runtime your visitors get (normal, consent mode, and the original experience) always attaches it.

  • Each call updates only the categories you pass, so you can answer them one at a time.
  • Only true and false count. Any other value for a category is ignored.
  • The answer is saved in a first-party cookie named avsb_consent for one year, so it still applies on the visitor's next page and their next visit.

If your banner runs before the snippet loads

The snippet is loaded with async, so a fast banner can call avsb.consent.set() before it arrives. That is safe, and there is nothing to add: the small first script tag from Quick Install already defines avsb.consent.set, holds the call, and applies it the moment the snippet takes over. This is the install in full:

HTML
<script>window.avsb=window.avsb||{};window.avsb.q=window.avsb.q||[];window.avsb.ready=window.avsb.ready||function(f){window.avsb.q.push(f)};window.avsb.on=window.avsb.on||function(n,f){var t=['on',n,f];window.avsb.q.push(t);return function(){t[0]=null}};window.avsb.consent=window.avsb.consent||{set:function(s){window.avsb.q.push(['consent.set',s])},get:function(){return{}}};window.avsb.track=window.avsb.track||{};['event','segment','purchase','cart'].forEach(function(m){window.avsb.track[m]=window.avsb.track[m]||function(a,b){window.avsb.q.push(['track.'+m,a,b])}});(function(){window.avsb._t0=Date.now();window.avsb._df=fetch('https://cdn.avsb.cloud/YOUR_SNIPPET_KEY/datafile.json').catch(function(){});var d=document,e=d.documentElement,s=d.currentScript,a=s&&s.getAttribute('data-avsb-timeout'),t=a==null?3000:+a;if(!(t>0)||window.avsb.version)return;e.style.opacity='0';e.style.pointerEvents='none';window.avsb._t=setTimeout(function(){e.style.removeProperty('opacity');e.style.removeProperty('pointer-events')},t)})();</script><script src="https://cdn.avsb.cloud/snippet.js?id=YOUR_SNIPPET_KEY" data-avsb="YOUR_SNIPPET_KEY" async></script>
HTML9 lines
Check the snippet on your site has the consent line

Installs made before consent support shipped end at the third line. On those pages a banner calling avsb.consent.set(...) above the loader tag throws, because avsb.consent does not exist yet. Re-copy the snippet from Project Settings > Snippet to fix it.

A refusal recorded this way applies to the page it happened on, not just the next one. Before the snippet has loaded, avsb.consent.get() returns {}, which is the honest answer: nothing has been recorded yet.

If your banner already speaks Google's consent language, you do not have to call us at all. Leave Read Google consent signal on in Project Settings → Configuration → Consent and we follow the visitor's choice automatically.

  • analytics_storage set to denied turns our analytics category off, and granted turns it on.
  • ad_storage does the same for marketing.
  • We read the choices already recorded when we start, and any update made later on the same page. The most recent one wins.
  • An explicit avsb.consent.set() call always wins over the signal, one category at a time. Setting analytics yourself does not stop the signal deciding marketing.
  • If the page carries no signal at all, nothing changes.

Turn the setting off if you would rather we ignore Google's signal and use only what your banner tells us directly.

A vs B keeps one visitor cookie, _avsb_visitor, so the same visitor keeps seeing the same variation. Whether your banner calls that a functional cookie or an analytics cookie is your decision, and we support both. Choose in Project Settings → Configuration → Consent.

ChoiceWhat happens when a visitor refuses analytics
Functional (default)The visitor cookie is written even when a visitor denies analytics, and you declare it as functional in your banner. That visitor keeps seeing the same variation on every page, and still contributes no data.
AnalyticsThe visitor cookie counts as analytics. When a visitor denies analytics, no cookie is written: that visitor sees the original page, with no experiment and no data.

Save the change and give it about a minute to reach your site.

There is one more cookie in play, avsb_consent, which stores the answer you send us through avsb.consent.set(). It exists only to remember the visitor's choice.

Showing an experiment without measuring it

With the Functional classification, a visitor who refuses analytics still sees your experiment, and still sees the same variation on every page. What stops is all measurement:

  • No experiment views, goals, purchases, or recommendation events are collected.
  • Nothing is forwarded to Google Analytics 4, Google Tag Manager, or any other analytics tool.
  • The avsb.on('event', ...) callback on your own page does not fire for that visitor either, so nothing you have built on top of it can measure them by accident.

The trade is worth being clear about: that visitor changes what your traffic looks like, but never appears in your numbers.

What a visitor who refused contributes

One thing, and it carries nothing about them: a small check that tells your dashboard the snippet is installed on the page. It contains your project's snippet key and nothing else, which is why it still runs. It is the reason Project Settings → Snippet can show the Installed badge for a site whose visitors all refuse analytics.

When a visitor changes their mind

Nothing is retroactive, in either direction.

  • Refusing part-way through a visit stops measurement from the next thing the visitor does. Anything already collected before the refusal stays collected: it was gathered while the visitor allowed it.
  • Agreeing part-way through a visit starts measurement from the next thing the visitor does. The actions taken before agreeing are not sent afterwards, because nothing was held back waiting for permission.
  • With the Analytics classification, a visitor who agrees after seeing the original page starts getting experiments on their next page view, not mid-page. This is deliberate: swapping the page underneath somebody who has already started reading it would be worse than waiting.

Results only count consented visitors

Because a refused visitor sends nothing, your experiment results are calculated from visitors who allowed measurement. This is the honest reading of the numbers, and it is worth remembering when your A vs B totals sit below the totals in a tool that measures on different rules. See Why counts differ between tools.

FAQ

Does the datafile fetch set cookies?

No. The datafile is a static JSON file served from a CDN. No cookies are set, no user data is sent, and no tracking happens. It's like loading a CSS file or a font.

What if init() is never called?

The datafile is fetched and your Project JS runs, but no cookies are set, no experiments run, and no tracking data is sent. The snippet sits idle with a stub API on window.avsb.

Can I call init() again after disable()?

No. After disable(), init() is a no-op. The visitor must reload the page to re-consent. This is intentional: it guarantees a clean state after consent is revoked.

What about the pre-load queue?

You can queue track.event(), track.segment(), and track.cart() calls before init(). They're replayed automatically once startup completes. A track.purchase() call queues too, but only while the pasted script is still loading. Once it has loaded, a track.purchase() call made before init() is dropped instead. See Behaviour by state.

Do I need both layers?

No. Consent mode alone is the strictest option: nothing runs until you say so. Categories alone let experiments run for everyone while measurement follows the banner. Using both means the outer gate is checked first, and the categories then decide what gets measured once you have called avsb.init().

Was this helpful?