Data Governance
Data Retention Policy
How long we keep different types of information and what happens when it's no longer needed
• 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
| Data Type | Retention Period | Legal Basis | Action on Expiry |
|---|---|---|---|
Membership Admin Data Name, DOB, contact, address, emergency contact | 7 years after membership ends | Insurance claims, legal disputes, financial audits | Anonymize: 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 investigation | Anonymize: 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 requirement | Delete: 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 annually | Delete: 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 defense | Immutable: 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 complaints | Keep: All consent records permanently Immutable: Cannot be modified or deleted |
1Membership Data Anonymization
Trigger: 7 years after membership_status = 'cancelled' or 'expired'
Process:
- Replace full_name with "Former Member [ID]"
- Delete: email, phone, address fields, date_of_birth
- Keep: member_id (for referential integrity), membership dates, membership_type
- Set email to "deleted_[id]@anonymized.local" (prevents duplicate constraint issues)
- Delete linked User account
Frequency: Automated monthly job
2Booking & Attendance Anonymization
Trigger: 3 years after event date
Process:
- Replace member_email with "anonymous@deleted.local"
- Replace member_name with "Anonymous Attendee"
- Keep: member_id (for foreign key), event_id, status, check_in status, timestamps
- Delete: notes field content
- 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:
- Set member_id to NULL (breaks link to member record)
- Keep all demographic responses (gender, ethnicity, etc.) - now fully anonymized
- Record remains in database for aggregate reporting
- 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:
- Full deletion of entire support_needs record
- No anonymization - complete removal from database
- 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:
- Safeguarding Officer reviews all notes for members who left 2+ years ago
- 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?
- If all risks resolved: Archive note and anonymize member link
- 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:
- Export logs to compressed archive file
- Store in secure cold storage (separate from live database)
- Remove from active database to improve performance
- Maintain tamper-proof checksums to verify integrity
- Archive remains queryable if needed for investigations
⚠️ Never delete audit logs - legal liability if deletion discovered
Frequency: Automated quarterly job
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:
Schedule: 1st of month at 2am
Runs: anonymizeMemberships, anonymizeBookings, deleteSupportNeeds
Schedule: 1st of Jan/Apr/Jul/Oct at 3am
Runs: archiveAuditLogs
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)
These CANNOT be automated and require human judgment: