Voltar ao blog
Compliance

GDPR and Children’s Data: New Guidance for Online Services

Equipa GetCookiesOctober 19, 202515 min de leitura
Children\s DataGDPRParental ConsentCompliance

TLDR: TikTok paid €345M for how they handled children's accounts. GDPR age thresholds vary by country (13-16), parental consent is required below that, and dark patterns around kids get multiplied penalties.

Read full summary

Comprehensive guide to GDPR's Article 8 requirements for children's consent. Covers age thresholds by country, verification methods, parental consent mechanisms, and designing child-friendly privacy experiences. Includes TypeScript implementations for age gates, parental verification workflows, and child-safe consent management.

Summary by Claude AI

The €345 Million Lesson

TikTok's 2023 fine wasn't about collecting children's data. That's legal with proper consent. The fine was about how they collected it.

Public profiles by default—so a 13-year-old's videos were visible to everyone unless they navigated buried settings to change it. "Family Pairing" that sounded protective but actually made parental controls harder to enable. Dark patterns that nudged kids toward sharing more, not less.

The Irish DPC catalogued each manipulation. Each one violated Article 8's requirement that children "merit specific protection." Each one contributed to a penalty that exceeded TikTok's annual revenue in some markets.

Children's data under GDPR isn't just more protected—it's a liability multiplier. Every dark pattern in your adult consent flow becomes exponentially more dangerous when a child encounters it. Every "maximize engagement" feature becomes evidence of exploitation when applied to users who can't legally consent.

The Patchwork Problem

GDPR's Article 8 sets the default age of digital consent at 16. Below that, parents must consent. But Article 8 also lets EU member states lower the threshold—down to 13.

This created a patchwork: 13 in the UK, Belgium, and Portugal. 14 in Spain and Italy. 15 in France. 16 in Germany and Netherlands. If your service is available across Europe, you're managing multiple thresholds simultaneously.

The default threshold under GDPR is 16 years, but Member States can set their own threshold anywhere between 13 and 16 years. This creates a patchwork of requirements across Europe that organizations must navigate carefully:

Country Age of Digital Consent Legal Basis
Germany 16 Default GDPR
Netherlands 16 Default GDPR
Ireland 16 Default GDPR (Data Protection Act 2018)
France 15 Law No. 2018-493
UK 13 Data Protection Act 2018
Spain 14 LOPDGDD
Italy 14 Legislative Decree 101/2018
Denmark 13 Data Protection Act
Sweden 13 Data Protection Act
Poland 16 Default GDPR
Belgium 13 Law of 30 July 2018
Austria 14 Data Protection Act
Portugal 13 Law No. 58/2019
// Age threshold configuration by jurisdiction
interface JurisdictionAgeConfig {
  country: string;
  countryCode: string;
  digitalConsentAge: number;
  legalReference: string;
  additionalRequirements?: string[];
}

const europeanAgeThresholds: JurisdictionAgeConfig[] = [
  {
    country: 'Germany',
    countryCode: 'DE',
    digitalConsentAge: 16,
    legalReference: 'GDPR Article 8 (default)',
    additionalRequirements: [
      'Stricter interpretation of "information society services"',
      'Enhanced documentation requirements'
    ]
  },
  {
    country: 'France',
    countryCode: 'FR',
    digitalConsentAge: 15,
    legalReference: 'Law No. 2018-493',
    additionalRequirements: [
      'CNIL guidelines on child-friendly notices',
      'Specific language requirements'
    ]
  },
  {
    country: 'United Kingdom',
    countryCode: 'GB',
    digitalConsentAge: 13,
    legalReference: 'Data Protection Act 2018, Section 9',
    additionalRequirements: [
      'ICO Age Appropriate Design Code compliance',
      'DPIA required for services likely to be accessed by children'
    ]
  },
  {
    country: 'Spain',
    countryCode: 'ES',
    digitalConsentAge: 14,
    legalReference: 'LOPDGDD Article 7',
    additionalRequirements: [
      'AEPD specific guidance on minors'
    ]
  },
  {
    country: 'Italy',
    countryCode: 'IT',
    digitalConsentAge: 14,
    legalReference: 'Legislative Decree 101/2018',
    additionalRequirements: [
      'Garante guidelines on processing children\'s data'
    ]
  },
  {
    country: 'Ireland',
    countryCode: 'IE',
    digitalConsentAge: 16,
    legalReference: 'Data Protection Act 2018',
    additionalRequirements: [
      'DPC Fundamentals guidance applicable'
    ]
  }
];

class JurisdictionResolver {
  private thresholds: Map<string, JurisdictionAgeConfig>;

  constructor() {
    this.thresholds = new Map();
    europeanAgeThresholds.forEach(config => {
      this.thresholds.set(config.countryCode, config);
    });
  }

  getAgeThreshold(countryCode: string): number {
    const config = this.thresholds.get(countryCode.toUpperCase());
    return config?.digitalConsentAge || 16; // Default to GDPR default
  }

  requiresParentalConsent(countryCode: string, userAge: number): boolean {
    return userAge < this.getAgeThreshold(countryCode);
  }

  getJurisdictionRequirements(countryCode: string): string[] {
    const config = this.thresholds.get(countryCode.toUpperCase());
    return config?.additionalRequirements || [];
  }
}

Beyond consent requirements, GDPR mandates several additional protections for children:

Transparency in Child-Friendly Language: Privacy notices must be clear and understandable to the intended audience. If your service targets children, your privacy notice must be written in language they can understand.

Data Minimization: The principle of collecting only necessary data applies with heightened scrutiny when children are involved.

Marketing Restrictions: Profiling and automated decision-making directed at children face significant restrictions, with behavioral advertising to children increasingly viewed as inappropriate.

Right to Erasure: Children have an enhanced right to have their data erased, particularly data collected when they were minors.

Age Verification: The Technical Challenge

Implementing reliable age verification is one of the most challenging aspects of child protection compliance. You need to verify age accurately enough to meet legal requirements without creating excessive friction or collecting more data than necessary.

Age Verification Methods

Different verification methods offer varying levels of assurance and user friction:

interface AgeVerificationMethod {
  id: string;
  name: string;
  assuranceLevel: 'low' | 'medium' | 'high';
  userFriction: 'minimal' | 'moderate' | 'significant';
  dataCollected: string[];
  suitableFor: string[];
  limitations: string[];
  implementation: (userData: any) => Promise<AgeVerificationResult>;
}

class AgeVerificationService {
  private methods: Map<string, AgeVerificationMethod>;
  private riskAssessor: RiskAssessmentService;

  constructor() {
    this.methods = new Map([
      ['self_declaration', {
        id: 'self_declaration',
        name: 'Self-Declaration (Date of Birth)',
        assuranceLevel: 'low',
        userFriction: 'minimal',
        dataCollected: ['date_of_birth'],
        suitableFor: ['low_risk_services', 'initial_gate'],
        limitations: [
          'Easy to circumvent',
          'Relies on user honesty',
          'May not meet requirements for high-risk processing'
        ],
        implementation: async (userData) => {
          const dob = new Date(userData.dateOfBirth);
          const age = this.calculateAge(dob);
          return {
            verified: true,
            age,
            method: 'self_declaration',
            confidence: 0.3
          };
        }
      }],
      ['hard_age_gate', {
        id: 'hard_age_gate',
        name: 'Hard Age Gate (Year Selection)',
        assuranceLevel: 'low',
        userFriction: 'minimal',
        dataCollected: ['birth_year'],
        suitableFor: ['low_risk_services'],
        limitations: [
          'Trivially bypassed',
          'ICO recommends against for child-focused services'
        ],
        implementation: async (userData) => {
          const currentYear = new Date().getFullYear();
          const age = currentYear - userData.birthYear;
          return {
            verified: true,
            age,
            method: 'hard_age_gate',
            confidence: 0.2
          };
        }
      }],
      ['credit_card_verification', {
        id: 'credit_card_verification',
        name: 'Credit Card Verification',
        assuranceLevel: 'medium',
        userFriction: 'significant',
        dataCollected: ['payment_token'],
        suitableFor: ['paid_services', 'adult_content'],
        limitations: [
          'Assumes card holder is adult',
          'Children may use parent\'s card',
          'Privacy concerns about payment data'
        ],
        implementation: async (userData) => {
          const verification = await this.paymentGateway.verifyCard(
            userData.paymentToken,
            { amount: 0, type: 'verification_only' }
          );
          return {
            verified: verification.valid,
            assumedAdult: true,
            method: 'credit_card',
            confidence: 0.6
          };
        }
      }],
      ['id_document_verification', {
        id: 'id_document_verification',
        name: 'ID Document Verification',
        assuranceLevel: 'high',
        userFriction: 'significant',
        dataCollected: ['id_image', 'selfie', 'extracted_dob'],
        suitableFor: ['high_risk_services', 'regulated_content'],
        limitations: [
          'High friction reduces conversion',
          'Privacy concerns about ID storage',
          'Accessibility issues',
          'Cost of verification services'
        ],
        implementation: async (userData) => {
          const idVerification = await this.idVerificationService.verify({
            documentImage: userData.idImage,
            selfieImage: userData.selfie
          });
          return {
            verified: idVerification.valid,
            age: idVerification.extractedAge,
            method: 'id_document',
            confidence: 0.95
          };
        }
      }],
      ['digital_identity', {
        id: 'digital_identity',
        name: 'Digital Identity (eID, Bank ID)',
        assuranceLevel: 'high',
        userFriction: 'moderate',
        dataCollected: ['verified_age_attribute'],
        suitableFor: ['regulated_services', 'government_services'],
        limitations: [
          'Not universally available',
          'Integration complexity',
          'User needs existing digital identity'
        ],
        implementation: async (userData) => {
          const assertion = await this.digitalIdentityProvider.verify({
            token: userData.eidToken,
            requestedAttributes: ['age_over_18', 'age_over_16', 'age_over_13']
          });
          return {
            verified: assertion.valid,
            ageAttributes: assertion.attributes,
            method: 'digital_identity',
            confidence: 0.99
          };
        }
      }],
      ['ai_estimation', {
        id: 'ai_estimation',
        name: 'AI-Based Age Estimation',
        assuranceLevel: 'medium',
        userFriction: 'moderate',
        dataCollected: ['facial_image'],
        suitableFor: ['medium_risk_services'],
        limitations: [
          'Estimation accuracy varies',
          'Bias concerns across demographics',
          'Privacy concerns about biometric data',
          'May not meet legal requirements alone'
        ],
        implementation: async (userData) => {
          const estimation = await this.ageEstimationService.estimate(
            userData.facialImage
          );
          return {
            verified: estimation.confidence > 0.8,
            estimatedAge: estimation.age,
            ageRange: estimation.range,
            method: 'ai_estimation',
            confidence: estimation.confidence
          };
        }
      }]
    ]);
  }

