Consent and opt-out

Start tracking on the first page and let visitors opt out, or keep it off until your consent banner says yes. One attribute on the script tag decides, and the app writes it for you.

Updated

What there is to consent to

The tracker sets no cookies. It does keep a random anonymous ID and a session ID in the browser’s local and session storage, which many privacy rules (such as the EU’s ePrivacy rules and the UK’s PECR) treat the same way as cookies. It sends pageviews, clicks, scroll depth and your own events to ClickClacks. What’s collected lists every storage key and every field.

Opt-out or opt-in

The data-consent attribute on the script tag sets how tracking starts. On the install step and on each source’s page in the app, the Start right away / Wait for consent switch rewrites the tag and the agent prompt for you.

ModeWhat happens on page loadUse it when
data-consent="opt-out"
The default, also when the attribute is missing.
Tracking starts at once, unless the visitor opted out earlier or the browser sends Global Privacy Control.Your visitors’ law lets you measure without asking first, or a tag manager already holds the tag back until consent.
data-consent="opt-in"Nothing is stored or sent. The tracker reads only its own opt-out marker, and waits for optIn.Visitors must agree first, as for most analytics in the EU and UK.

The opt-in tag, with the queue stub so your banner can call it before the script has loaded:

html
<script>
  window.clickclacks = window.clickclacks || function () {
    (window.clickclacks.q = window.clickclacks.q || []).push(arguments)
  }
</script>
<script async src="https://app.clickclacks.io/c.js" data-key="pk_live_3f9a1c7e5b2d4f6a8c0e1b3d" data-domains="acme.com,www.acme.com,app.acme.com" data-consent="opt-in" data-respect-gpc="on"></script>

In opt-in mode every page starts off. A stored ID is never treated as consent, so your consent tool must call optIn on each page load for a visitor who already agreed. Before that call there are no IDs, no events, no heatmap requests and no identity carried from another domain. Loading c.js itself is still a request to ClickClacks; if even that must wait, load the tag after consent.

optOut and optIn

js
// The visitor declines, or withdraws consent
window.clickclacks('optOut')

// They change their mind (no reload needed)
window.clickclacks('optIn')

optOut, straight away:

  • drops any events not sent yet, cancels retries and captures in progress, and stops all tracking on the page: no events, no link tagging, no heatmap captures;
  • deletes the tracker’s IDs and capture markers from local and session storage, and removes the _ccid it added to links;
  • remembers the choice in local storage (cco), so on every later page load the tracker stays off before it reads or writes an ID;
  • stops tracking in the visitor’s other open tabs on the same hostname.

optIn forgets that choice and starts tracking with brand-new IDs. If the current page hasn’t been counted, optIn counts it now. Both also exist as window.clickclacks.optOut() and window.clickclacks.optIn().

  • Neither can recall events already sent. Use opt-in mode if nothing may be sent before the visitor agrees.
  • The choice is per hostname, like all browser storage. On acme.com and app.acme.com, apply it on both.
  • optIn applies to the current tab. Other tabs pick it up on their next page load, from your consent tool.
  • Clearing site data clears the choice. Re-apply it from your consent tool’s stored record on each load; calling optOut again is harmless.

Connect your consent tool’s callback to optIn and optOut. It is the same code in both modes. Put the queue stub above the tag so a choice made before c.js loads is queued: queued calls run before the first pageview, so an early optOut means not even that is sent, and no ID is read or created.

consent.js
// Adapt to your consent tool's callback. Call it on every page load with the
// saved choice too, not only when the visitor clicks.
onConsentChange((choices) => {
  if (choices.analytics)
    window.clickclacks('optIn')
  else
    window.clickclacks('optOut')
})

Your consent tool keeps the record of who agreed to what, and when. ClickClacks stores only the opt-out marker in the visitor’s browser.

The strictest approach: don’t put the tag in your HTML at all, and add it once the visitor agrees. Nothing runs, and no request reaches ClickClacks, until then.

html
<script>
  // Call this once the visitor has agreed to analytics.
  function loadClickClacks() {
    if (document.getElementById('clickclacks')) return
    var s = document.createElement('script')
    s.id = 'clickclacks'
    s.async = true
    s.src = 'https://app.clickclacks.io/c.js'
    s.setAttribute('data-key', 'pk_live_3f9a1c7e5b2d4f6a8c0e1b3d')
    s.setAttribute('data-domains', 'acme.com,www.acme.com,app.acme.com')
    s.setAttribute('data-respect-gpc', 'on')
    document.head.appendChild(s)
  }
</script>

The tracker reads its attributes from its own tag, so adding it from script works the same as writing it in the page. Loaded mid-visit, it records the current page as the first pageview. Leave data-consent out here: the tag only exists after consent.

Google Tag Manager

If the tag is installed as a Custom HTML tag, let Tag Manager hold it back: in the tag’s Consent settings, require additional consent for analytics_storage. With Consent Mode fed by your banner, the tag only fires once that consent is granted, so the default opt-out tag is the right one to paste.

Global Privacy Control

With data-respect-gpc="on", which the app’s snippet includes, a browser that sends Global Privacy Control is never tracked: the tracker stays off on load, and optIn does nothing while the signal is on. Remove the attribute if you want the signal ignored, for example because you ask for consent explicitly and want that answer to win.

ClickClacks doesn’t read the older Do Not Track setting. To honour it, opt the visitor out before the tag runs, after the queue stub:

html
<script>
  // Before the ClickClacks tag, after the queue stub.
  if (navigator.doNotTrack === '1') window.clickclacks('optOut')
</script>

Next steps