Data Retention Schedule
Last reviewed 29 July 2026 · Next scheduled review 12 May 2027
Controller of record: Mentum. Contact: privacy@mentumjobs.com (data protection); dpo@mentumjobs.com (DPO routing); security@mentumjobs.com (security reports); support@mentumjobs.com (operational).
Anchors: Privacy Policy; Terms of Service; Data Processing Addendum; Sub-processor list; Acceptable Use Policy; Service Level Agreement; Cookie Policy.
This Data Retention Schedule is Mentum's published statement of how long each category of personal data is kept, what event starts the retention clock, and how the data is destroyed at the end of its period. It is the operational counterpart to the Privacy Policy: the Privacy Policy explains what Mentum processes and why; this Schedule sets the how long and how it ends.
1. Purpose and scope
1.1 Purpose
This Schedule discharges Mentum's obligation under UK GDPR Article 13(2)(a) to inform data subjects of “the period for which the personal data will be stored, or if that is not possible, the criteria used to determine that period”. It also operationalises Article 5(1)(e) (storage limitation) by binding every personal-data category to a defined retention period, deletion trigger, and destruction mechanism.
1.2 What this document is not
- It is not the Privacy Policy. The Privacy Policy is the canonical disclosure of lawful bases, data categories, recipients, transfers, and data-subject rights. This Schedule presupposes that policy and goes deeper on retention only.
- It is not the Data Processing Addendum. The DPA governs the contractual processor terms between Mentum and its paying customers; this Schedule describes Mentum's controller-side practice.
- It is not Mentum's internal Records of Processing Activities (ROPA). ROPA is the Article 30 record and goes wider (lawful bases, recipients, transfers) and shallower (no per-table cron timing). Where this Schedule and ROPA disagree, this Schedule is authoritative for retention timing because it is generated from the live code.
1.3 How to read this alongside the Privacy Policy
- The Privacy Policy gives the summary retention statements that a candidate or employer can read in two minutes.
- This Schedule gives the per-table retention statements that a DPO or auditor can verify against the code and migrations.
2. Lawful basis recap
Mentum's processing rests on the following Article 6 bases (see Privacy Policy § 3 for the full mapping):
- Article 6(1)(b) — Contract. Account creation, matching, chat, interview booking, employer search of candidate profiles, advisor conversations for candidates who have opted in. This is the lawful basis for the majority of retention rows below.
- Article 6(1)(a) — Consent. Marketing communications, AI-feature opt-in, and cookie categories beyond strictly necessary. Consent records themselves carry a statutory retention period (see § 9).
- Article 6(1)(f) — Legitimate interests. Security telemetry, application audit logs, abuse prevention. Balancing-test outcomes are recorded in Mentum's internal ROPA.
- Article 6(1)(c) — Legal obligation. Consent retention under UK GDPR Article 7(1) and Article 17(3)(e); breach-register retention under Article 33; PECR record-keeping. See § 9.
3. Two retention concepts you'll see in this document
This Schedule uses two distinct end-of-life mechanisms, and the distinction matters.
3.1 Deletion
The row is physically removed from the database. After the daily retention sweep, a hard DELETE (or a chain of foreign-key-cascading deletes) takes the data out of the live system. The only residual copy is in Supabase's encrypted, time-limited backup window — see § 10. Most rows on this Schedule are deleted at end-of-life.
3.2 Pseudonymisation
The row is preserved, but the identifying columns are nulled or replaced with the literal token [Removed]. The remaining row is structurally meaningful (foreign-key relationships, employer-side recordkeeping, statistical integrity) but is no longer attributable to the original data subject. Two specific cases use pseudonymisation rather than deletion:
- The 36-month inactivity sweep on candidate profiles. A candidate who has not signed in for 36 months has their personal identifiers blanked from
candidate_profilewhile structural columns are retained for aggregate analytics. - Chat messages after a match closes. Closed-match chat content is replaced with a retention notice; the surrounding metadata (sender, timestamp, match reference) is preserved for employer-side recordkeeping.
In both cases the data is no longer personal data within the meaning of UK GDPR after pseudonymisation, but the database row remains. This Schedule labels every such case “Pseudonymisation” in the Destruction mechanism column so the difference is unambiguous.
4. Retention schedule — candidate data
The following table is the authoritative per-category retention schedule for personal data Mentum processes about candidates. Underlying table names are shown in code font after the plain-English category name for auditor cross-reference.
| Data category | Retention period | Deletion trigger | Destruction mechanism |
|---|---|---|---|
Active candidate account (candidate_profile) | While account active. 24 months of sign-in inactivity → deactivation; 36 months → pseudonymisation (identifiers nulled to [Removed]) | Last sign-in timestamp older than threshold | pg_cron @ 04:15 UTC via run_retention_cleanup() |
| CV file storage | Until account deletion or the 36-month pseudonymisation sweep | User-triggered erasure or 36-month inactivity sweep | Synchronous on DELETE /api/auth/account. On the 36-month path the 04:15 UTC retention-cleanup-daily job nulls cv_url and enqueues the storage object paths into pending_storage_purge; the 05:00 UTC retention-storage-purge-daily job then deletes the actual storage bytes (a two-step flow, not a standalone Python sweeper) |
| Profile photo | Until account deletion or explicit user delete | User-triggered erasure or user clears photo in profile settings | Synchronous on DELETE /api/auth/account; storage delete on explicit user action |
Chat message content (chat_message.content) | 12 months after the parent match enters a closed status; 36 months absolute outer bound regardless of status | Closed match status or absolute age over 36 months | Pseudonymisation (row preserved, content replaced with a retention notice; sender, timestamp, match reference preserved) — pg_cron @ 04:15 UTC |
| Chat attachments | Aligned with message content — purged when the closed-match window elapses | Closed match + 12-month window | Two steps, like CV files: the retention sweep pg_cron @ 04:15 UTC clears the attachment metadata and queues the storage object; the bytes are deleted from the bucket by the storage-purge job at 05:00 UTC the same morning. |
AI match scores, summaries, feedback (match_ai_score, match_ai_summary, match_ai_positive_feedback, match_ai_negative_feedback, match_stats) | Lifetime of the underlying job posting | Parent job deleted at 6 months inactive with no active matches or user erasure | pg_cron @ 04:15 UTC FK-cascading delete or synchronous on DELETE /api/auth/account |
Enhanced career insights (match_enhanced_insights) | Lifetime of the parent match | Same as match scores above | Same as match scores above |
Advisor conversations (advisor_conversation) | 24 months after the conversation is marked inactive (fixed in the scheduled job run_retention_cleanup()) | is_active = false and age over threshold or user erasure | pg_cron @ 04:15 UTC via run_retention_cleanup() |
Career-trajectory cache (career_trajectory_cache) | 7 days (regenerated on demand) | Cache age over threshold | Refreshed on next request; deleted synchronously on account erasure |
| Notifications — read | 6 months | is_read = true and age over 6 months | pg_cron @ 04:15 UTC |
| Notifications — unread | 12 months | is_read = false and age over 12 months | pg_cron @ 04:15 UTC |
| Notification preferences | Until account deletion | User-triggered erasure | Synchronous on DELETE /api/auth/account |
| Push subscriptions | Until user opt-out or account deletion | User opt-out via app settings or user erasure | Synchronous on user action or DELETE /api/auth/account |
Behaviour events (candidate_behaviour_event) | 6 months (fixed in the scheduled job run_retention_cleanup()) | Age over threshold | pg_cron @ 04:15 UTC |
Eligibility declarations (candidate_requirement_status) — your self-declared answers to a role's licence, clearance, professional-registration, qualification and shift-availability requirements | Until account deletion. Any single answer can be cleared back to unanswered at any time, which deletes that row immediately. Answers are included in the Art. 20 data export. | User-triggered erasure or the candidate clearing an individual answer | Synchronous on DELETE /api/auth/account via the delete_candidate_account RPC |
| Saved matches, bookmarks, blocked companies, question answers, skills, experience, education, interests, accessibility preferences, availability (all candidate-owned rows) | Until account deletion | User-triggered erasure | Synchronous on DELETE /api/auth/account via the delete_candidate_account RPC |
Match details rows (match_details) | Lifetime of the parent job; anonymised rather than deleted on candidate erasure — the candidate identifier is set to NULL while the row is preserved for the employer's recordkeeping (employment-law defensibility) | Parent job purged at 6 months inactive + no active matches or user erasure | pg_cron @ 04:15 UTC FK cascade (job-deletion path) or synchronous FK NULL on erasure |
Headline read
- No “set-and-forget” candidate data exists. Either the account is active (and rows persist), or the user has chosen to delete (and rows are removed synchronously), or the account has gone cold for 24/36 months (and rows are pseudonymised on the inactivity sweep).
- The two pseudonymisation points are deliberate. The 36-month sweep is the inactivity floor; closed-match chat is the per-match floor. Both are explicit choices rather than incomplete deletion logic.
- The
match_detailsrow is the only “preserved-but-anonymised” employer-facing artefact — preserved because deleting it would corrupt the employer's hiring record. The candidate identifier is removed; the row remains as a hiring-decision record under employment law.
5. Retention schedule — employer and job data
| Data category | Retention period | Deletion trigger | Destruction mechanism |
|---|---|---|---|
Active employer account (company_profile) | While account active. No inactivity timer — deleted only on user-triggered erasure | User-triggered erasure | Synchronous on DELETE /api/auth/account via the delete_employer_account RPC |
| Employer-posted job — active state | While is_active = true. Auto-deactivated at 90 days from posted_on if there are no active matches | 90-day age + no active matches | pg_cron @ 04:15 UTC flips is_active = false. Pre-launch carve-out: the frozen pre-launch Adzuna job corpus is intentionally retained for matching development and is not subject to the 90-day deactivation until launch. |
| Employer-posted job — inactive state | 6 months after the last update to the row, provided no active matches remain | is_active = false for over 6 months and no active matches | pg_cron @ 04:15 UTC hard-delete with full match-tree FK cascade |
| Adzuna inbound job description text | 30 days after posted_on once the parent job is inactive | Inactive Adzuna job + 30-day age threshold | pg_cron @ 04:15 UTC updates description text to NULL; the parent row is preserved |
| Job ingestion queue — processed | 7 days | Processed status + age over 7 days | pg_cron @ 04:15 UTC |
| Job ingestion queue — failed | 30 days | Failed status + age over 30 days | pg_cron @ 04:15 UTC |
Skill processing log (skillprocessinglog) | 30 days | Age over 30 days | pg_cron @ 04:15 UTC |
Market snapshot (market_snapshot) | 12 months | Age over threshold | pg_cron @ 04:15 UTC |
Skill metrics history (skill_metrics_history) | 6 months | Age over threshold | pg_cron @ 04:15 UTC |
Skill trend (skill_trend) | 6 months | Age over threshold | pg_cron @ 04:15 UTC |
Employer analytics snapshot (employer_analytics_snapshot) | 24 months | Age over threshold | pg_cron @ 04:15 UTC |
Salary benchmark cache (salary_benchmark_cache) | 12 months | Age over threshold | pg_cron @ 04:15 UTC |
Company intelligence cache (company_intelligence_cache) | 12 months | Age over threshold | pg_cron @ 04:15 UTC |
Company intelligence history (company_intelligence_history) | 52 weeks | Age over threshold | Weekly snapshot job @ 04:30 UTC Sundays |
Salary benchmark history (salary_benchmark_history) | 52 weeks | Age over threshold | Weekly snapshot job @ 04:30 UTC Sundays |
Regional market snapshot (regional_market_snapshot) | 180 days | Age over threshold | Daily capture job @ 03:15 UTC |
Headline read
- Employer accounts have no inactivity timer. Unlike candidate accounts, an employer account remains live until the user actively deletes it. This is deliberate: long-running employer relationships are a normal commercial pattern.
- Jobs go through three states with three clocks. Active (90 days posted with no matches), then inactive (6 months for hard delete), with Adzuna-sourced descriptions further accelerated to a 30-day text NULLification once the parent goes inactive.
- Analytics tables intentionally outlive jobs. 6-to-24-month windows for market and skill analytics support employer dashboards that compare period-over-period; they hold aggregated analytics, not candidate-attributable data.
6. Retention schedule — operational and audit data
These tables matter less to a typical end user but are the artefacts auditors and DPOs ask about during procurement.
| Data category | Retention period | Deletion trigger | Destruction mechanism |
|---|---|---|---|
Data access log (data_access_log) — every employer→candidate PII access | 24 months (fixed in the scheduled job run_retention_cleanup()) | Age over threshold | pg_cron @ 04:15 UTC via run_retention_cleanup() |
Security events (security_events) | 12 months (fixed in the scheduled job run_retention_cleanup()) | Age over threshold | pg_cron @ 04:15 UTC |
Application audit (audit_errors, audit_runs) | 12 months (fixed in the scheduled job run_retention_cleanup()) | Age over threshold | pg_cron @ 04:15 UTC |
Pipeline run log (pipeline_run_log) | 6 months | Age over threshold | pg_cron @ 04:15 UTC |
LLM usage log (gemini_usage_log) — cost and token attribution | 90 days (the permanent record is a per-day aggregate with no per-user attribution) | Age over threshold | pg_cron @ 04:15 UTC |
Email send log (email_send_log) | 12 months | Age over threshold | Separate pg_cron @ 04:20 UTC (email-send-log-retention-nightly) and the main retention cron — belt-and-braces |
Push send log (push_send_log) | 6 months | Age over threshold | pg_cron @ 04:15 UTC |
Email unsubscribe tokens (email_unsubscribe_token) | 30 days | Age over threshold | pg_cron @ 04:15 UTC |
Cron health log (cron_health_log) — clipped-run monitor | 90 days | Age over threshold | pg_cron every 30 min via detect_clipped_cron_runs() |
Consent records (user_consent) — statutory carve-out | 7 years (84 months) | Age over threshold (survives account deletion) | pg_cron @ 04:15 UTC — see § 9.1 |
Breach register (breach_register) — statutory carve-out | Indefinite | None — retained for Art. 33 record-keeping | No automated purge — see § 9.2 |
| Asynchronous task store (Redis) | 10 minutes | TTL | Redis-managed TTL expiry |
| Idempotency-Key store (Redis) | 24 hours | TTL | Redis-managed TTL expiry |
| Rate-limit keys (Redis) | 24-hour sliding window + 2-hour slack | TTL | Redis-managed TTL expiry |
Headline read
- Operational logs run on three-band retention. Security and access logs hold the longest (12–24 months); pipeline logs hold mid-term (6 months) and the raw LLM-cost log short-mid-term (90 days, with a permanent non-attributable daily aggregate); transactional Redis state holds short-term (minutes to a day).
- Email send-log retention is enforced twice. A standalone 04:20 UTC cron supplements the main 04:15 UTC retention sweep so the compliance team can observe the email-log path on its own schedule. Both jobs are idempotent.
- Two carve-outs survive account deletion — consent records and breach register. These are spelled out below.
7. AI-generated artefacts
Mentum applies data-minimisation controls at the boundary with Google Gemini. The data flowing outbound to Gemini varies by feature:
- CV parsing is the only path where candidate names are sent to Gemini, because the name appears in the CV body. The response is persisted to structured fields on
candidate_profile. - Match scoring, enhanced insights, skill estimation, career trajectory, culture-fit, advisor conversations all strip the candidate name before fan-out. Gemini sees structured profile data without the identifier.
The Gemini-generated outputs — match scores, insights, advisor transcripts, trajectory caches — are subject to the candidate-side retention rows in § 4. There is no separate Gemini-side persistence: per the sub-processor list, Google is contracted as a stateless processor and inputs are not retained for training under Mentum's enterprise terms.
8. User-triggered erasure (UK GDPR Art. 17)
A candidate or employer who wishes to exercise the right to erasure (“right to be forgotten”) under UK GDPR Article 17 does so by deleting their account in their app settings, which calls DELETE /api/auth/account.
8.1 What happens synchronously
When the deletion request is accepted (after explicit in-app confirmation), the request handler executes the role-appropriate RPC — delete_candidate_account or delete_employer_account — within the same HTTP request, before the 2xx response is returned. The RPC performs:
- Hard delete of all candidate-owned PII rows — profile, skills, experience, education, interests, question answers, saved matches, bookmarks, blocked companies, notifications, notification preferences, push subscriptions, accessibility preferences, availability records, and the related child rows.
- Storage-bucket purge — CV file and profile photo from object storage.
- Auth account deletion — the underlying authentication user row is deleted, which propagates as a SET NULL or cascade to every other table holding the foreign key.
- FK-cascading behaviour for match-history rows —
match_detailsrows have the candidate identifier set to NULL rather than being deleted, so the employer's hiring record remains structurally intact while the candidate is no longer attributable. Chat messages are scrubbed (content replaced with a deletion notice; sender and timestamp preserved).
8.2 What does not happen synchronously
- Backup retention. Supabase performs a daily encrypted backup with a 7-day rotation window. The deleted rows exist in those backups until the next rotation cycle naturally expires them. Mentum has no operational access to the backup contents and does not restore backups except in a documented disaster-recovery scenario (see § 10).
- Statutory carve-outs. Consent records (Art. 7(1) and Art. 17(3)(e)) and breach-register entries (Art. 33) survive account deletion. See § 9.
8.3 Cross-reference
For the procedural detail on exercising any data-subject right (verification, response times, complaint route), see Privacy Policy § 11.
9. Statutory retention carve-outs
Two categories survive account deletion because UK GDPR or PECR requires it.
9.1 Consent records
Retention: 7 years (84 months). Survives account deletion under UK GDPR Article 17(3)(e) (processing necessary for the establishment, exercise or defence of legal claims) read with Article 7(1) (the controller's burden to demonstrate that consent was given). The 7-year window aligns with the UK limitation period for contract and tort claims under the Limitation Act 1980.
Practical effect: even after a candidate or employer deletes their account, Mentum keeps an attenuated record showing which consent was given, at what time, for what purpose, and when it was withdrawn. The record does not include the substantive personal data the user has erased; it is a metadata receipt.
9.2 Breach register
Retention: indefinite. Retained under UK GDPR Article 33(5). The Article 33 record-keeping obligation has no defined expiry, and the supervisory authority (ICO) may request the historical record at any time during an investigation. The breach register contains incident metadata only; it does not store the affected data subjects' personal data beyond pseudonymised counts and category labels.
9.3 Other carve-outs
- Security telemetry retained for legitimate interest (
security_events,data_access_log) — see § 6. These are not consent-based and are retained for the duration described regardless of any deletion request, on the legitimate-interest basis (Art. 6(1)(f)) of operating a secure platform. The Art. 21 right to object is balanced against Mentum's documented legitimate interest. - Application audit logs (
audit_errors,audit_runs) are retained for the 12-month window described in § 6 on the same legitimate-interest basis.
10. Backup retention
10.1 Backup window
Supabase performs daily encrypted backups of the production database. The retention window is 7 days (Pro plan default). Point-in-time recovery (PITR) is available within the same 7-day window down to a 5-minute granularity.
10.2 What this means for an Art. 17 request
A data subject who has exercised the right to erasure will see their data removed from the live database within the same HTTP request (see § 8). The data continues to exist within the encrypted backup window for up to 7 calendar days, at which point the natural rotation removes the backup copy.
Mentum does not perform partial restores or surgical edits to backup contents — the operational shape of the backup system does not support row-level intervention. If a backup is restored as part of a disaster-recovery event, Mentum will re-run the retention process immediately after the restore so that prior erasure requests are re-applied.
10.3 Recovery targets
Mentum's Service Level Agreement commits to the following recovery posture, which is restated here for completeness:
- Recovery Point Objective (RPO): ≤ 5 minutes (PITR Write-Ahead Log window).
- Recovery Time Objective (RTO): ≤ 4 hours for the database tier.
- Storage RTO: ≤ 8 hours.
11. How retention is enforced
11.1 Automation
All time-based retention in this Schedule is enforced by pg_cron jobs running in the production database. There is no human review step. The relevant jobs:
retention-cleanup-dailyat 04:15 UTC daily — runs the masterrun_retention_cleanup()PL/pgSQL function which executes every cleanup case in §§ 4, 5, and 6 except the standalone email-log cron. Each case is wrapped in its own savepoint so a failure in one bucket does not abort the rest of the sweep.retention-storage-purge-dailyat 05:00 UTC daily — drains thepending_storage_purgequeue (populated by the 04:15 job when it nullscv_url) and deletes the actual storage bytes. Runs viapg_net→POST /api/internal/cron/run-retention-storage-purge(migration 499). The 04:15 job enqueues the object paths; this job removes the files — so CV-file destruction is a two-step flow, not a standalone Python sweeper.email-send-log-retention-nightlyat 04:20 UTC daily — standalone job foremail_send_logdeletion, kept separate from the master cron for compliance-team observability.detect-clipped-cron-runsevery 30 minutes — internal health monitor that flags any cron job whose runtime was clipped by Supabase's 120-second platform default. Writes tocron_health_log(90-day retention per § 6) and surfaces in the on-call alerts.
11.2 Configurability
Retention periods that are user-configurable via environment variables are flagged in the tables above with (configurable via HYRE_*). Operator-side changes to these variables are recorded in the source-control history and reflected on the next review of this Schedule.
11.3 Trigger types
Two trigger types govern retention:
- Date-based. “Age over 12 months” — the row's
created_ator equivalent column is compared againstnow() - interval '12 months'within the cron-driven RPC. Most rows in §§ 5 and 6 fall here. - State-based. “Closed match status” or
is_active = false— the row's status column drives the clock, not the calendar. Chat-message pseudonymisation and job-deletion paths fall here.
The candidate-profile pseudonymisation case in § 4 is inactivity-triggered: a candidate who never signs in has the 24-month and 36-month clocks started on the last sign-in date, not the account-creation date. Active users have no time-based retention against their candidate_profile row.
11.4 Monitoring trail
Each cron run writes to pipeline_run_log (6-month retention per § 6), giving Mentum an auditable record of when the retention sweep ran, what it deleted, and whether it completed within the platform timeout. The cron_health_log health monitor cross-checks this and alerts on clipped or skipped runs.
12. Changes to this schedule
Mentum updates this Schedule on an annual cadence (the Next scheduled review date at the top of this page) and off-cycle on any material code change to the retention RPCs, pg_cron schedules, or deletion paths.
- Material changes — a change to a retention period, a removal of a category, the addition of a new personal-data category, or a change to a deletion trigger — are notified to data subjects via the Privacy Policy banner and reflected in the Last reviewed date on this page.
- Editorial changes — clarifications, link updates, formatting fixes — do not trigger re-notification; the Last reviewed date is still bumped so the audit trail is unambiguous.
Data subjects or customers who require a historical version of any prior period of this Schedule may write to privacy@mentumjobs.com and we will provide the relevant prior version.
13. Contact and complaints
For questions or complaints about Mentum's retention practice:
- Data protection / DPO: dpo@mentumjobs.com and privacy@mentumjobs.com
- Security reports: security@mentumjobs.com
- Operational support: support@mentumjobs.com
If you are not satisfied with Mentum's response, you have the right to lodge a complaint with the Information Commissioner's Office (ICO):
- Website: ico.org.uk/make-a-complaint
- Telephone: 0303 123 1113
- Postal: Information Commissioner's Office, Wycliffe House, Water Lane, Wilmslow, Cheshire SK9 5AF
You may also lodge a complaint with the data-protection supervisory authority of your habitual residence or place of work within the UK or EEA.
Related: Privacy Policy | Terms of Service | Data Processing Addendum | Sub-processors | Service Level Agreement | Cookie Policy