  async selectVerificationMethod(
    context: VerificationContext
  ): Promise<AgeVerificationMethod> {
    const riskLevel = await this.riskAssessor.assessRisk(context);

    // Select method based on risk and service type
    if (riskLevel === 'high' || context.regulatedContent) {
      return this.methods.get('id_document_verification')!;
    }

    if (context.hasDigitalIdentitySupport) {
      return this.methods.get('digital_identity')!;
    }

    if (context.isPaidService) {
      return this.methods.get('credit_card_verification')!;
    }

    // Default to self-declaration with additional checks
    return this.methods.get('self_declaration')!;
  }

  private calculateAge(dateOfBirth: Date): number {
    const today = new Date();
    let age = today.getFullYear() - dateOfBirth.getFullYear();
    const monthDiff = today.getMonth() - dateOfBirth.getMonth();

    if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < dateOfBirth.getDate())) {
      age--;
    }

    return age;
  }
}

The UK's Age Appropriate Design Code

The UK's Children's Code (officially the Age Appropriate Design Code) provides detailed guidance that has influenced global thinking on child protection online. While technically a UK-specific requirement, its principles are increasingly relevant globally.

Key requirements include:

  1. Best Interests of the Child: The child's best interests must be a primary consideration in service design
  2. Age Appropriate Application: Risk assessment must consider children likely to access the service
  3. Transparency: Privacy information must be provided in a clear, age-appropriate manner
  4. Detrimental Use of Data: Data must not be used in ways detrimental to children's wellbeing
  5. Policies and Community Standards: Published terms must uphold these standards
  6. Default Settings: High privacy settings must be the default for children
  7. Data Minimization: Only collect data minimally necessary
  8. Data Sharing: Limit data sharing unless compelling reason exists
  9. Geolocation: Default off for geolocation for children
  10. Parental Controls: Age-appropriate controls for parents
  11. Profiling: Default off for profiling unless compelling reason
  12. Nudge Techniques: Don't use techniques that encourage excessive use
  13. Connected Toys: Ensure appropriate protections for connected devices
  14. Online Tools: Provide prominent, accessible tools for children to exercise rights
class ChildrensCodeComplianceChecker {
  async assessCompliance(
    service: ServiceConfiguration
  ): Promise<ChildrensCodeAssessment> {
    const assessments: PrincipleAssessment[] = [];

    // Principle 1: Best Interests
    assessments.push(await this.assessBestInterests(service));

    // Principle 2: Age Appropriate Application
    assessments.push(await this.assessAgeAppropriateness(service));

    // Principle 3: Transparency
    assessments.push(await this.assessTransparency(service));

    // Principle 4: Detrimental Use
    assessments.push(await this.assessDetrimentalUse(service));

    // Principle 5: Policies and Standards
    assessments.push(await this.assessPolicies(service));

    // Principle 6: Default Settings
    assessments.push(await this.assessDefaultSettings(service));

    // Principle 7: Data Minimization
    assessments.push(await this.assessDataMinimization(service));

    // Principle 8: Data Sharing
    assessments.push(await this.assessDataSharing(service));

    // Principle 9: Geolocation
    assessments.push(await this.assessGeolocation(service));

    // Principle 10: Parental Controls
    assessments.push(await this.assessParentalControls(service));

    // Principle 11: Profiling
    assessments.push(await this.assessProfiling(service));

    // Principle 12: Nudge Techniques
    assessments.push(await this.assessNudgeTechniques(service));

    // Principle 13: Connected Toys
    if (service.type === 'connected_device') {
      assessments.push(await this.assessConnectedToys(service));
    }

    // Principle 14: Online Tools
    assessments.push(await this.assessOnlineTools(service));

    return {
      overallCompliance: this.calculateOverallCompliance(assessments),
      principles: assessments,
      recommendations: this.generateRecommendations(assessments),
      riskLevel: this.assessRiskLevel(assessments)
    };
  }

  private async assessDefaultSettings(
    service: ServiceConfiguration
  ): Promise<PrincipleAssessment> {
    const issues: ComplianceIssue[] = [];

    // Check privacy settings defaults
    if (!service.defaultSettings.highPrivacy) {
      issues.push({
        severity: 'high',
        issue: 'Privacy settings not defaulted to high for children',
        requirement: 'Settings must be "high privacy" by default for children',
        recommendation: 'Set default privacy to maximum for users under 18'
      });
    }

    // Check location sharing default
    if (service.defaultSettings.locationSharing !== false) {
      issues.push({
        severity: 'high',
        issue: 'Location sharing not defaulted to off',
        requirement: 'Geolocation must be off by default for children',
        recommendation: 'Default location services to disabled'
      });
    }

    // Check profiling default
    if (service.defaultSettings.profilingEnabled !== false) {
      issues.push({
        severity: 'high',
        issue: 'Profiling not defaulted to off',
        requirement: 'Profiling must be off by default for children',
        recommendation: 'Disable all profiling by default for users under 18'
      });
    }

    // Check data sharing defaults
    if (service.defaultSettings.thirdPartySharing !== false) {
      issues.push({
        severity: 'medium',
        issue: 'Third-party data sharing not defaulted to off',
        requirement: 'Data sharing should be minimal by default',
        recommendation: 'Disable non-essential data sharing by default'
      });
    }

    return {
      principle: 'Default Settings',
      number: 6,
      compliant: issues.filter(i => i.severity === 'high').length === 0,
      issues,
      score: this.calculateScore(issues)
    };
  }

  private async assessProfiling(
    service: ServiceConfiguration
  ): Promise<PrincipleAssessment> {
    const issues: ComplianceIssue[] = [];

    // Check if profiling is used
    if (service.features.profiling) {
      // Profiling must be off by default
      if (service.defaultSettings.profilingEnabled !== false) {
        issues.push({
          severity: 'high',
          issue: 'Profiling enabled by default',
          requirement: 'Profiling must be off by default for children',
          recommendation: 'Disable profiling by default; require explicit opt-in'
        });
      }

      // Check for compelling reason
      if (!service.profilingJustification) {
        issues.push({
          severity: 'high',
          issue: 'No compelling reason documented for profiling',
          requirement: 'Profiling requires documented compelling reason',
          recommendation: 'Document and justify any profiling with child welfare focus'
        });
      }

      // Check profiling uses
      if (service.profilingPurposes?.includes('advertising')) {
        issues.push({
          severity: 'critical',
          issue: 'Behavioral advertising profiling of children',
          requirement: 'Profiling for behavioral advertising is problematic',
          recommendation: 'Remove behavioral advertising targeting for children'
        });
      }
    }

    return {
      principle: 'Profiling',
      number: 11,
      compliant: issues.filter(i =>
        i.severity === 'high' || i.severity === 'critical'
      ).length === 0,
      issues,
      score: this.calculateScore(issues)
    };
  }
}

Parental Consent Mechanisms

When processing requires parental consent, you need mechanisms to verify that consent actually comes from a parent or guardian, not the child pretending to be a parent.

Implementing Parental Verification

interface ParentalConsentWorkflow {
  initiate(childUserId: string, parentEmail: string): Promise<ConsentRequest>;
  verify(requestId: string, verificationData: any): Promise<VerificationResult>;
  recordConsent(requestId: string, consentData: ConsentRecord): Promise<void>;
  checkConsentStatus(childUserId: string): Promise<ConsentStatus>;
}

class ParentalConsentService implements ParentalConsentWorkflow {
  private emailService: EmailService;
  private verificationService: ParentVerificationService;
  private consentStorage: ConsentStorage;

  async initiate(
    childUserId: string,
    parentEmail: string
  ): Promise<ConsentRequest> {
    // Generate secure consent request
    const requestId = this.generateSecureRequestId();
    const verificationToken = this.generateVerificationToken();

    const request: ConsentRequest = {
      id: requestId,
      childUserId,
      parentEmail,
      verificationToken,
      status: 'pending',
      createdAt: new Date(),
      expiresAt: new Date(Date.now() + 48 * 60 * 60 * 1000), // 48 hours
      consentPurposes: await this.getRequiredConsentPurposes(childUserId)
    };

    // Store request
    await this.consentStorage.saveRequest(request);

    // Send verification email to parent
    await this.sendParentVerificationEmail(request);

    return request;
  }

  private async sendParentVerificationEmail(
    request: ConsentRequest
  ): Promise<void> {
    const verificationUrl = this.buildVerificationUrl(request);

    await this.emailService.send({
      to: request.parentEmail,
      template: 'parental_consent_request',
      data: {
        childFirstName: await this.getChildFirstName(request.childUserId),
        serviceName: this.config.serviceName,
        verificationUrl,
        consentPurposes: request.consentPurposes.map(p => ({
          name: p.name,
          description: p.description,
          dataCollected: p.dataCategories
        })),
        expiresAt: request.expiresAt,
        privacyPolicyUrl: this.config.privacyPolicyUrl,
        childRightsInfo: this.getChildRightsInfo()
      }
    });
  }

  async verify(
    requestId: string,
    verificationData: ParentVerificationData
  ): Promise<VerificationResult> {
    const request = await this.consentStorage.getRequest(requestId);

    if (!request) {
      throw new RequestNotFoundError(requestId);
    }

    if (request.status !== 'pending') {
      throw new RequestAlreadyProcessedError(requestId);
    }

    if (new Date() > request.expiresAt) {
      throw new RequestExpiredError(requestId);
    }

    // Verify token
    if (verificationData.token !== request.verificationToken) {
      throw new InvalidTokenError();
    }

    // Verify parent identity based on chosen method
    const parentVerification = await this.verifyParentIdentity(
      verificationData
    );

    if (!parentVerification.verified) {
      return {
        success: false,
        reason: parentVerification.failureReason,
        retryAllowed: parentVerification.retryAllowed
      };
    }

    // Update request status
    request.status = 'verified';
    request.parentVerificationMethod = verificationData.method;
    request.verifiedAt = new Date();
    await this.consentStorage.updateRequest(request);

    return {
      success: true,
      requestId,
      nextStep: 'consent_form'
    };
  }

  private async verifyParentIdentity(
    data: ParentVerificationData
  ): Promise<ParentVerificationResult> {
    switch (data.method) {
      case 'credit_card':
        return this.verifyCreditCard(data.paymentToken);

      case 'knowledge_based':
        return this.verifyKnowledgeBased(data.answers);

      case 'video_verification':
        return this.verifyVideo(data.videoSessionId);

      case 'signed_form':
        return this.verifySignedForm(data.formData);

      case 'government_id':
        return this.verifyGovernmentId(data.idData);

      default:
        throw new UnsupportedVerificationMethodError(data.method);
    }
  }

