Skip to content

Events Documentation

Consent Pro V2 provides a comprehensive event system for tracking user interactions and consent state changes.

Table of Contents

Event System Overview

V2 features a robust event system that allows you to:

  • Listen for consent changes
  • Track user interactions with the banner
  • Monitor visibility state changes
  • Integrate with analytics platforms
  • Debug and troubleshoot issues

All events fire through multiple channels:

  1. API event listeners - Via on(), off(), once(), subscribe()
  2. Custom DOM events - Dispatched on document with consentpro: prefix
  3. GTM dataLayer events - For Google Tag Manager integration

Subscribing to Events

The recommended way to subscribe to events is through the callback approach:

javascript
window.FinsweetConsentPro = window.FinsweetConsentPro || [];
window.FinsweetConsentPro.push([
  'consent',
  async (instance) => {
    // Subscribe to a specific event
    instance.on('consent-updated', (data) => {
      console.log('Consent updated:', data);

      if (data.choices.analytics) {
        initializeAnalytics();
      }
    });

    // Fire only once
    instance.once('banner-shown', (data) => {
      console.log('Banner shown for the first time:', data);
    });

    // Subscribe to all events
    const unsubscribe = instance.subscribe((event, data) => {
      console.log(`Event: ${event}`, data);

      // Send to analytics
      analytics.track(`consentpro:${event}`, data);
    });

    // Later: unsubscribe
    // unsubscribe();
  },
]);

Alternative: Direct API Access

You can also subscribe to events directly via window.FinsweetConsentProAPI (see API Reference for details):

javascript
// Basic subscription
window.FinsweetConsentProAPI.on('consent-updated', (data) => {
  console.log('Consent updated:', data);
});

// One-time subscription
window.FinsweetConsentProAPI.once('ready', (data) => {
  console.log('Initialized:', data);
});

// Universal subscription
const unsubscribe = window.FinsweetConsentProAPI.subscribe((event, data) => {
  console.log(`Event: ${event}`, data);
});

Note: Majority of examples in this documentation use the direct access approach. Both methods work identically and callback approach is recommended for most use cases.

Event Types

ready

Fired when Consent Pro is fully initialized and ready to use.

When: After initialization completes Frequency: Once per load

Event Data:

typescript
{
  version: string; // Script version
  instanceCount: number; // Number of instances
  instance: string; // Active instance key
  config: InstanceSettings; // Instance configuration
  detectedRegion: string; // Detected user region
  initTime: number; // Initialization timestamp
}

Example:

javascript
window.FinsweetConsentProAPI.on('ready', (data) => {
  console.log('Consent Pro v' + data.version + ' initialized');
  console.log('Region detected:', data.detectedRegion);
  console.log('Init time:', data.initTime);
  console.log('Instance:', data.instance);
});

Use Cases:

  • Wait for initialization before checking consent
  • Log initialization metrics
  • Display region-specific messages
  • Initialize dependent systems

Fired whenever consent choices change.

When: User accepts/rejects, programmatic update, preference changes Frequency: Every consent change

Event Data:

typescript
{
  instanceKey: string;
  consent: ConsentRecord;         // New consent record
  choices: ConsentChoices;        // New choices
  previousChoices?: ConsentChoices;  // Previous state
  source: 'banner' | 'preferences' | 'form' | 'api';
  record: ConsentRecord;          // Same as consent
  consentModes?: Record<string, 'granted' | 'denied'>;  // GTM consent modes
}

Example:

javascript
window.FinsweetConsentProAPI.on('consent-updated', (data) => {
  console.log('Consent ID:', data.consent.id);
  console.log('New choices:', data.choices);
  console.log('Previous choices:', data.previousChoices);
  console.log('Changed via:', data.source);
  console.log('GTM consent modes:', data.consentModes);

  // Check what changed
  if (data.choices.analytics !== data.previousChoices?.analytics) {
    console.log('Analytics consent changed');

    if (data.choices.analytics) {
      initializeAnalytics();
    }
  }

  if (data.choices.marketing && !data.previousChoices?.marketing) {
    console.log('Marketing consent newly granted');
    loadMarketingScripts();
  }
});

