> ## Documentation Index
> Fetch the complete documentation index at: https://docs.abtestly.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Consent flag never flips to granted

> You are in deferred consent mode. The visitor granted consent on your CMP. But ABTestly's snippet does not fire. Here is why.

If you have set ABTestly to **deferred consent** with a `consentKey`,
the snippet waits for `window[consentKey] === 'granted'` before it
does anything. If your CMP is not flipping that flag the way ABTestly
expects, the snippet just… waits. Forever. Silently.

## The check ABTestly does

```js theme={null}
setInterval(() => {
  if (window[consentKey] === 'granted') {
    /* fire snippet */
  }
}, 200);
```

The exact string `'granted'`, on the exact top-level property named
by `consentKey`. Anything else is a no.

## Common breakage

**Wrong value.** Your CMP writes `true`, or `'accepted'`, or `1`.
ABTestly wants literally `'granted'`.

**Wrong key.** Your CMP writes to `window.cookieConsent.status =
'granted'` (a nested path). ABTestly reads `window[consentKey]`
directly, no path resolution.

**Property redefined with a getter.** Some CMPs use
`Object.defineProperty` with a getter. ABTestly's polling reads the
property, which invokes the getter, so this actually works fine.
unless the getter throws.

**Property set inside an iframe.** ABTestly reads the top-level
window. A CMP that lives in an iframe and sets the flag on the
iframe's window does not reach the outer.

## Debugging

Open DevTools on a fresh page load, before the CMP has fired:

```js theme={null}
Object.getOwnPropertyDescriptor(window, 'yourConsentKey')
```

If it returns `undefined`, the key is not set on `window` at all.
Wait for the CMP to fire, then re-run.

```js theme={null}
window.yourConsentKey
```

If it returns anything other than `'granted'`, ABTestly will not
fire.

## The bridge pattern

If your CMP does something other than "assign `'granted'` to a
top-level window property", write a small bridge in your Global JS:

```js theme={null}
// Bridge: fires ABTestly when our CMP's real event happens
document.addEventListener('mycmp:consent-accepted', () => {
  window.abtestlyConsent = 'granted';
});
```

Now set ABTestly's `consentKey` to `'abtestlyConsent'` in
Settings → Snippet. The bridge translates your CMP's real event into
ABTestly's expected flag.

## The fail-closed default

If `consentKey` is empty in your snippet, because deferred mode was
turned on but no key was set, the snippet logs an error and does
nothing. That is the safe default; ABTestly will never fire without
explicit consent.

Check the DevTools Console for:

```
[abtestly] deferred consent mode is configured but consentKey is empty
```

If you see it, either set the key in your dashboard, or turn off
deferred mode.

## When "consent stuck" is actually opt-out

An opted-out visitor also does not fire the snippet, but the reason
is opt-out, not consent. Check for the cookie:

```js theme={null}
document.cookie.split(';').find(c => c.trim().startsWith('__abtestly_optout'))
```

If it says `__abtestly_optout=1`, the visitor is opted out. That is
working as designed. Have them visit
`https://yoursite.com/?abtestly_optout=0` to opt back in.
