[{"data":1,"prerenderedAt":139},["ShallowReactive",2],{"doc:\u002Fdocs\u002Fconsent-tools":3},{"page":4,"toc":129,"updated":138},{"path":5,"title":6,"seoTitle":7,"description":8,"blocks":9},"\u002Fdocs\u002Fconsent-tools","Consent tools","Connect Fingerly to OneTrust, Cookiebot and Google Consent Mode","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.",[10,13,17,22,29,36,39,41,46,49,51,56,59,61,66,69,72,77,80,82,107,113,116,118,121,126],{"type":11,"text":12},"p","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.",{"type":14,"tone":15,"text":16},"callout","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](\u002Fdocs\u002Fprivacy-and-consent).",{"type":18,"level":19,"text":20,"id":21},"heading",2,"The pattern","the-pattern",{"type":23,"samples":24},"code",[25],{"label":26,"lang":27,"code":28},"fingerly.ts","ts","import { load } from '@fingerly\u002Fweb-js'\n\n\u002F\u002F Nothing is read or sent until setConsent('granted').\nexport const fingerly = await load({ apiKey: 'fly_pk_us_production_…', consent: 'pending' })",{"type":30,"items":31},"list",[32,33,34,35],"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.",{"type":18,"level":19,"text":37,"id":38},"OneTrust","onetrust",{"type":11,"text":40},"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.",{"type":23,"samples":42},[43],{"label":44,"lang":27,"code":45},"onetrust.ts","import { fingerly } from '.\u002Ffingerly'\n\n\u002F\u002F What OneTrust puts on the page, for TypeScript.\ndeclare global {\n  interface Window {\n    OnetrustActiveGroups?: string\n    OptanonWrapper?: () => void\n    OneTrust?: unknown\n  }\n}\n\nconst CATEGORY = 'C0003'   \u002F\u002F the OneTrust category you assigned Fingerly to\n\nfunction applyOneTrust() {\n  const accepted = (window.OnetrustActiveGroups ?? '').split(',').includes(CATEGORY)\n  fingerly.setConsent(accepted ? 'granted' : 'denied')\n}\n\n\u002F\u002F Keep any OptanonWrapper already on the page.\nconst previous = window.OptanonWrapper\nwindow.OptanonWrapper = () => {\n  previous?.()\n  applyOneTrust()\n}\n\nif (window.OneTrust) applyOneTrust()   \u002F\u002F the banner loaded before this code",{"type":18,"level":19,"text":47,"id":48},"Cookiebot","cookiebot",{"type":11,"text":50},"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.",{"type":23,"samples":52},[53],{"label":54,"lang":27,"code":55},"cookiebot.ts","import { fingerly } from '.\u002Ffingerly'\n\n\u002F\u002F What Cookiebot puts on the page, for TypeScript.\ndeclare global {\n  interface Window {\n    Cookiebot?: { hasResponse?: boolean; consent?: Record\u003Cstring, boolean> }\n  }\n}\n\nconst CATEGORY = 'preferences'   \u002F\u002F necessary, preferences, statistics or marketing\n\nfunction applyCookiebot() {\n  const accepted = Boolean(window.Cookiebot?.consent?.[CATEGORY])\n  fingerly.setConsent(accepted ? 'granted' : 'denied')\n}\n\nwindow.addEventListener('CookiebotOnConsentReady', applyCookiebot)\nif (window.Cookiebot?.hasResponse) applyCookiebot()   \u002F\u002F answered before this code ran",{"type":18,"level":19,"text":57,"id":58},"Google Consent Mode","google-consent-mode",{"type":11,"text":60},"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.",{"type":23,"samples":62},[63],{"label":64,"lang":27,"code":65},"consent.ts","import { fingerly } from '.\u002Ffingerly'\n\nexport function saveConsent(choices: { security: boolean; analytics: boolean; ads: boolean }) {\n  const state = (allowed: boolean) => (allowed ? 'granted' : 'denied')\n\n  gtag('consent', 'update', {\n    security_storage: state(choices.security),\n    analytics_storage: state(choices.analytics),\n    ad_storage: state(choices.ads),\n    ad_user_data: state(choices.ads),\n    ad_personalization: state(choices.ads),\n  })\n\n  fingerly.setConsent(state(choices.security))\n}",{"type":14,"tone":67,"text":68},"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.",{"type":18,"level":19,"text":70,"id":71},"Your own banner","your-own-banner",{"type":23,"samples":73},[74],{"label":75,"lang":27,"code":76},"banner.ts","import { fingerly } from '.\u002Ffingerly'\n\nconst saved = localStorage.getItem('consent.fraud-prevention')\nif (saved) fingerly.setConsent(saved === 'yes' ? 'granted' : 'denied')\n\nacceptButton.addEventListener('click', () => {\n  localStorage.setItem('consent.fraud-prevention', 'yes')\n  fingerly.setConsent('granted')\n})",{"type":18,"level":19,"text":78,"id":79},"Frameworks","frameworks",{"type":11,"text":81},"The framework SDKs take the same `consent` option, and wait for it: identification set to run on mount starts once consent becomes `granted`.",{"type":83,"columns":84,"rows":88},"table",[85,86,87],"SDK","Starting state","Change it with",[89,93,97,100,103],[90,91,92],"[React](\u002Fdocs\u002Fsdks\u002Freact), [Next.js](\u002Fdocs\u002Fsdks\u002Fnextjs)","`\u003CFingerlyProvider consent=\"pending\">`","The `consent` prop, or `useFingerly().setConsent()`",[94,95,96],"[Vue](\u002Fdocs\u002Fsdks\u002Fvue)","`createFingerly({ apiKey, consent: 'pending' })`","`useFingerly().setConsent()`",[98,99,96],"[Nuxt](\u002Fdocs\u002Fsdks\u002Fnuxt)","`fingerly: { consent: 'pending' }` in `nuxt.config`",[101,102,96],"[Svelte](\u002Fdocs\u002Fsdks\u002Fsvelte)","`setupFingerly({ apiKey, consent: 'pending' })`",[104,105,106],"[Angular](\u002Fdocs\u002Fsdks\u002Fangular)","`provideFingerly({ apiKey, consent: 'pending' })`","`injectFingerly().setConsent()`",{"type":23,"samples":108},[109],{"label":110,"lang":111,"code":112},"React","tsx","import { useEffect } from 'react'\nimport { useFingerly } from '@fingerly\u002Freact'\n\n\u002F\u002F Window.OnetrustActiveGroups, OptanonWrapper and OneTrust are declared\n\u002F\u002F as in onetrust.ts.\n\nexport function OneTrustBridge() {\n  const fingerly = useFingerly()\n\n  useEffect(() => {\n    const apply = () => {\n      const accepted = (window.OnetrustActiveGroups ?? '').split(',').includes('C0003')\n      fingerly.setConsent(accepted ? 'granted' : 'denied')\n    }\n    const previous = window.OptanonWrapper\n    window.OptanonWrapper = () => {\n      previous?.()\n      apply()\n    }\n    if (window.OneTrust) apply()\n  }, [fingerly])\n\n  return null\n}",{"type":18,"level":19,"text":114,"id":115},"Mobile apps","mobile-apps",{"type":11,"text":117},"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](\u002Fdocs\u002Fprivacy-and-consent#consent).",{"type":18,"level":19,"text":119,"id":120},"Check that it works","check-that-it-works",{"type":30,"items":122},[123,124,125],"Before consent, the browser's Network panel shows no request to `\u002Fapi\u002Fv1\u002Fidentify`, 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.",{"type":14,"tone":127,"text":128},"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](\u002Fdocs\u002Fserver-side-verification#without-a-request-id).",[130,131,132,133,134,135,136,137],{"id":21,"text":20,"level":19},{"id":38,"text":37,"level":19},{"id":48,"text":47,"level":19},{"id":58,"text":57,"level":19},{"id":71,"text":70,"level":19},{"id":79,"text":78,"level":19},{"id":115,"text":114,"level":19},{"id":120,"text":119,"level":19},"2026-09-17T16:58:12.000Z",1789667797515]