Use Cases:

  • Initialize analytics when consent granted
  • Load marketing scripts conditionally
  • Update UI based on consent state
  • Sync consent to backend
  • Track consent changes in analytics

Fired when existing consent is loaded from storage.

When: On initialization, if consent exists in storage and hasn't expired Frequency: Once per load (if consent exists)

Event Data:

typescript
{
  instanceKey: string;
  consent: ConsentRecord;
}

Example:

javascript
window.FinsweetConsentProAPI.on('consent-loaded', (data) => {
  console.log('Loaded existing consent:', data.consent);
  console.log('User already consented on:', new Date(data.consent.timestamp));

  // Initialize systems based on stored consent
  if (data.consent.choices.analytics) {
    initializeAnalytics();
  }
});

Use Cases:

  • Fast-path initialization for returning users
  • Restore user preferences
  • Skip banner display for returning users
  • Initialize services immediately

Fired when consent is reset to defaults.

When: User resets preferences, programmatic reset, expired consent Frequency: When reset occurs

Event Data:

typescript
{
  instanceKey: string;
  consent: ConsentRecord; // New default consent
}

Example:

javascript
window.FinsweetConsentProAPI.on('consent-reset', (data) => {
  console.log('Consent reset for:', data.instanceKey);
  console.log('New defaults:', data.consent.choices);

  // Clean up analytics
  if (window.gtag) {
    gtag('consent', 'update', {
      analytics_storage: 'denied',
      ad_storage: 'denied',
    });
  }

  // Show banner again
  window.FinsweetConsentProAPI.showBanner();
});

Use Cases:

  • Clean up analytics sessions
  • Reset tracking scripts
  • Show banner after reset
  • Log reset events

Fired when the consent banner becomes visible.

When: Banner is displayed to user Frequency: Each time banner is shown

Event Data:

typescript
{
  instanceKey: string;
  reason: 'first-visit' | 'manual' | 'api' | 'expired';
}

Example:

javascript
window.FinsweetConsentProAPI.on('banner-shown', (data) => {
  console.log('Banner shown:', data.instanceKey);
  console.log('Reason:', data.reason);

  // Track banner impression
  analytics.track('consent_banner_shown', {
    instance: data.instanceKey,
    reason: data.reason,
    timestamp: Date.now(),
  });

  // Pause video if banner shown
  if (data.reason === 'api') {
    pauseBackgroundVideo();
  }
});

Use Cases:

  • Track banner impressions
  • Pause media when banner shows
  • Adjust page layout
  • Analytics tracking

Fired when the consent banner is hidden.

When: Banner dismissed or hidden Frequency: Each time banner is hidden

Event Data:

typescript
{
  instanceKey: string;
  reason: 'manual' | 'api' | 'consent-given';
}

Example:

javascript
window.FinsweetConsentProAPI.on('banner-hidden', (data) => {
  console.log('Banner hidden:', data.instanceKey);
  console.log('Reason:', data.reason);

  // Resume media
  if (data.reason === 'consent-given') {
    resumeBackgroundVideo();
  }

  // Track banner dismissal
  analytics.track('consent_banner_hidden', {
    reason: data.reason,
  });
});

Use Cases:

  • Resume paused media
  • Restore page layout
  • Track dismissal events
  • Enable page interactions

preferences-shown

Fired when preferences modal opens.

When: User opens detailed consent preferences Frequency: Each time modal is opened

Event Data:

typescript
{
  instanceKey: string;
  source: 'api' | 'button' | 'link';
}

Example:

javascript
window.FinsweetConsentProAPI.on('preferences-shown', (data) => {
  console.log('Preferences opened:', data.instanceKey);
  console.log('Opened via:', data.source);

  // Track modal opening
  analytics.track('consent_preferences_opened', {
    source: data.source,
    timestamp: Date.now(),
  });

  // Disable page scroll
  document.body.style.overflow = 'hidden';
});