  private async verifyCreditCard(
    paymentToken: string
  ): Promise<ParentVerificationResult> {
    // COPPA-compliant method: charge small amount, refund
    // FTC guidance allows this as verification method
    const charge = await this.paymentGateway.charge({
      token: paymentToken,
      amount: 0.50, // Small verification charge
      description: 'Parent verification for child account'
    });

    if (charge.success) {
      // Immediately refund
      await this.paymentGateway.refund(charge.transactionId);

      return {
        verified: true,
        method: 'credit_card',
        confidence: 0.7
      };
    }

    return {
      verified: false,
      failureReason: 'Payment verification failed',
      retryAllowed: true
    };
  }

  async recordConsent(
    requestId: string,
    consentData: ParentalConsentRecord
  ): Promise<void> {
    const request = await this.consentStorage.getRequest(requestId);

    if (request.status !== 'verified') {
      throw new UnverifiedRequestError(requestId);
    }

    const consent: StoredParentalConsent = {
      id: this.generateConsentId(),
      requestId,
      childUserId: request.childUserId,
      parentEmail: request.parentEmail,
      parentVerificationMethod: request.parentVerificationMethod,
      consentedPurposes: consentData.purposes,
      consentTimestamp: new Date(),
      ipAddress: consentData.ipAddress,
      userAgent: consentData.userAgent,
      consentLanguage: consentData.language,
      withdrawalInfo: {
        url: this.buildWithdrawalUrl(request.childUserId),
        contactEmail: this.config.parentContactEmail
      }
    };

    await this.consentStorage.saveConsent(consent);

    // Update request status
    request.status = 'consented';
    request.consentedAt = new Date();
    await this.consentStorage.updateRequest(request);

    // Activate child account with consented features
    await this.activateChildAccount(request.childUserId, consentData.purposes);

    // Send confirmation to parent
    await this.sendConsentConfirmation(consent);
  }

  async handleConsentWithdrawal(
    childUserId: string,
    parentVerification: ParentVerificationData
  ): Promise<WithdrawalResult> {
    // Verify parent identity
    const verification = await this.verifyParentIdentity(parentVerification);

    if (!verification.verified) {
      throw new ParentVerificationFailedError();
    }

    // Get current consent
    const consent = await this.consentStorage.getActiveConsent(childUserId);

    if (!consent) {
      throw new NoActiveConsentError(childUserId);
    }

    // Record withdrawal
    consent.withdrawnAt = new Date();
    consent.withdrawalMethod = parentVerification.method;
    await this.consentStorage.updateConsent(consent);

    // Deactivate child account features
    await this.deactivateChildFeatures(childUserId);

    // Schedule data deletion
    const deletionDate = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
    await this.scheduleDeletion(childUserId, deletionDate);

    // Notify parent
    await this.sendWithdrawalConfirmation(consent.parentEmail, {
      childUserId,
      deletionDate
    });

    return {
      success: true,
      accountStatus: 'deactivated',
      dataDeletedBy: deletionDate
    };
  }
}

Child-Friendly Privacy Notices

GDPR requires that privacy information be provided in a "concise, transparent, intelligible and easily accessible form, using clear and plain language." When the audience includes children, this requirement becomes critical and specific.

Writing for Different Age Groups

interface ChildFriendlyNotice {
  ageRange: [number, number];
  readingLevel: string;
  visualElements: boolean;
  interactiveElements: boolean;
  content: NoticeContent;
}

class ChildFriendlyNoticeGenerator {
  private templates: Map<string, NoticeTemplate>;

  generateNotice(
    processingInfo: ProcessingInformation,
    targetAgeRange: [number, number]
  ): ChildFriendlyNotice {
    if (targetAgeRange[1] <= 8) {
      return this.generateEarlyChildhoodNotice(processingInfo);
    } else if (targetAgeRange[1] <= 12) {
      return this.generatePreTeenNotice(processingInfo);
    } else if (targetAgeRange[1] <= 16) {
      return this.generateTeenNotice(processingInfo);
    } else {
      return this.generateStandardNotice(processingInfo);
    }
  }

  private generateEarlyChildhoodNotice(
    info: ProcessingInformation
  ): ChildFriendlyNotice {
    // Ages 5-8: Simple language, heavy visual elements, parent involvement
    return {
      ageRange: [5, 8],
      readingLevel: 'early_elementary',
      visualElements: true,
      interactiveElements: true,
      content: {
        title: `${info.serviceName} and Your Information`,
        sections: [
          {
            id: 'what_we_collect',
            title: 'What We Learn About You',
            childContent: `
              <div class="notice-card">
                <img src="/icons/info-collected.svg" alt="" class="notice-icon">
                <p>When you play ${info.serviceName}, we remember some things about you:</p>
                <ul class="simple-list">
                  ${info.dataCategories.map(cat =>
                    `<li>${this.simplifyDataCategory(cat, 'early_childhood')}</li>`
                  ).join('')}
                </ul>
              </div>
            `,
            parentContent: `
              <details class="parent-details">
                <summary>Information for Parents</summary>
                <p>We collect the following categories of personal data:</p>
                <ul>
                  ${info.dataCategories.map(cat =>
                    `<li><strong>${cat.name}:</strong> ${cat.description}</li>`
                  ).join('')}
                </ul>
                <p>Legal basis: ${info.legalBasis}</p>
              </details>
            `
          },
          {
            id: 'why_we_collect',
            title: 'Why We Need This',
            childContent: `
              <div class="notice-card">
                <img src="/icons/why.svg" alt="" class="notice-icon">
                <p>We use this to:</p>
                <ul class="simple-list">
                  ${info.purposes.map(purpose =>
                    `<li>${this.simplifyPurpose(purpose, 'early_childhood')}</li>`
                  ).join('')}
                </ul>
              </div>
            `
          },
          {
            id: 'your_choices',
            title: 'Your Choices',
            childContent: `
              <div class="notice-card">
                <img src="/icons/choices.svg" alt="" class="notice-icon">
                <p>You and your grown-up can:</p>
                <ul class="simple-list">
                  <li>Ask to see what we know about you</li>
                  <li>Ask us to forget about you</li>
                  <li>Stop playing anytime</li>
                </ul>
                <p>Ask your grown-up to help!</p>
              </div>
            `
          }
        ],
        parentGuidance: `
          <div class="parent-guidance">
            <h3>Guide for Parents</h3>
            <p>This notice is designed to be read together with your child.
            The "Information for Parents" sections provide the full legal details.</p>
            <p>You can exercise data rights on behalf of your child by
            <a href="${info.parentPortalUrl}">visiting our Parent Portal</a>.</p>
          </div>
        `,
        readAloudVersion: this.generateReadAloudScript(info)
      }
    };
  }

