# Cookiebot consent recipe

Start the SDK paused until consent, then grant or withdraw from the Cookiebot category you assign to Telltale. This recipe uses Cookiebot's public browser API: the CookiebotOnConsentReady, CookiebotOnAccept and CookiebotOnDecline events and the Cookiebot.hasResponse and Cookiebot.consent properties.

## Choose the category

Cookiebot's categories are necessary, preferences, statistics and marketing. Statistics is the usual choice for measurement, but classify the SDK according to your own policy. The recipe reads the category from the script tag and defaults to statistics:

```html
<script src="/telltale-cookiebot.js" data-category="statistics"></script>
```

## Recipe

Save this code as /telltale-cookiebot.js on your own origin (the Telltale server does not serve recipe files) and load it after the SDK tag, or after the page-owned bridge from the [Consent guide](/docs/consent) (nonce it under a nonce policy; see the [CSP guide](/docs/csp)). When the bridge is present the file sends decisions through it and creates no client of its own.

```javascript
// /telltale-cookiebot.js
(() => {
  const CATEGORY = document.currentScript?.dataset.category || 'statistics';
  // With the page-owned bridge (Consent guide) loaded first, decisions go through it; otherwise this file owns the client.
  const bridge = window.telltaleConsent;
  const client = bridge ? null : window.Telltale.init({
    endpoint: 'https://app.trytelltale.dev',
    key: 'YOUR_SITE_PUBLISHABLE_KEY',
    waitForConsent: true,
    discover: true
  });
  const decide = bridge
    ? allowed => bridge.applyConsent(allowed)
    : allowed => (allowed ? client.optIn() : client.optOut()).catch(console.error);
  if (!['preferences', 'statistics', 'marketing'].includes(CATEGORY)) {
    console.error('Telltale: data-category must be preferences, statistics or marketing; collection stays held');
    return;
  }

  let decided = false; // a Cookiebot response has been applied on this page
  function sync() {
    const cookiebot = window.Cookiebot;
    if (!cookiebot) return;
    // No response yet: stay held. Unknown is never treated as consent.
    if (!cookiebot.hasResponse && !decided) return;
    decided = true;
    // A response that disappears after one was applied (for example after withdraw or renew) is a withdrawal.
    decide(cookiebot.hasResponse === true && cookiebot.consent?.[CATEGORY] === true);
  }

  // Saved and new choices. An accept can still deny this category, so it is checked, not assumed.
  let eventSeen = false;
  const onEvent = () => { eventSeen = true; sync(); };
  window.addEventListener('CookiebotOnConsentReady', onEvent);
  window.addEventListener('CookiebotOnAccept', onEvent);
  window.addEventListener('CookiebotOnDecline', () => {
    eventSeen = true;
    decided = true;
    decide(false);
  });

  // Cookiebot may have resolved consent before this file ran. Wait, by reading only, until its API is ready (the
  // readiness rule of /adapters/v1/cmp.js), then apply the saved response once.
  const apiReady = () => typeof window.Cookiebot?.hasResponse === 'boolean' && !!window.Cookiebot.consent && typeof window.Cookiebot.consent === 'object';
  const started = Date.now();
  (function waitForCookiebot() {
    if (eventSeen) return;
    if (apiReady()) return sync();
    if (Date.now() - started > 10000) return console.warn('Telltale: Cookiebot is not ready; collection stays held until it reports a decision');
    setTimeout(waitForCookiebot, 100);
  })();
})();
```

The readiness wait only reads window.Cookiebot; it never clicks the dialog and never calls a Cookiebot method. If Cookiebot has not loaded after 10 seconds the file logs a warning and stays held; a later Cookiebot event still applies the decision.

## Site withdraw buttons

If your page has its own "withdraw consent" button, call optOut() synchronously in the same click handler before Cookiebot.withdraw(), so collection stops even if no Cookiebot event follows:

```javascript
document.querySelector('#withdraw-consent').addEventListener('click', () => {
  window.Telltale.getClient().optOut().catch(console.error); // or window.telltaleConsent.applyConsent(false)
  window.Cookiebot.withdraw();
});
```

## Notes

Repeated optIn() or optOut() calls with the same decision are harmless. If you use Cookiebot's automatic blocking, check how it classifies sdk.js. The SDK makes no requests while held, so loading it before consent is safe, and having it loaded is what lets a later withdrawal stop collection on the current page.

Withdrawal calls optOut(), which stops owned collection and tools and discards pending evidence. It cannot recall data already sent. Logging in never grants consent. Keep identity (identify/reset, or connectIdentity from the Consent guide) separate from this consent bridge.