Use Cases:

  • Track preference engagement
  • Disable page scroll
  • Show help tooltips
  • Load preference UI components

preferences-hidden

Fired when preferences modal closes.

When: User closes preferences without saving or after saving Frequency: Each time modal is closed

Event Data:

typescript
{
  instanceKey: string;
  action: 'api' | 'button' | 'save';
}

Example:

javascript
window.FinsweetConsentProAPI.on('preferences-hidden', (data) => {
  console.log('Preferences closed:', data.instanceKey);
  console.log('Closed via:', data.action);

  // Re-enable page scroll
  document.body.style.overflow = '';

  // Track modal closure
  analytics.track('consent_preferences_closed', {
    action: data.action,
    saved: data.action === 'save',
  });
});

Use Cases:

  • Re-enable page scroll
  • Track modal engagement
  • Clean up UI overlays
  • Save analytics

donotsell-clicked

Fired when user interacts with "Do Not Sell" checkbox (CCPA).

When: User clicks DoNotSell checkbox (opt-out/informational modes only) Frequency: Each checkbox interaction

Event Data:

typescript
{
  instanceKey: string;
  mode: 'opt-out' | 'informational';
  donotsellState: boolean;
}

Example:

javascript
window.FinsweetConsentProAPI.on('donotsell-clicked', (data) => {
  console.log('Do Not Sell clicked:', data.instanceKey);
  console.log('Mode:', data.mode);
  console.log('Do Not Sell state:', data.donotsellState);

  if (data.donotsellState) {
    console.log('User opted out of data selling');
    // Track opt-out
    analytics.track('ccpa_donotsell_enabled');
  } else {
    console.log('User allows data selling');
    // Track opt-in
    analytics.track('ccpa_donotsell_disabled');
  }
});

Use Cases:

  • CCPA compliance tracking
  • Update backend systems
  • Display confirmation messages
  • Analytics tracking

Custom DOM Events

All events are also dispatched as custom DOM events with the consentpro: prefix.

Listening to DOM Events

javascript
document.addEventListener('consentpro:consent-updated', (event) => {
  const data = event.detail;
  console.log('Consent updated via DOM event:', data);
});

document.addEventListener('consentpro:banner-shown', (event) => {
  console.log('Banner shown:', event.detail);
});

document.addEventListener('consentpro:ready', (event) => {
  console.log('Ready via DOM event:', event.detail);
});

Benefits of DOM Events

  • Works with any framework (React, Vue, Angular)
  • Integrate with third-party scripts
  • Use with legacy code
  • Event delegation support

Example with React:

jsx
useEffect(() => {
  const handleConsentUpdate = (event) => {
    setConsent(event.detail.choices);
  };

  document.addEventListener('consentpro:consent-updated', handleConsentUpdate);

  return () => {
    document.removeEventListener('consentpro:consent-updated', handleConsentUpdate);
  };
}, []);

GTM Integration Events

Consent Pro automatically fires Google Tag Manager events for consent changes.

Consent Pro automatically updates Google Consent Mode v2 based on cookie category consent. Here's how cookie categories map to consent modes:

Cookie CategoryConsent Modes UpdatedRejectAccept
Essentialsecurity_storageGrantedAlways Granted
Analyticsanalytics_storageDeniedGranted
Marketingad_storage
ad_user_data
ad_personalization
DeniedGranted
Personalizationfunctionality_storage
personalization_storage
DeniedGranted

Example: When a user grants Analytics consent, the following consent mode is updated:

javascript
gtag('consent', 'update', {
  analytics_storage: 'granted',
});

Example: When a user grants Marketing consent, multiple consent modes are updated:

javascript
gtag('consent', 'update', {
  ad_storage: 'granted',
  ad_user_data: 'granted',
  ad_personalization: 'granted',
});