  private generatePreTeenNotice(
    info: ProcessingInformation
  ): ChildFriendlyNotice {
    // Ages 9-12: More detail, some autonomy, still visual
    return {
      ageRange: [9, 12],
      readingLevel: 'upper_elementary',
      visualElements: true,
      interactiveElements: true,
      content: {
        title: `How ${info.serviceName} Uses Your Information`,
        sections: [
          {
            id: 'what_we_collect',
            title: 'Information We Collect',
            childContent: `
              <div class="notice-section">
                <h4>Things You Tell Us</h4>
                <p>When you sign up or use ${info.serviceName}, you might share:</p>
                <ul>
                  ${info.dataCategories
                    .filter(c => c.source === 'user_provided')
                    .map(cat => `<li>${this.simplifyDataCategory(cat, 'pre_teen')}</li>`)
                    .join('')}
                </ul>

                <h4>Things We Notice</h4>
                <p>While you use ${info.serviceName}, we also learn:</p>
                <ul>
                  ${info.dataCategories
                    .filter(c => c.source === 'observed')
                    .map(cat => `<li>${this.simplifyDataCategory(cat, 'pre_teen')}</li>`)
                    .join('')}
                </ul>
              </div>
            `
          },
          {
            id: 'why_matters',
            title: 'Why This Matters',
            childContent: `
              <div class="notice-section">
                <p>Your information is important! Here's what we do with it:</p>
                <div class="purpose-cards">
                  ${info.purposes.map(purpose => `
                    <div class="purpose-card">
                      <h5>${this.simplifyPurpose(purpose, 'pre_teen')}</h5>
                      <p class="purpose-example">${purpose.example}</p>
                    </div>
                  `).join('')}
                </div>
              </div>
            `
          },
          {
            id: 'your_rights',
            title: 'Your Rights',
            childContent: `
              <div class="notice-section">
                <p>You have important rights about your information:</p>
                <div class="rights-list">
                  <div class="right-item">
                    <span class="right-icon">👀</span>
                    <div class="right-text">
                      <strong>See It:</strong> Ask to see what information we have about you
                    </div>
                  </div>
                  <div class="right-item">
                    <span class="right-icon">✏️</span>
                    <div class="right-text">
                      <strong>Fix It:</strong> Tell us if something is wrong
                    </div>
                  </div>
                  <div class="right-item">
                    <span class="right-icon">🗑️</span>
                    <div class="right-text">
                      <strong>Delete It:</strong> Ask us to erase your information
                    </div>
                  </div>
                  <div class="right-item">
                    <span class="right-icon">📦</span>
                    <div class="right-text">
                      <strong>Take It:</strong> Get a copy of your information
                    </div>
                  </div>
                </div>
                <p>Talk to your parent or guardian to use these rights, or
                <a href="${info.rightsPanelUrl}">use our Rights Center</a>.</p>
              </div>
            `
          }
        ]
      }
    };
  }

