Consent Mode (GDPR)
A vs B has two ways to respect a visitor's cookie choice. Use either on its own, or both together.
| Layer | What it does | Where you set it |
|---|---|---|
| Consent mode | All or nothing. The snippet does nothing at all until your site says the visitor agreed. | Project Settings → Configuration |
| Consent categories | Finer 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.
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: all or nothing
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().
Enabling consent mode
- Go to Project Settings → Configuration.
- Turn on Consent mode in the Advanced settings section.
- 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.avsbis exposed withinit(),disable(), queue support, andconsent.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_visitorcookie untilinit()) - 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:
// 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() } })}Cookiebot:
window.addEventListener('CookiebotOnAccept', () => { if (window.Cookiebot && window.Cookiebot.consent.statistics) { avsb.init() }})window.addEventListener('CookiebotOnDecline', () => { avsb.disable()})Custom consent banner:
// 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() })}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
| State | track.event/segment/cart | track.purchase | getVisitorId() | getVariation() | forceVariation() |
|---|---|---|---|---|---|
Before init() | Queued | See note below | null | null | false |
After init() | Fires immediately | Fires immediately | Returns visitor ID | Returns variation ID | Works normally |
After disable() | Dropped | Dropped | null | null | false |
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: run without measuring
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
| Category | What it covers today |
|---|---|
analytics | Our 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. |
marketing | Reserved 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.
// 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 }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
trueandfalsecount. Any other value for a category is ignored. - The answer is saved in a first-party cookie named
avsb_consentfor 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:
<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>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.
Reading Google's consent signal
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_storageset todeniedturns ouranalyticscategory off, andgrantedturns it on.ad_storagedoes the same formarketing.- 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. Settinganalyticsyourself does not stop the signal decidingmarketing. - 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.
Classifying the visitor cookie
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.
| Choice | What 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. |
| Analytics | The 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
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.
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.
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.
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.
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().