Guides

Consent tools

Gate identification behind your consent tool: load the SDK with consent pending, then pass on the answer from OneTrust, Cookiebot, Google Consent Mode or your own banner.

Every Fingerly SDK takes a consent state. Until it is granted, the SDK reads nothing from the device, sends nothing, and identify() rejects with a ConsentError. The SDKs do not talk to consent tools themselves: your code passes the tool's answer on with setConsent(). This page shows that code for common tools.

The pattern

fingerly.ts
import { load } from '@fingerly/web-js'

// Nothing is read or sent until setConsent('granted').
export const fingerly = await load({ apiKey: 'fly_pk_us_production_…', consent: 'pending' })
  • Load with pending before your consent tool has answered.
  • Call setConsent('granted') when the visitor accepts the category you put Fingerly in, and setConsent('denied') when they refuse or withdraw it. pending and denied behave the same.
  • Apply the tool's answer on page load too: a returning visitor's choice is already stored.
  • Withdrawing consent stops an identification that is already running before anything more is sent.

OneTrust

OneTrust lists the categories a visitor has accepted in OnetrustActiveGroups, and calls OptanonWrapper() when its banner loads and every time consent changes. Use the ID of the category you assigned Fingerly to, such as C0003 for Functional cookies in OneTrust's default categories.

onetrust.ts
import { fingerly } from './fingerly'

// What OneTrust puts on the page, for TypeScript.
declare global {
  interface Window {
    OnetrustActiveGroups?: string
    OptanonWrapper?: () => void
    OneTrust?: unknown
  }
}

const CATEGORY = 'C0003'   // the OneTrust category you assigned Fingerly to

function applyOneTrust() {
  const accepted = (window.OnetrustActiveGroups ?? '').split(',').includes(CATEGORY)
  fingerly.setConsent(accepted ? 'granted' : 'denied')
}

// Keep any OptanonWrapper already on the page.
const previous = window.OptanonWrapper
window.OptanonWrapper = () => {
  previous?.()
  applyOneTrust()
}

if (window.OneTrust) applyOneTrust()   // the banner loaded before this code

Cookiebot

Cookiebot exposes the visitor's answer per category on Cookiebot.consent, and fires CookiebotOnConsentReady on the window once consent is known, including after every change.

cookiebot.ts
import { fingerly } from './fingerly'

// What Cookiebot puts on the page, for TypeScript.
declare global {
  interface Window {
    Cookiebot?: { hasResponse?: boolean; consent?: Record<string, boolean> }
  }
}

const CATEGORY = 'preferences'   // necessary, preferences, statistics or marketing

function applyCookiebot() {
  const accepted = Boolean(window.Cookiebot?.consent?.[CATEGORY])
  fingerly.setConsent(accepted ? 'granted' : 'denied')
}

window.addEventListener('CookiebotOnConsentReady', applyCookiebot)
if (window.Cookiebot?.hasResponse) applyCookiebot()   // answered before this code ran

Consent Mode passes consent to Google's tags; it gives other scripts no supported way to read it. Set Fingerly's state in the same place you call gtag('consent', 'update'). The closest Consent Mode type is security_storage, which Google describes as covering fraud prevention.

consent.ts
import { fingerly } from './fingerly'

export function saveConsent(choices: { security: boolean; analytics: boolean; ads: boolean }) {
  const state = (allowed: boolean) => (allowed ? 'granted' : 'denied')

  gtag('consent', 'update', {
    security_storage: state(choices.security),
    analytics_storage: state(choices.analytics),
    ad_storage: state(choices.ads),
    ad_user_data: state(choices.ads),
    ad_personalization: state(choices.ads),
  })

  fingerly.setConsent(state(choices.security))
}

Your own banner

banner.ts
import { fingerly } from './fingerly'

const saved = localStorage.getItem('consent.fraud-prevention')
if (saved) fingerly.setConsent(saved === 'yes' ? 'granted' : 'denied')

acceptButton.addEventListener('click', () => {
  localStorage.setItem('consent.fraud-prevention', 'yes')
  fingerly.setConsent('granted')
})

Frameworks

The framework SDKs take the same consent option, and wait for it: identification set to run on mount starts once consent becomes granted.

SDKStarting stateChange it with
React, Next.js<FingerlyProvider consent="pending">The consent prop, or useFingerly().setConsent()
VuecreateFingerly({ apiKey, consent: 'pending' })useFingerly().setConsent()
Nuxtfingerly: { consent: 'pending' } in nuxt.configuseFingerly().setConsent()
SveltesetupFingerly({ apiKey, consent: 'pending' })useFingerly().setConsent()
AngularprovideFingerly({ apiKey, consent: 'pending' })injectFingerly().setConsent()
React
import { useEffect } from 'react'
import { useFingerly } from '@fingerly/react'

// Window.OnetrustActiveGroups, OptanonWrapper and OneTrust are declared
// as in onetrust.ts.

export function OneTrustBridge() {
  const fingerly = useFingerly()

  useEffect(() => {
    const apply = () => {
      const accepted = (window.OnetrustActiveGroups ?? '').split(',').includes('C0003')
      fingerly.setConsent(accepted ? 'granted' : 'denied')
    }
    const previous = window.OptanonWrapper
    window.OptanonWrapper = () => {
      previous?.()
      apply()
    }
    if (window.OneTrust) apply()
  }, [fingerly])

  return null
}

Mobile apps

The iOS, Android, React Native, Flutter and Lynx SDKs take the same states. Load with pending, and call setConsent from your consent SDK's callback. In React Native, Flutter and Lynx the state is shared by the whole app, so pass it on every load. See the samples on privacy and consent.

Check that it works

  • Before consent, the browser's Network panel shows no request to /api/v1/identify, and identify() rejects with a ConsentError whose code is consent_required.
  • After accepting, the next identification succeeds without reloading the page.
  • After withdrawing, identification stops again.