  private generateTeenNotice(
    info: ProcessingInformation
  ): ChildFriendlyNotice {
    // Ages 13-16: More adult-like but still accessible
    return {
      ageRange: [13, 16],
      readingLevel: 'middle_school',
      visualElements: true,
      interactiveElements: true,
      content: {
        title: `${info.serviceName} Privacy Notice`,
        sections: [
          {
            id: 'overview',
            title: 'The Quick Version',
            childContent: `
              <div class="tldr-box">
                <h4>TL;DR</h4>
                <ul>
                  <li>We collect: ${info.dataCategories.map(c => c.shortName).join(', ')}</li>
                  <li>We use it for: ${info.purposes.map(p => p.shortName).join(', ')}</li>
                  <li>We share with: ${info.sharingPartners.length > 0 ?
                    info.sharingPartners.map(p => p.name).join(', ') :
                    'Nobody outside of us'}</li>
                  <li>You can: See, fix, delete, or download your data anytime</li>
                </ul>
                <p>Read on for the details, or
                <a href="${info.fullPolicyUrl}">see our full privacy policy</a>.</p>
              </div>
            `
          },
          {
            id: 'data_collection',
            title: 'What We Collect & Why',
            childContent: `
              <div class="collection-table-wrapper">
                <table class="collection-table">
                  <thead>
                    <tr>
                      <th>Data Type</th>
                      <th>Why We Need It</th>
                      <th>Can You Opt Out?</th>
                    </tr>
                  </thead>
                  <tbody>
                    ${info.dataCategories.map(cat => `
                      <tr>
                        <td>
                          <strong>${cat.name}</strong>
                          <p class="data-example">${cat.examples}</p>
                        </td>
                        <td>${cat.purposes.join(', ')}</td>
                        <td>${cat.optional ?
                          '<span class="yes">Yes</span>' :
                          '<span class="no">Required</span>'}</td>
                      </tr>
                    `).join('')}
                  </tbody>
                </table>
              </div>
            `
          },
          {
            id: 'your_control',
            title: 'You\'re In Control',
            childContent: `
              <div class="control-section">
                <p>Your data belongs to you. Here's what you can do:</p>

                <div class="control-actions">
                  <a href="${info.settingsUrl}" class="control-btn">
                    <span class="btn-icon">⚙️</span>
                    Privacy Settings
                  </a>
                  <a href="${info.downloadUrl}" class="control-btn">
                    <span class="btn-icon">📥</span>
                    Download My Data
                  </a>
                  <a href="${info.deleteUrl}" class="control-btn">
                    <span class="btn-icon">🗑️</span>
                    Delete My Account
                  </a>
                </div>

                <p class="note">Some actions might need a parent's help if you're under
                ${info.parentalConsentAge} in your country.</p>
              </div>
            `
          }
        ]
      }
    };
  }

  private simplifyDataCategory(
    category: DataCategory,
    ageGroup: string
  ): string {
    const simplifications: Record<string, Record<string, string>> = {
      'early_childhood': {
        'email': 'Your email address',
        'username': 'The name you pick for the game',
        'location': 'Where you are',
        'device_info': 'What kind of phone or tablet you use',
        'usage_data': 'What games you play and for how long',
        'ip_address': 'Your computer\'s special number'
      },
      'pre_teen': {
        'email': 'Your email address (or your parent\'s)',
        'username': 'Your username',
        'location': 'Your location (city or country)',
        'device_info': 'Device type and settings',
        'usage_data': 'How you use the app',
        'ip_address': 'Your internet address'
      }
    };

    return simplifications[ageGroup]?.[category.id] ||
           category.simplifiedDescription ||
           category.name;
  }

  private simplifyPurpose(purpose: Purpose, ageGroup: string): string {
    const simplifications: Record<string, Record<string, string>> = {
      'early_childhood': {
        'service_provision': 'Make the game work for you',
        'personalization': 'Remember what you like',
        'safety': 'Keep you safe',
        'analytics': 'Make the game better',
        'customer_support': 'Help you when something goes wrong'
      },
      'pre_teen': {
        'service_provision': 'Make the service work',
        'personalization': 'Customize your experience',
        'safety': 'Keep the community safe',
        'analytics': 'Improve our service',
        'customer_support': 'Help when you need it'
      }
    };

    return simplifications[ageGroup]?.[purpose.id] ||
           purpose.simplifiedDescription ||
           purpose.name;
  }
}

Age-Appropriate Default Settings

One of the most impactful requirements for child protection is appropriate default settings. Children should receive the highest privacy protections by default, not opt-in to them.

class ChildSafeDefaultsManager {
  private settingsRepository: SettingsRepository;
  private featureFlags: FeatureFlagService;

  async applyChildSafeDefaults(
    userId: string,
    userAge: number,
    jurisdiction: string
  ): Promise<AppliedDefaults> {
    const defaults = this.determineDefaults(userAge, jurisdiction);

    // Apply privacy settings
    await this.settingsRepository.update(userId, {
      privacy: {
        profileVisibility: defaults.profileVisibility,
        searchable: defaults.searchable,
        showActivityStatus: defaults.showActivityStatus,
        allowDirectMessages: defaults.allowDirectMessages
      },
      data: {
        locationTracking: defaults.locationTracking,
        analyticsEnabled: defaults.analyticsEnabled,
        personalizedAds: defaults.personalizedAds,
        profilingEnabled: defaults.profilingEnabled,
        thirdPartySharing: defaults.thirdPartySharing
      },
      communication: {
        marketingEmails: defaults.marketingEmails,
        pushNotifications: defaults.pushNotifications,
        inAppMessages: defaults.inAppMessages
      },
      social: {
        whoCanContact: defaults.whoCanContact,
        whoCanSeeContent: defaults.whoCanSeeContent,
        whoCanTagMe: defaults.whoCanTagMe
      },
      safety: {
        contentFiltering: defaults.contentFiltering,
        screenTimeReminders: defaults.screenTimeReminders,
        breakReminders: defaults.breakReminders
      }
    });

    // Disable restricted features
    for (const feature of defaults.disabledFeatures) {
      await this.featureFlags.disable(userId, feature);
    }

    return {
      userId,
      appliedDefaults: defaults,
      appliedAt: new Date()
    };
  }

