# 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.

Last updated: 2026-09-17

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.

> **Warning:** Whether identification for fraud prevention needs consent, and which category it belongs in, is a legal decision for you. If your assessment is that it does not need consent, leave `consent` at its default, `granted`, and skip this page. See [privacy and consent](https://docs.fingerly.io/docs/privacy-and-consent).

## The pattern

```ts 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.

```ts 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.

```ts 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
```

## Google Consent Mode

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.

```ts 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))
}
```

> **Tip:** If a consent platform such as OneTrust or Cookiebot sets Consent Mode for you, read the answer from that platform instead, as in the sections above.

## Your own banner

```ts 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`.

| SDK | Starting state | Change it with |
| --- | --- | --- |
| [React](https://docs.fingerly.io/docs/sdks/react), [Next.js](https://docs.fingerly.io/docs/sdks/nextjs) | `<FingerlyProvider consent="pending">` | The `consent` prop, or `useFingerly().setConsent()` |
| [Vue](https://docs.fingerly.io/docs/sdks/vue) | `createFingerly({ apiKey, consent: 'pending' })` | `useFingerly().setConsent()` |
| [Nuxt](https://docs.fingerly.io/docs/sdks/nuxt) | `fingerly: { consent: 'pending' }` in `nuxt.config` | `useFingerly().setConsent()` |
| [Svelte](https://docs.fingerly.io/docs/sdks/svelte) | `setupFingerly({ apiKey, consent: 'pending' })` | `useFingerly().setConsent()` |
| [Angular](https://docs.fingerly.io/docs/sdks/angular) | `provideFingerly({ apiKey, consent: 'pending' })` | `injectFingerly().setConsent()` |

```tsx 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](https://docs.fingerly.io/docs/privacy-and-consent#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.

> **Note:** Without consent there is no request ID. Your server should decide the action with less evidence, the same way it does when identification fails. See [server-side verification](https://docs.fingerly.io/docs/server-side-verification#without-a-request-id).