Note: In opt-out and informational modes, all non-essential categories default to granted until the user opts out.

Category Activation Events

When a category is granted consent, a custom GTM event is fired:

  • analytics-activated - Analytics consent granted
  • marketing-activated - Marketing consent granted
  • personalization-activated - Personalization consent granted
  • essential-activated - Essential consent granted
  • uncategorized-activated - All non-essential consents granted

Example GTM Trigger:

javascript
// GTM Trigger: Custom Event
// Event name: analytics-activated

// This will fire when user grants analytics consent
console.log('Analytics activated - GTM tag will fire');

A special event fired whenever consent mode changes:

javascript
window.dataLayer.push({
  event: 'fs-consent-consentModeUpdate',
});

Use Case in GTM: Create a trigger on this event to fire tags when consent changes:

Trigger Type: Custom Event
Event name: fs-consent-consentModeUpdate

Listening to GTM Events

javascript
// Listen to dataLayer pushes
const originalPush = window.dataLayer.push;
window.dataLayer.push = function () {
  const result = originalPush.apply(this, arguments);

  const event = arguments[0];
  if (event.event?.includes('activated')) {
    console.log('Consent category activated:', event.event);
  }

  return result;
};

Event Patterns

Wait for Initialization

Recommended: Using callbacks (automatically waits for initialization)

javascript
window.FinsweetConsentPro = window.FinsweetConsentPro || [];
window.FinsweetConsentPro.push([
  'consent',
  async (instance) => {
    // Code here runs automatically after initialization
    const consent = instance.getConsent();
    console.log('Ready with consent:', consent);

    initializeServices();
  },
]);

Alternative: Using direct API access

javascript
// Pattern 1: Using ready event
window.FinsweetConsentProAPI.once('ready', () => {
  const consent = window.FinsweetConsentProAPI.getConsent();
  console.log('Ready with consent:', consent);
});

// Pattern 2: Check if already ready
if (window.FinsweetConsentProAPI?.isReady()) {
  initializeServices();
} else {
  window.FinsweetConsentProAPI.once('ready', initializeServices);
}

Conditional Script Loading

Recommended: Using callbacks

javascript
window.FinsweetConsentPro = window.FinsweetConsentPro || [];
window.FinsweetConsentPro.push([
  'consent',
  async (instance) => {
    // Check existing consent on load
    const currentConsent = instance.getConsent();
    if (currentConsent?.choices.analytics && !window.analyticsLoaded) {
      loadAnalyticsScript();
      window.analyticsLoaded = true;
    }

    // Listen for consent updates
    instance.on('consent-updated', (data) => {
      const { choices } = data;

      // Load scripts based on consent
      if (choices.analytics && !window.analyticsLoaded) {
        loadAnalyticsScript();
        window.analyticsLoaded = true;
      }

      if (choices.marketing && !window.marketingLoaded) {
        loadMarketingPixels();
        window.marketingLoaded = true;
      }
    });
  },
]);

Alternative: Using direct API access

javascript
window.FinsweetConsentProAPI.on('consent-updated', (data) => {
  const { choices } = data;

  if (choices.analytics && !window.analyticsLoaded) {
    loadAnalyticsScript();
    window.analyticsLoaded = true;
  }

  if (choices.marketing && !window.marketingLoaded) {
    loadMarketingPixels();
    window.marketingLoaded = true;
  }
});

window.FinsweetConsentProAPI.once('consent-loaded', (data) => {
  const { choices } = data.consent;

  if (choices.analytics) {
    loadAnalyticsScript();
    window.analyticsLoaded = true;
  }
});

Track All User Interactions

Recommended: Using callbacks