  private determineDefaults(
    age: number,
    jurisdiction: string
  ): ChildSafeDefaults {
    // Most restrictive defaults for youngest users
    if (age < 13) {
      return {
        // Privacy - Maximum restriction
        profileVisibility: 'private',
        searchable: false,
        showActivityStatus: false,
        allowDirectMessages: 'approved_only',

        // Data - Minimal collection
        locationTracking: false,
        analyticsEnabled: 'essential_only',
        personalizedAds: false,
        profilingEnabled: false,
        thirdPartySharing: false,

        // Communication - Restricted
        marketingEmails: false,
        pushNotifications: 'essential_only',
        inAppMessages: 'essential_only',

        // Social - Restricted
        whoCanContact: 'approved_only',
        whoCanSeeContent: 'friends_only',
        whoCanTagMe: 'nobody',

        // Safety - Maximum
        contentFiltering: 'strict',
        screenTimeReminders: true,
        breakReminders: true,

        // Features - Restricted
        disabledFeatures: [
          'live_streaming',
          'public_posting',
          'direct_purchase',
          'external_links',
          'friend_suggestions',
          'location_sharing'
        ]
      };
    }

    // 13-15: Moderate restrictions
    if (age < 16) {
      return {
        profileVisibility: 'friends_only',
        searchable: false,
        showActivityStatus: false,
        allowDirectMessages: 'friends_only',

        locationTracking: false,
        analyticsEnabled: 'essential_only',
        personalizedAds: false,
        profilingEnabled: false,
        thirdPartySharing: false,

        marketingEmails: false,
        pushNotifications: 'friends_and_essential',
        inAppMessages: 'friends_and_essential',

        whoCanContact: 'friends_only',
        whoCanSeeContent: 'friends_only',
        whoCanTagMe: 'friends_only',

        contentFiltering: 'moderate',
        screenTimeReminders: true,
        breakReminders: true,

        disabledFeatures: [
          'live_streaming_public',
          'direct_purchase',
          'location_sharing_public'
        ]
      };
    }

    // 16-17: Lighter restrictions
    return {
      profileVisibility: 'friends_only',
      searchable: false,
      showActivityStatus: false,
      allowDirectMessages: 'everyone',

      locationTracking: false,
      analyticsEnabled: true,
      personalizedAds: false,
      profilingEnabled: false,
      thirdPartySharing: false,

      marketingEmails: false,
      pushNotifications: true,
      inAppMessages: true,

      whoCanContact: 'everyone',
      whoCanSeeContent: 'friends_only',
      whoCanTagMe: 'friends_only',

      contentFiltering: 'off',
      screenTimeReminders: false,
      breakReminders: false,

      disabledFeatures: []
    };
  }
}

FAQ

What age is a "child" under GDPR?

GDPR allows Member States to set the age of digital consent between 13 and 16 years. The default is 16. You need to know the specific threshold in each jurisdiction where you operate. Anyone below that threshold requires parental consent for consent-based processing.

Do the child-specific rules apply to all data processing?

Article 8's parental consent requirements specifically apply to "information society services offered directly to a child" where consent is the legal basis. If you're processing children's data under a different legal basis (like legitimate interests or contract), Article 8's consent requirements don't apply—but other child-protection obligations still do.

How do I know if my service is "offered directly to a child"?

Look at who your service is designed for and marketed to. Educational apps, games, social media, and entertainment services commonly used by children are typically considered offered directly to children, even if not exclusively. The ICO's guidance suggests considering if a service is "likely to be accessed by children."

What counts as "verifiable parental consent"?

GDPR requires "reasonable efforts" to verify that consent comes from a parent. Methods include credit card verification, signed consent forms, video verification, government ID verification, or knowledge-based verification. The appropriate method depends on the risk level of processing.

Can children exercise their own data rights?

Yes, children have the same data rights as adults. However, for younger children, parents can exercise rights on their behalf. As children mature, they should be able to exercise more rights independently. The UK's Children's Code specifically requires tools that allow children to exercise their rights.

What happens when a child turns the age of digital consent?

Existing parental consent doesn't automatically convert to the child's own consent. Best practice is to re-obtain consent from the now-adult user, giving them the opportunity to make their own informed choice.

Do I need to delete data when children turn 18?

GDPR gives individuals an enhanced right to erasure for data collected when they were children. While you don't automatically need to delete data at 18, you should make it easy for now-adults to request deletion and should carefully consider retention of childhood data.

Building Child-Safe Services

Protecting children online isn't just about compliance—it's about building services that genuinely respect children's wellbeing and development. The legal requirements provide a floor, not a ceiling. Organizations that excel in this space go beyond minimum compliance to create genuinely child-appropriate experiences.

The most effective approach treats child safety as a design principle, not an afterthought. Consider children throughout product development: in user research, feature design, default settings, and ongoing monitoring. Involve child development experts, not just lawyers and engineers.

Remember that children are not a homogeneous group. A 7-year-old and a 15-year-old have vastly different capabilities, needs, and risks. Services that succeed with children recognize this diversity and adapt accordingly.

The regulatory environment for children's data protection is only becoming stricter. The EU's Digital Services Act adds new requirements. The UK's Children's Code sets detailed expectations. The US is considering comprehensive federal children's privacy legislation. Organizations that build child-safe practices now will be better positioned as these requirements evolve.

Most importantly, remember that behind every data point is a real child. The choices you make about data collection, retention, and use have real impacts on real young people. Build services you'd be comfortable having your own children use.

G

Equipa GetCookies

Escrito e mantido pela equipa GetCookies da Getia AS em Oslo, Noruega. Este artigo é informação geral sobre consentimento de cookies, não aconselhamento jurídico.

Analise o seu próprio site grátis

O plano gratuito mostra o banner num site com três análises de cookies por mês. Sem cartão, sem chamadas comerciais.