Data Governance

Data Retention Policy

How long we keep different types of information and what happens when it's no longer needed

Policy Principles

• We only keep personal data as long as necessary for the purposes we collected it

• Legal requirements (tax, insurance, safeguarding) take precedence over deletion requests

• Data is either securely deleted or anonymized when retention periods expire

• Regular automated reviews identify data due for deletion/anonymization

• All deletion actions are logged in the audit trail

Retention Periods by Data Type
Data TypeRetention PeriodLegal BasisAction on Expiry
Membership Admin Data
Name, DOB, contact, address, emergency contact
7 years after membership ends
Insurance claims, legal disputes, financial auditsAnonymize: Replace name/contact with "Member [ID]"
Delete: Address, DOB
Bookings & Attendance
Event bookings, check-ins, attendance history
3 years after event date
Statistical reporting, insurance, incident investigationAnonymize: Replace member details with "Anonymous"
Keep: Aggregate stats (count, event type)
Payments & Invoices
Financial transactions, receipts, refunds
7 years from transaction date
HMRC requirement (mandatory)
UK tax law compliance
Cannot delete: Legal obligation
After 7 years: Anonymize member name, keep amounts/dates
Equality Monitoring
Gender, ethnicity, disability, orientation, religion
Delete link immediately on member exit
Voluntary monitoring; no legal retention requirementDelete: member_id link (unlink from member record)
Keep: Anonymized responses for aggregate reporting
Support Needs
Reasonable adjustments, communication preferences
6 months after membership ends
Grace period for potential re-joining; reviewed annuallyDelete: All text fields
Keep: Category tags for aggregate statistics (if anonymized)
Safeguarding Notes
Risk assessments, incident records, concerns
Indefinite (case-by-case review)
Legal safeguarding duty
Protection of vulnerable adults/children
Review: Safeguarding Officer determines retention
Minimum: Until individual is no longer at risk
Cannot: Delete on member request
Audit Logs
System access, data changes, security events
7 years minimum
Evidence for investigations, compliance audits, legal defenseImmutable: Cannot be modified or deleted
Archive: Move to cold storage after 2 years
Consent Records
Privacy policy, terms, marketing consents
Indefinite (proof of consent)
Legal proof that consent was obtained; defense against complaintsKeep: All consent records permanently
Immutable: Cannot be modified or deleted
Deletion & Anonymization Procedures

1Membership Data Anonymization

Trigger: 7 years after membership_status = 'cancelled' or 'expired'

Process:

  1. Replace full_name with "Former Member [ID]"
  2. Delete: email, phone, address fields, date_of_birth
  3. Keep: member_id (for referential integrity), membership dates, membership_type
  4. Set email to "deleted_[id]@anonymized.local" (prevents duplicate constraint issues)
  5. Delete linked User account

Frequency: Automated monthly job

2Booking & Attendance Anonymization

Trigger: 3 years after event date

Process:

  1. Replace member_email with "anonymous@deleted.local"
  2. Replace member_name with "Anonymous Attendee"
  3. Keep: member_id (for foreign key), event_id, status, check_in status, timestamps
  4. Delete: notes field content
  5. Aggregate stats (event attendance counts) remain accurate

Why keep member_id: Allows analysis like "repeat attendance patterns" without revealing identity

Frequency: Automated monthly job

3Equality Monitoring De-identification

Trigger: Immediately when member leaves (membership_status = 'cancelled')

Process:

  1. Set member_id to NULL (breaks link to member record)
  2. Keep all demographic responses (gender, ethnicity, etc.) - now fully anonymized
  3. Record remains in database for aggregate reporting
  4. Impossible to reverse-engineer which member provided which responses

Result: Aggregate statistics preserved ("40% identify as neurodivergent") without identifying individuals

Frequency: Automated (triggered by membership cancellation)

4Support Needs Deletion

Trigger: 6 months after membership ends

Process:

  1. Full deletion of entire support_needs record
  2. No anonymization - complete removal from database
  3. Rationale: No legitimate need to retain after grace period

Exception: If linked to active safeguarding_notes, defer deletion until safeguarding review

Frequency: Automated monthly job

5Safeguarding Notes Retention Review

Trigger: Annual review by Safeguarding Officer

Process:

  1. Safeguarding Officer reviews all notes for members who left 2+ years ago
  2. Decision criteria:
    • Is there ongoing risk to this individual or others?
    • Could this information be needed for future safeguarding?
    • Are legal proceedings possible or ongoing?
  3. If all risks resolved: Archive note and anonymize member link
  4. If risks remain: Retain with documented justification

