Cookie Consent Management for ChatGPT Apps: GDPR Compliance Guide
Cookie consent management is a critical privacy requirement for ChatGPT apps that track user behavior, store preferences, or integrate third-party analytics. The General Data Protection Regulation (GDPR) and ePrivacy Directive mandate explicit user consent before placing non-essential cookies, making robust consent management infrastructure essential for European users and global compliance.
This comprehensive guide demonstrates production-ready cookie consent implementation for ChatGPT apps, covering consent banner design, preference management, cookie blocking, Google Consent Mode integration, and compliance monitoring. Whether building a fitness booking assistant, restaurant reservation system, or e-commerce chatbot, this article provides the technical foundation for GDPR-compliant cookie management.
Cookie consent isn't just about legal compliance—it builds user trust, demonstrates respect for privacy, and differentiates your ChatGPT app in a privacy-conscious market. According to the Interactive Advertising Bureau (IAB), 73% of users are more likely to engage with apps that provide transparent cookie controls, while 82% abandon apps with confusing or deceptive cookie consent mechanisms.
Understanding GDPR Cookie Requirements
The GDPR and ePrivacy Directive establish strict requirements for cookie consent in ChatGPT apps. Understanding these legal obligations prevents costly fines (up to €20 million or 4% of annual global turnover) and builds user trust.
Cookie Categories and Classification
GDPR distinguishes between strictly necessary cookies (exempt from consent) and non-essential cookies (requiring explicit opt-in consent). Strictly necessary cookies enable core functionality like session management, authentication, and load balancing—ChatGPT apps can set these without consent. Non-essential cookies include analytics (Google Analytics, Mixpanel), marketing (Facebook Pixel, Google Ads), and personalization (recommendation engines, A/B testing).
Categorize cookies into four standard groups: Strictly Necessary (session tokens, CSRF protection), Functional (language preferences, accessibility settings), Analytics (user behavior tracking, performance monitoring), and Marketing (advertising, retargeting, social media integration). This classification enables granular consent controls and aligns with IAB Transparency and Consent Framework (TCF) standards.
Consent Requirements and Legal Basis
GDPR Article 6 requires one of six legal bases for data processing: consent (most common for cookies), contractual necessity (rare for cookies), legal obligation, vital interests, public task, or legitimate interest. For non-essential cookies, explicit opt-in consent is mandatory—pre-ticked checkboxes, implied consent, or cookie walls violate GDPR.
Valid consent requires four elements: freely given (no coercion or detriment for refusing), specific (separate consent per cookie category), informed (clear description of data collection), and unambiguous (affirmative action required). Users must be able to withdraw consent as easily as granting it, and consent records must be retained for regulatory audits.
The ePrivacy Directive (2002/58/EC) specifically addresses cookies, requiring prior informed consent except for strictly necessary cookies. National implementations vary—France's CNIL enforces strict cookie walls prohibition, while UK ICO allows limited legitimate interest for analytics cookies.
Opt-In vs Opt-Out Mechanisms
GDPR mandates opt-in consent for non-essential cookies—users must actively agree before cookies are set. Opt-out mechanisms (cookies set by default, users must disable) violate GDPR and trigger enforcement actions. ChatGPT apps must block analytics, marketing, and personalization cookies until users explicitly consent.
Implement granular consent controls enabling users to accept or reject specific cookie categories. A binary "Accept All" or "Reject All" choice is legally insufficient—users need category-level control. Progressive disclosure (simple banner with "Manage Preferences" link) balances user experience with compliance requirements.
Consent refresh is required when cookie purposes change, new vendors are added, or consent expires (recommended 12-month maximum). Store consent timestamps, versions, and specific categories accepted to demonstrate GDPR compliance during regulatory audits.
Designing Cookie Consent Banners
Cookie consent banners are the primary interface for GDPR compliance in ChatGPT apps. Effective banners balance legal requirements with user experience, accessibility, and conversion optimization.
UI Design and Positioning
Position consent banners prominently without blocking ChatGPT conversation interfaces. Bottom bar banners work well for mobile ChatGPT apps, while modal overlays suit desktop web experiences. Avoid intrusive designs that prevent chat interaction before consent—GDPR prohibits "cookie walls" that block access to services.
Design banners with clear visual hierarchy: heading ("Cookie Consent" or "Privacy Preferences"), concise description (1-2 sentences explaining cookie usage), category toggles (expandable sections for granular control), and action buttons ("Accept All", "Reject All", "Save Preferences"). Use accessible color contrast (WCAG AA minimum), readable font sizes (16px minimum), and touch-friendly button sizes (44px minimum).
Include privacy policy links and cookie policy details within the banner. GDPR requires users to access detailed information before consenting—link to dedicated cookie policy pages explaining data retention, third-party vendors, and user rights.
// Cookie Consent Banner Component (React + TypeScript)
import React, { useState, useEffect } from 'react';
import './CookieConsent.css';
interface CookieCategory {
id: string;
name: string;
description: string;
required: boolean;
enabled: boolean;
}
interface ConsentBannerProps {
onConsent: (categories: string[]) => void;
language?: string;
}
const CookieConsentBanner: React.FC<ConsentBannerProps> = ({
onConsent,
language = 'en'
}) => {
const [isVisible, setIsVisible] = useState(false);
const [showDetails, setShowDetails] = useState(false);
const [categories, setCategories] = useState<CookieCategory[]>([
{
id: 'necessary',
name: 'Strictly Necessary',
description: 'Essential cookies for authentication, session management, and security. Cannot be disabled.',
required: true,
enabled: true
},
{
id: 'functional',
name: 'Functional',
description: 'Cookies for language preferences, accessibility settings, and user interface customization.',
required: false,
enabled: false
},
{
id: 'analytics',
name: 'Analytics',
description: 'Cookies for usage analytics, performance monitoring, and user behavior tracking.',
required: false,
enabled: false
},
{
id: 'marketing',
name: 'Marketing',
description: 'Cookies for advertising, retargeting, and social media integration.',
required: false,
enabled: false
}
]);
useEffect(() => {
// Check for existing consent
const existingConsent = localStorage.getItem('cookie_consent');
if (!existingConsent) {
setIsVisible(true);
}
}, []);
const handleAcceptAll = () => {
const allCategories = categories.map(cat => cat.id);
saveConsent(allCategories);
setIsVisible(false);
onConsent(allCategories);
};
const handleRejectAll = () => {
const necessaryOnly = categories
.filter(cat => cat.required)
.map(cat => cat.id);
saveConsent(necessaryOnly);
setIsVisible(false);
onConsent(necessaryOnly);
};
const handleSavePreferences = () => {
const acceptedCategories = categories
.filter(cat => cat.enabled)
.map(cat => cat.id);
saveConsent(acceptedCategories);
setIsVisible(false);
onConsent(acceptedCategories);
};
const saveConsent = (acceptedCategories: string[]) => {
const consentRecord = {
categories: acceptedCategories,
timestamp: new Date().toISOString(),
version: '1.0.0',
language: language
};
localStorage.setItem('cookie_consent', JSON.stringify(consentRecord));
};
const toggleCategory = (categoryId: string) => {
setCategories(prev =>
prev.map(cat =>
cat.id === categoryId && !cat.required
? { ...cat, enabled: !cat.enabled }
: cat
)
);
};
if (!isVisible) return null;
return (
<div className="cookie-consent-overlay" role="dialog" aria-labelledby="cookie-consent-title" aria-modal="true">
<div className="cookie-consent-banner">
<h2 id="cookie-consent-title" className="consent-title">
Cookie Consent
</h2>
<p className="consent-description">
We use cookies to enhance your ChatGPT app experience, analyze usage patterns,
and personalize content. You can customize your cookie preferences below.
</p>
{!showDetails ? (
<div className="consent-actions-simple">
<button
className="btn btn-primary"
onClick={handleAcceptAll}
aria-label="Accept all cookies"
>
Accept All
</button>
<button
className="btn btn-secondary"
onClick={handleRejectAll}
aria-label="Reject non-essential cookies"
>
Reject All
</button>
<button
className="btn btn-text"
onClick={() => setShowDetails(true)}
aria-label="Manage cookie preferences"
>
Manage Preferences
</button>
</div>
) : (
<div className="consent-details">
<div className="consent-categories">
{categories.map(category => (
<div key={category.id} className="consent-category">
<div className="category-header">
<label className="category-toggle">
<input
type="checkbox"
checked={category.enabled}
disabled={category.required}
onChange={() => toggleCategory(category.id)}
aria-label={`${category.name} cookies`}
/>
<span className="category-name">{category.name}</span>
{category.required && (
<span className="category-badge">Required</span>
)}
</label>
</div>
<p className="category-description">{category.description}</p>
</div>
))}
</div>
<div className="consent-actions-detailed">
<button
className="btn btn-primary"
onClick={handleSavePreferences}
aria-label="Save cookie preferences"
>
Save Preferences
</button>
<button
className="btn btn-text"
onClick={() => setShowDetails(false)}
aria-label="Back to simple view"
>
Back
</button>
</div>
</div>
)}
<div className="consent-links">
<a href="/privacy-policy" target="_blank" rel="noopener noreferrer">
Privacy Policy
</a>
<a href="/cookie-policy" target="_blank" rel="noopener noreferrer">
Cookie Policy
</a>
</div>
</div>
</div>
);
};
export default CookieConsentBanner;
Accessibility and Mobile Optimization
Ensure cookie consent banners meet WCAG 2.1 AA accessibility standards. Use semantic HTML (role="dialog", aria-modal="true"), keyboard navigation (Tab, Enter, Escape), and screen reader announcements (aria-live regions for dynamic content). Test with assistive technologies like NVDA, JAWS, and VoiceOver.
Mobile optimization is critical—ChatGPT apps are increasingly mobile-first. Design banners with responsive layouts (stack vertically on small screens), touch-friendly buttons (minimum 44x44px), and readable text (16px minimum font size). Test on iOS and Android devices across various screen sizes.
Implement focus management to trap keyboard focus within the consent modal until users make a choice. When the banner appears, move focus to the heading; when dismissed, return focus to the ChatGPT input field. This prevents users from interacting with the app before consenting.
Language Support and Localization
GDPR requires cookie consent interfaces in users' native languages. Implement automatic language detection based on browser settings (navigator.language) or ChatGPT conversation context. Support minimum EU languages: English, French, German, Spanish, Italian, Dutch, Polish, Romanian.
Translate cookie category names, descriptions, and action buttons accurately. Avoid machine translation for legal text—poor translations undermine GDPR compliance and user trust. Professional translation services like Phrase or Lokalise ensure legal accuracy across languages.
Store language preferences with consent records for audit purposes. If users change language settings, re-display consent banner to ensure they understand cookie policies in their preferred language.
Implementing Preference Management
Granular cookie preference management enables users to control specific cookie categories, withdraw consent, and update preferences post-initial choice. GDPR requires preference management interfaces accessible from any page.
Preference Manager Component
Build a preference center accessible via settings menu, footer links, or cookie badge icons. The preference manager displays current consent state, category descriptions, and toggle controls. Update consent records when users modify preferences, and block/unblock cookies accordingly.
// Cookie Preference Manager (TypeScript)
interface ConsentPreferences {
categories: {
necessary: boolean;
functional: boolean;
analytics: boolean;
marketing: boolean;
};
timestamp: string;
version: string;
language: string;
userAgent: string;
}
class CookiePreferenceManager {
private static readonly STORAGE_KEY = 'cookie_consent';
private static readonly CONSENT_VERSION = '1.0.0';
private static readonly CONSENT_EXPIRY_DAYS = 365;
/**
* Get current consent preferences
*/
static getPreferences(): ConsentPreferences | null {
try {
const stored = localStorage.getItem(this.STORAGE_KEY);
if (!stored) return null;
const preferences: ConsentPreferences = JSON.parse(stored);
// Check if consent has expired
const consentDate = new Date(preferences.timestamp);
const expiryDate = new Date();
expiryDate.setDate(expiryDate.getDate() - this.CONSENT_EXPIRY_DAYS);
if (consentDate < expiryDate) {
this.clearPreferences();
return null;
}
// Check if version has changed
if (preferences.version !== this.CONSENT_VERSION) {
return null;
}
return preferences;
} catch (error) {
console.error('Error reading consent preferences:', error);
return null;
}
}
/**
* Save consent preferences
*/
static savePreferences(categories: string[]): ConsentPreferences {
const preferences: ConsentPreferences = {
categories: {
necessary: true, // Always true
functional: categories.includes('functional'),
analytics: categories.includes('analytics'),
marketing: categories.includes('marketing')
},
timestamp: new Date().toISOString(),
version: this.CONSENT_VERSION,
language: navigator.language,
userAgent: navigator.userAgent
};
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(preferences));
// Trigger consent change event
window.dispatchEvent(new CustomEvent('cookieConsentChange', {
detail: preferences
}));
return preferences;
}
/**
* Update specific category
*/
static updateCategory(category: string, enabled: boolean): void {
const preferences = this.getPreferences();
if (!preferences) return;
const categories: string[] = ['necessary'];
if (preferences.categories.functional) categories.push('functional');
if (preferences.categories.analytics) categories.push('analytics');
if (preferences.categories.marketing) categories.push('marketing');
if (enabled && !categories.includes(category)) {
categories.push(category);
} else if (!enabled && categories.includes(category)) {
const index = categories.indexOf(category);
categories.splice(index, 1);
}
this.savePreferences(categories);
}
/**
* Check if specific category is enabled
*/
static isCategoryEnabled(category: string): boolean {
const preferences = this.getPreferences();
if (!preferences) return false;
return preferences.categories[category as keyof ConsentPreferences['categories']] || false;
}
/**
* Clear all preferences
*/
static clearPreferences(): void {
localStorage.removeItem(this.STORAGE_KEY);
window.dispatchEvent(new CustomEvent('cookieConsentChange', {
detail: null
}));
}
/**
* Export preferences for GDPR data access request
*/
static exportPreferences(): string {
const preferences = this.getPreferences();
return JSON.stringify(preferences, null, 2);
}
/**
* Get consent summary for display
*/
static getConsentSummary(): {
hasConsent: boolean;
acceptedCategories: string[];
timestamp: string | null;
} {
const preferences = this.getPreferences();
if (!preferences) {
return {
hasConsent: false,
acceptedCategories: [],
timestamp: null
};
}
const acceptedCategories = Object.entries(preferences.categories)
.filter(([_, enabled]) => enabled)
.map(([category]) => category);
return {
hasConsent: true,
acceptedCategories,
timestamp: preferences.timestamp
};
}
}
export default CookiePreferenceManager;
Consent Withdrawal and Deletion
GDPR Article 7(3) requires users to withdraw consent as easily as granting it. Implement one-click consent withdrawal via preference manager, clearing all non-essential cookies immediately. Display confirmation message: "Your cookie preferences have been updated. Analytics and marketing cookies have been deleted."
When users withdraw consent, delete corresponding cookies using document.cookie manipulation or cookie management libraries. Clear localStorage/sessionStorage data associated with non-consented categories. Reload the page if necessary to stop blocked scripts from executing.
Provide consent reset functionality to restore default state (all non-essential cookies disabled). This is useful for testing, troubleshooting, or users who want to start fresh.
Preference Storage and Synchronization
Store consent preferences in localStorage for client-side persistence across sessions. For authenticated ChatGPT apps, synchronize preferences to backend databases (Firestore, PostgreSQL) to maintain consistency across devices.
Implement server-side consent storage for compliance auditing. When users consent, send preferences to backend API:
// Consent Storage Service (TypeScript)
interface ConsentRecord {
userId: string;
sessionId: string;
categories: string[];
timestamp: string;
version: string;
ipAddress: string;
userAgent: string;
language: string;
}
class ConsentStorageService {
private static readonly API_ENDPOINT = '/api/consent';
/**
* Save consent record to server
*/
static async saveConsentRecord(
categories: string[],
userId?: string
): Promise<void> {
try {
const record: ConsentRecord = {
userId: userId || 'anonymous',
sessionId: this.getSessionId(),
categories,
timestamp: new Date().toISOString(),
version: '1.0.0',
ipAddress: await this.getClientIP(),
userAgent: navigator.userAgent,
language: navigator.language
};
const response = await fetch(this.API_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(record)
});
if (!response.ok) {
throw new Error(`Consent storage failed: ${response.statusText}`);
}
console.log('Consent record saved successfully');
} catch (error) {
console.error('Error saving consent record:', error);
// Fail gracefully - don't block user experience
}
}
/**
* Get or create session ID
*/
private static getSessionId(): string {
let sessionId = sessionStorage.getItem('session_id');
if (!sessionId) {
sessionId = this.generateUUID();
sessionStorage.setItem('session_id', sessionId);
}
return sessionId;
}
/**
* Get client IP address (via backend API)
*/
private static async getClientIP(): Promise<string> {
try {
const response = await fetch('/api/client-ip');
const data = await response.json();
return data.ip || 'unknown';
} catch (error) {
return 'unknown';
}
}
/**
* Generate UUID for session tracking
*/
private static generateUUID(): string {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
/**
* Retrieve consent history for user
*/
static async getConsentHistory(userId: string): Promise<ConsentRecord[]> {
try {
const response = await fetch(`${this.API_ENDPOINT}?userId=${userId}`);
if (!response.ok) {
throw new Error(`Failed to retrieve consent history: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error('Error retrieving consent history:', error);
return [];
}
}
}
export default ConsentStorageService;
Backend APIs should validate consent records, store them in audit-compliant databases with immutable timestamps, and provide GDPR data access endpoints for users to download their consent history.
Technical Cookie Blocking Implementation
GDPR requires blocking non-essential cookies until users consent. Implement technical controls to prevent analytics, marketing, and personalization scripts from executing before consent.
Script Blocking and Lazy Loading
Block third-party scripts (Google Analytics, Facebook Pixel, Hotjar) by modifying <script> tag type attribute to type="text/plain" and adding data-category attributes. When users consent to specific categories, dynamically change script types to text/javascript to execute blocked scripts.
// Cookie Blocker Service (TypeScript)
class CookieBlocker {
private static readonly BLOCKED_TYPE = 'text/plain';
private static readonly ACTIVE_TYPE = 'text/javascript';
/**
* Block all non-essential scripts on page load
*/
static blockScripts(): void {
const scripts = document.querySelectorAll('script[data-category]');
scripts.forEach((script) => {
const category = script.getAttribute('data-category');
if (category && category !== 'necessary') {
script.setAttribute('type', this.BLOCKED_TYPE);
}
});
}
/**
* Unblock scripts for consented categories
*/
static unblockScripts(consentedCategories: string[]): void {
const scripts = document.querySelectorAll('script[data-category]');
scripts.forEach((script) => {
const category = script.getAttribute('data-category');
const src = script.getAttribute('src');
if (!category) return;
if (consentedCategories.includes(category)) {
// For inline scripts
if (!src) {
this.executeInlineScript(script);
} else {
// For external scripts
this.loadExternalScript(script);
}
}
});
}
/**
* Execute inline blocked script
*/
private static executeInlineScript(script: Element): void {
const newScript = document.createElement('script');
newScript.setAttribute('type', this.ACTIVE_TYPE);
// Copy attributes
Array.from(script.attributes).forEach(attr => {
if (attr.name !== 'type') {
newScript.setAttribute(attr.name, attr.value);
}
});
// Copy script content
newScript.textContent = script.textContent;
// Replace old script with new script
script.parentNode?.replaceChild(newScript, script);
}
/**
* Load external blocked script
*/
private static loadExternalScript(script: Element): void {
const src = script.getAttribute('src');
if (!src) return;
const newScript = document.createElement('script');
newScript.setAttribute('type', this.ACTIVE_TYPE);
newScript.setAttribute('src', src);
// Copy other attributes
Array.from(script.attributes).forEach(attr => {
if (attr.name !== 'type' && attr.name !== 'src') {
newScript.setAttribute(attr.name, attr.value);
}
});
// Replace old script with new script
script.parentNode?.replaceChild(newScript, script);
}
/**
* Block iframes for non-consented categories
*/
static blockIframes(consentedCategories: string[]): void {
const iframes = document.querySelectorAll('iframe[data-category]');
iframes.forEach((iframe) => {
const category = iframe.getAttribute('data-category');
if (!category || !consentedCategories.includes(category)) {
// Replace iframe with placeholder
const placeholder = this.createIframePlaceholder(iframe);
iframe.parentNode?.replaceChild(placeholder, iframe);
}
});
}
/**
* Create placeholder for blocked iframe
*/
private static createIframePlaceholder(iframe: Element): HTMLElement {
const placeholder = document.createElement('div');
placeholder.className = 'blocked-iframe-placeholder';
placeholder.innerHTML = `
<div class="placeholder-content">
<p>This content requires your consent to display.</p>
<button class="btn-enable-content" data-category="${iframe.getAttribute('data-category')}">
Enable Content
</button>
</div>
`;
// Store original iframe for restoration
placeholder.setAttribute('data-original-src', iframe.getAttribute('src') || '');
placeholder.setAttribute('data-category', iframe.getAttribute('data-category') || '');
return placeholder;
}
/**
* Delete cookies for non-consented categories
*/
static deleteCookies(categoriesToDelete: string[]): void {
const cookieMap = this.getCookieCategoryMap();
const cookies = document.cookie.split(';');
cookies.forEach(cookie => {
const [name] = cookie.trim().split('=');
const category = cookieMap[name];
if (category && categoriesToDelete.includes(category)) {
this.deleteCookie(name);
}
});
}
/**
* Delete specific cookie
*/
private static deleteCookie(name: string): void {
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
// Also delete with domain variants
const hostname = window.location.hostname;
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${hostname}`;
document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=.${hostname}`;
}
/**
* Map cookie names to categories (customize per app)
*/
private static getCookieCategoryMap(): Record<string, string> {
return {
'_ga': 'analytics',
'_gid': 'analytics',
'_gat': 'analytics',
'fbp': 'marketing',
'_fbp': 'marketing',
'fr': 'marketing',
'tr': 'marketing',
'lang': 'functional',
'theme': 'functional',
'session': 'necessary',
'csrf': 'necessary'
};
}
/**
* Initialize blocker on page load
*/
static initialize(): void {
// Block scripts immediately
this.blockScripts();
// Listen for consent changes
window.addEventListener('cookieConsentChange', (event: any) => {
const preferences = event.detail;
if (preferences) {
const consentedCategories = Object.entries(preferences.categories)
.filter(([_, enabled]) => enabled)
.map(([category]) => category);
this.unblockScripts(consentedCategories);
} else {
// Consent withdrawn - block all non-essential
this.deleteCookies(['functional', 'analytics', 'marketing']);
location.reload(); // Reload to reset scripts
}
});
}
}
// Auto-initialize on DOM ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => CookieBlocker.initialize());
} else {
CookieBlocker.initialize();
}
export default CookieBlocker;
Use this pattern in your ChatGPT app's HTML:
<!-- Blocked Google Analytics (loads only with analytics consent) -->
<script
type="text/plain"
data-category="analytics"
src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"
></script>
<!-- Blocked Facebook Pixel (loads only with marketing consent) -->
<script type="text/plain" data-category="marketing">
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', 'YOUR_PIXEL_ID');
</script>
Google Consent Mode Integration
Google Consent Mode enables granular control over Google Analytics and Google Ads behavior based on user consent. Implement Consent Mode V2 (required for EEA users as of March 2024) to signal consent status to Google services.
// Google Consent Mode Integration (TypeScript)
declare global {
interface Window {
gtag: (...args: any[]) => void;
dataLayer: any[];
}
}
class GoogleConsentMode {
/**
* Initialize Consent Mode with default denied state
*/
static initialize(): void {
// Initialize dataLayer
window.dataLayer = window.dataLayer || [];
window.gtag = function() {
window.dataLayer.push(arguments);
};
// Set default consent state (all denied)
window.gtag('consent', 'default', {
'ad_storage': 'denied',
'ad_user_data': 'denied',
'ad_personalization': 'denied',
'analytics_storage': 'denied',
'functionality_storage': 'denied',
'personalization_storage': 'denied',
'security_storage': 'granted', // Always granted
'wait_for_update': 500 // Wait 500ms for consent banner
});
// Enable URL passthrough for link decoration
window.gtag('set', 'url_passthrough', true);
// Enable ads data redaction
window.gtag('set', 'ads_data_redaction', true);
}
/**
* Update consent state based on user preferences
*/
static updateConsent(preferences: {
necessary: boolean;
functional: boolean;
analytics: boolean;
marketing: boolean;
}): void {
window.gtag('consent', 'update', {
'ad_storage': preferences.marketing ? 'granted' : 'denied',
'ad_user_data': preferences.marketing ? 'granted' : 'denied',
'ad_personalization': preferences.marketing ? 'granted' : 'denied',
'analytics_storage': preferences.analytics ? 'granted' : 'denied',
'functionality_storage': preferences.functional ? 'granted' : 'denied',
'personalization_storage': preferences.functional ? 'granted' : 'denied',
'security_storage': 'granted'
});
console.log('Google Consent Mode updated:', preferences);
}
/**
* Get current consent state
*/
static getConsentState(): Record<string, string> {
// This is a simplified version - actual implementation would query gtag
return {
ad_storage: 'denied',
analytics_storage: 'denied',
functionality_storage: 'denied',
security_storage: 'granted'
};
}
}
// Initialize on page load
GoogleConsentMode.initialize();
export default GoogleConsentMode;
Consent Mode enables Google Analytics to function in "cookieless" mode when analytics consent is denied, using aggregated pings instead of individual user tracking. This balances privacy compliance with basic analytics visibility.
Analytics Integration Patterns
Integrate cookie consent with ChatGPT app analytics platforms (Google Analytics, Mixpanel, Amplitude). Only initialize analytics scripts after users consent to analytics category:
// Analytics Manager with Consent Integration
class AnalyticsManager {
private static isInitialized = false;
static initialize(consentedCategories: string[]): void {
if (this.isInitialized) return;
if (consentedCategories.includes('analytics')) {
this.initializeGoogleAnalytics();
this.initializeMixpanel();
this.isInitialized = true;
}
}
private static initializeGoogleAnalytics(): void {
window.gtag('js', new Date());
window.gtag('config', 'G-XXXXXXXXXX', {
anonymize_ip: true,
cookie_flags: 'SameSite=None;Secure'
});
}
private static initializeMixpanel(): void {
// Mixpanel initialization logic
}
static trackEvent(eventName: string, properties?: Record<string, any>): void {
if (!this.isInitialized) return;
window.gtag('event', eventName, properties);
}
}
Compliance Monitoring and Auditing
GDPR Article 30 requires records of processing activities, including cookie consent. Implement audit logging and compliance monitoring to demonstrate regulatory compliance.
Audit Logging System
Log all consent events (granted, updated, withdrawn) with timestamps, IP addresses, and user agents. Store logs in append-only databases or audit log services for tamper-proof records.
// Audit Logger (TypeScript)
interface AuditLogEntry {
eventId: string;
eventType: 'consent_granted' | 'consent_updated' | 'consent_withdrawn';
userId: string;
sessionId: string;
categories: string[];
timestamp: string;
ipAddress: string;
userAgent: string;
referrer: string;
}
class ConsentAuditLogger {
private static readonly API_ENDPOINT = '/api/audit-log';
/**
* Log consent event
*/
static async logEvent(
eventType: AuditLogEntry['eventType'],
categories: string[],
userId?: string
): Promise<void> {
try {
const entry: AuditLogEntry = {
eventId: this.generateEventId(),
eventType,
userId: userId || 'anonymous',
sessionId: this.getSessionId(),
categories,
timestamp: new Date().toISOString(),
ipAddress: await this.getClientIP(),
userAgent: navigator.userAgent,
referrer: document.referrer
};
await fetch(this.API_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(entry)
});
console.log('Audit log entry created:', entry.eventId);
} catch (error) {
console.error('Audit logging failed:', error);
}
}
private static generateEventId(): string {
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
private static getSessionId(): string {
return sessionStorage.getItem('session_id') || 'unknown';
}
private static async getClientIP(): Promise<string> {
try {
const response = await fetch('/api/client-ip');
const data = await response.json();
return data.ip || 'unknown';
} catch {
return 'unknown';
}
}
}
export default ConsentAuditLogger;
Compliance Reporting Dashboard
Build admin dashboards displaying consent metrics: total consents, category breakdowns, consent rates over time, and regional compliance status. Track key metrics:
- Consent rate: Percentage of users who accept cookies
- Category acceptance: Breakdown by cookie category (functional 85%, analytics 60%, marketing 40%)
- Opt-in vs opt-out: Ratio of Accept All vs Reject All vs Custom
- Regional compliance: EU users vs non-EU users
- Consent refresh: Users who update preferences after initial choice
// Compliance Reporter (TypeScript)
interface ComplianceMetrics {
totalConsents: number;
consentRate: number;
categoryBreakdown: {
functional: number;
analytics: number;
marketing: number;
};
optInRate: number;
optOutRate: number;
customRate: number;
}
class ComplianceReporter {
/**
* Generate compliance report
*/
static async generateReport(startDate: string, endDate: string): Promise<ComplianceMetrics> {
try {
const response = await fetch(`/api/compliance-report?start=${startDate}&end=${endDate}`);
return await response.json();
} catch (error) {
console.error('Failed to generate compliance report:', error);
throw error;
}
}
/**
* Export GDPR-compliant consent records
*/
static async exportConsentRecords(userId: string): Promise<Blob> {
const response = await fetch(`/api/consent-export?userId=${userId}`);
return await response.blob();
}
/**
* Validate compliance status
*/
static async validateCompliance(): Promise<{
isCompliant: boolean;
issues: string[];
}> {
const issues: string[] = [];
// Check consent banner implementation
if (!document.querySelector('[role="dialog"][aria-modal="true"]')) {
issues.push('Consent banner missing ARIA attributes');
}
// Check cookie policy link
if (!document.querySelector('a[href*="cookie-policy"]')) {
issues.push('Cookie policy link missing');
}
// Check granular controls
const categoryToggles = document.querySelectorAll('input[type="checkbox"][data-category]');
if (categoryToggles.length < 3) {
issues.push('Insufficient granular cookie controls');
}
return {
isCompliant: issues.length === 0,
issues
};
}
}
export default ComplianceReporter;
Vendor Management
Track third-party vendors (Google, Facebook, Hotjar) and their cookie usage. Maintain vendor lists in cookie policies, update when adding new services, and ensure vendor contracts include GDPR Data Processing Agreements (DPAs).
Implement vendor categorization:
// Vendor Manager (TypeScript)
interface CookieVendor {
id: string;
name: string;
category: string;
cookies: string[];
privacyPolicy: string;
dataProcessingAgreement: boolean;
}
class VendorManager {
private static vendors: CookieVendor[] = [
{
id: 'google-analytics',
name: 'Google Analytics',
category: 'analytics',
cookies: ['_ga', '_gid', '_gat'],
privacyPolicy: 'https://policies.google.com/privacy',
dataProcessingAgreement: true
},
{
id: 'facebook-pixel',
name: 'Facebook Pixel',
category: 'marketing',
cookies: ['_fbp', 'fr'],
privacyPolicy: 'https://www.facebook.com/privacy/explanation',
dataProcessingAgreement: true
}
];
static getVendorsByCategory(category: string): CookieVendor[] {
return this.vendors.filter(v => v.category === category);
}
static getAllVendors(): CookieVendor[] {
return this.vendors;
}
static addVendor(vendor: CookieVendor): void {
this.vendors.push(vendor);
}
}
export default VendorManager;
Update cookie policies when adding vendors, and notify users if material changes require consent refresh.
Conclusion: Building GDPR-Compliant ChatGPT Apps
Cookie consent management is non-negotiable for ChatGPT apps targeting European users or handling personal data. This guide provided production-ready implementations for consent banners, preference management, cookie blocking, Google Consent Mode, and compliance auditing—the complete technical foundation for GDPR compliance.
Key takeaways: implement granular consent controls (category-level toggles), proactive script blocking (prevent non-consented cookies), Google Consent Mode V2 (EEA requirement), audit logging (regulatory compliance), and accessible interfaces (WCAG AA standards). These patterns ensure legal compliance while maintaining user trust and conversion rates.
Start Building GDPR-Compliant ChatGPT Apps Today
Ready to implement cookie consent management for your ChatGPT app? MakeAIHQ provides built-in GDPR compliance tools, cookie consent templates, and automated script blocking—no legal expertise required. Our Instant App Wizard generates consent banners with category controls, Google Consent Mode integration, and audit logging in under 5 minutes.
Start your free trial and deploy a GDPR-compliant ChatGPT app to the App Store within 48 hours. No coding, no compliance headaches—just production-ready cookie management that protects user privacy and builds trust.
Related Resources
Internal Links
- ChatGPT App Privacy & Security Guide - Complete privacy implementation guide
- GDPR Data Protection ChatGPT Apps - Data protection implementation
- User Privacy Controls ChatGPT Apps - Privacy dashboard implementation
- Data Encryption ChatGPT Apps - Encryption best practices
- Privacy Policy Generator ChatGPT Apps - Automated policy generation
- Security Audit Checklist ChatGPT Apps - Comprehensive security audit
- AI Ethics Best Practices - Ethical AI development
External Resources
- GDPR Official Text (EUR-Lex) - Complete GDPR regulation
- ePrivacy Directive (2002/58/EC) - Cookie-specific regulations
- IAB Transparency & Consent Framework - Industry consent standards
Schema Markup (HowTo):
{
"@context": "https://schema.org",
"@type": "HowTo",
"name": "Cookie Consent Management for ChatGPT Apps: GDPR Compliance Guide",
"description": "Implement cookie consent for ChatGPT apps with GDPR compliance, consent modes, preference management, and tracking controls.",
"step": [
{
"@type": "HowToStep",
"name": "Understand GDPR Cookie Requirements",
"text": "Classify cookies into categories (necessary, functional, analytics, marketing) and implement opt-in consent for non-essential cookies."
},
{
"@type": "HowToStep",
"name": "Design Cookie Consent Banner",
"text": "Create accessible, mobile-optimized consent banner with granular category controls and privacy policy links."
},
{
"@type": "HowToStep",
"name": "Implement Preference Management",
"text": "Build preference center enabling users to control categories, withdraw consent, and update preferences post-initial choice."
},
{
"@type": "HowToStep",
"name": "Block Non-Consented Cookies",
"text": "Implement technical controls blocking analytics, marketing, and personalization scripts until users consent."
},
{
"@type": "HowToStep",
"name": "Integrate Google Consent Mode",
"text": "Enable Google Consent Mode V2 for granular control over Google Analytics and Ads based on user consent."
},
{
"@type": "HowToStep",
"name": "Monitor Compliance",
"text": "Implement audit logging, compliance reporting, and vendor management for GDPR regulatory compliance."
}
],
"totalTime": "PT4H",
"tool": [
"TypeScript",
"React",
"Google Consent Mode",
"Cookie Consent Banner",
"Preference Manager"
]
}