javascript
window.FinsweetConsentPro = window.FinsweetConsentPro || [];
window.FinsweetConsentPro.push([
  'consent',
  async (instance) => {
    const consentEvents = [
      'banner-shown',
      'banner-hidden',
      'preferences-shown',
      'preferences-hidden',
      'consent-updated',
      'donotsell-clicked',
    ];

    consentEvents.forEach((event) => {
      instance.on(event, (data) => {
        // Send to analytics
        analytics.track(`consent_${event}`, {
          ...data,
          timestamp: Date.now(),
          page: window.location.pathname,
        });
      });
    });

    // Or use subscribe for all events
    instance.subscribe((event, data) => {
      analytics.track(`consent_${event}`, {
        ...data,
        timestamp: Date.now(),
        page: window.location.pathname,
      });
    });
  },
]);

Debug All Events

javascript
if (window.location.search.includes('debug')) {
  window.FinsweetConsentProAPI.subscribe((event, data) => {
    console.group(`ConsentPro Event: ${event}`);
    console.log('Data:', data);
    console.log('Timestamp:', new Date().toISOString());
    console.groupEnd();
  });
}

React Hook Pattern

Recommended: Using callback approach

jsx
import { useEffect, useState } from 'react';

function useConsentPro() {
  const [consent, setConsent] = useState(null);
  const [isReady, setIsReady] = useState(false);

  useEffect(() => {
    window.FinsweetConsentPro = window.FinsweetConsentPro || [];
    window.FinsweetConsentPro.push([
      'consent',
      async (instance) => {
        setIsReady(true);
        const current = instance.getConsent();
        setConsent(current);

        // Listen for updates
        const unsubscribe = instance.on('consent-updated', (data) => {
          setConsent(data.consent);
        });

        // Cleanup function
        return () => {
          unsubscribe();
        };
      },
    ]);
  }, []);

  return { consent, isReady };
}

// Usage
function MyComponent() {
  const { consent, isReady } = useConsentPro();

  if (!isReady) return <div>Loading consent...</div>;

  return (
    <div>
      <p>Analytics: {consent?.choices.analytics ? 'Enabled' : 'Disabled'}</p>
      <p>Marketing: {consent?.choices.marketing ? 'Enabled' : 'Disabled'}</p>
    </div>
  );
}

Alternative: Using direct API access

jsx
import { useEffect, useState } from 'react';

function useConsentPro() {
  const [consent, setConsent] = useState(null);
  const [isReady, setIsReady] = useState(false);

  useEffect(() => {
    const unsubReady = window.FinsweetConsentProAPI?.once('ready', () => {
      setIsReady(true);
      const current = window.FinsweetConsentProAPI.getConsent();
      setConsent(current);
    });

    const unsubUpdate = window.FinsweetConsentProAPI?.on('consent-updated', (data) => {
      setConsent(data.consent);
    });

    return () => {
      unsubReady?.();
      unsubUpdate?.();
    };
  }, []);

  return { consent, isReady };
}

Vue Composable Pattern

Recommended: Using callback approach

javascript
import { ref, onMounted } from 'vue';

export function useConsentPro() {
  const consent = ref(null);
  const isReady = ref(false);

  onMounted(() => {
    window.FinsweetConsentPro = window.FinsweetConsentPro || [];
    window.FinsweetConsentPro.push([
      'consent',
      async (instance) => {
        isReady.value = true;
        consent.value = instance.getConsent();

        // Listen for updates
        instance.on('consent-updated', (data) => {
          consent.value = data.consent;
        });
      },
    ]);
  });

  return { consent, isReady };
}

Alternative: Using direct API access

javascript
import { ref, onMounted, onUnmounted } from 'vue';

export function useConsentPro() {
  const consent = ref(null);
  const isReady = ref(false);

  let unsubscribers = [];

  onMounted(() => {
    const api = window.FinsweetConsentProAPI;

    unsubscribers.push(
      api?.once('ready', () => {
        isReady.value = true;
        consent.value = api.getConsent();
      })
    );

    unsubscribers.push(
      api?.on('consent-updated', (data) => {
        consent.value = data.consent;
      })
    );
  });

  onUnmounted(() => {
    unsubscribers.forEach((unsub) => unsub?.());
  });

  return { consent, isReady };
}

Next Steps