Frequency: Annual manual review (cannot be automated due to sensitivity)

6Audit Log Archival

Trigger: Audit logs older than 2 years

Process:

  1. Export logs to compressed archive file
  2. Store in secure cold storage (separate from live database)
  3. Remove from active database to improve performance
  4. Maintain tamper-proof checksums to verify integrity
  5. Archive remains queryable if needed for investigations

⚠️ Never delete audit logs - legal liability if deletion discovered

Frequency: Automated quarterly job

Implementation Steps for Base44

1. Create Scheduled Automation Functions

Create backend functions for each retention task:

• functions/anonymizeMemberships.js - Run monthly

• functions/anonymizeBookings.js - Run monthly

• functions/delinkEqualityData.js - Triggered on membership cancellation

• functions/deleteSupportNeeds.js - Run monthly

• functions/archiveAuditLogs.js - Run quarterly

2. Function Example: Anonymize Memberships

import { createClientFromRequest } from 'npm:@base44/sdk@0.8.6';

Deno.serve(async (req) => {
  try {
    const base44 = createClientFromRequest(req);
    
    // Admin-only function
    const user = await base44.auth.me();
    if (user?.role !== 'admin') {
      return Response.json({ error: 'Admin access required' }, { status: 403 });
    }
    
    // Find memberships that expired 7+ years ago
    const sevenYearsAgo = new Date();
    sevenYearsAgo.setFullYear(sevenYearsAgo.getFullYear() - 7);
    
    const expiredMembers = await base44.asServiceRole.entities.Member.filter({
      membership_status: ['cancelled', 'expired'],
      // Add custom query for updated_date < sevenYearsAgo
    });
    
    let anonymizedCount = 0;
    
    for (const member of expiredMembers) {
      // Check if already anonymized
      if (member.full_name?.startsWith('Former Member')) continue;
      
      // Anonymize
      await base44.asServiceRole.entities.Member.update(member.id, {
        full_name: `Former Member ${member.id}`,
        email: `deleted_${member.id}@anonymized.local`,
        phone: null,
        address_line1: null,
        address_line2: null,
        address_city: null,
        address_postcode: null,
        date_of_birth: null,
        medical_conditions: null,
        notes: 'Record anonymized per data retention policy'
      });
      
      // Log the action
      await base44.asServiceRole.entities.AuditLog.create({
        user_email: 'system@automated',
        user_role: 'system',
        action_type: 'delete',
        entity_type: 'Member',
        entity_id: member.id,
        description: 'Automated anonymization: 7 years after membership expiry'
      });
      
      anonymizedCount++;
    }
    
    return Response.json({
      success: true,
      anonymized: anonymizedCount,
      message: `Anonymized ${anonymizedCount} member records`
    });
    
  } catch (error) {
    return Response.json({ error: error.message }, { status: 500 });
  }
});

3. Set Up Scheduled Automations

In Base44 Dashboard → Automations, create:

Monthly Data Retention
Schedule: 1st of month at 2am
Runs: anonymizeMemberships, anonymizeBookings, deleteSupportNeeds
Quarterly Audit Archival
Schedule: 1st of Jan/Apr/Jul/Oct at 3am
Runs: archiveAuditLogs
Entity Automation: Membership Cancellation
Trigger: Member entity updated with status='cancelled'
Runs: delinkEqualityData (immediate)

4. Add Retention Metadata to Entities

Add tracking fields to monitor retention status:

// Add to Member entity
"anonymized": {
  "type": "boolean",
  "default": false,
  "description": "Whether this record has been anonymized"
},
"anonymized_date": {
  "type": "string",
  "format": "date-time",
  "description": "When anonymization occurred"
}

5. Create Admin Dashboard for Retention Monitoring

Build a page showing:

  • Records due for anonymization/deletion (next 30 days)
  • Last run dates for each retention job
  • Count of anonymized records by type
  • Manual trigger buttons for testing (admin-only)
Manual Review Requirements

These CANNOT be automated and require human judgment:

Safeguarding Notes: Annual review by Safeguarding Officer to determine continued retention need
Legal Holds: If member is involved in legal proceedings, all retention schedules paused
Payment Disputes: Active disputes extend payment data retention beyond 7 years
Data Subject Requests: Member-requested early deletion requires admin approval