Skip to content

Retention + deletion — ZevSend

Honest snapshot of where retention stands today. NDPA §31 requires data to be kept "no longer than necessary" — the substance of that rule applies to ZevSend now; the automated enforcement of it across every table is partial today and on the roadmap to formalise.

Current state — short version

A few targeted cleanups already run on a cron; the rest of the tables retain for the lifetime of the team / message / domain. When the DPO requests a full retention schedule, the windows will be agreed and the relevant cleanup crons added (mirroring ZevID's pattern).

What runs today

Cleanup Where Cadence
Expired-but-unchecked verifications → status failed VerifyJanitorService Every 5 minutes
Subscription renewal sweep (no PII deleted; invoices generated) BillingRenewalService Daily 02:00 Africa/Lagos
Domain health re-check (no PII deleted; status transitions on stale DNS) DomainHealthService Every 5 minutes

No automated deletion of expired verification rows (the hash + envelope row stays around for the customer's check lookup window beyond the code's validity; it could be purged on a longer horizon — to be agreed).

What this means per category

Category Currently retained Comment
Team + membership Lifetime of the team Hard delete is admin-only and rare; a deactivated team's rows stay
Domains Lifetime of the team Customer deletion through dashboard removes the row (and de-registers the SES identity); admin deletion is permission-gated and audited
Display-name applications Lifetime of the team Approved + withdrawn + rejected rows all retained for audit
Sender-ID applications (SMS) Lifetime of the team Same — full audit trail retained
Templates (team-owned) Lifetime of the team Archive ≠ delete; archived templates are hidden from the customer catalogue but still readable by historical send rows
API keys Lifetime — revoked keys retained with revokedAt set Hash only; plaintext never persisted at any point
Send logs (email / SMS / WhatsApp messages) Indefinite today Operational deliverability + customer audit value; an ageing-out window will be agreed with the DPO
Verifications Indefinite today Hash only; plaintext OTP never persisted. Expired rows could be purged on a longer horizon
Suppressions Indefinite Required for anti-spam compliance — once a recipient has bounced or complained, the suppression stays unless the customer manually removes it
Verified recipients (sandbox-only) Lifetime of the team Sandbox-mode allowlist; deleted when the team is deleted
Webhook subscriptions Lifetime of the team Customer can delete a subscription at any time
Invoices + subscriptions + payment-method tokens Retained — financial-record-adjacent Expected to align with CBN 7-year financial-record retention when formalised
Admin audit logs Retained — security-forensic Expected long retention; will be agreed with DPO
ZPIP token + delivery audit Retained — replay protection + audit

Account deletion (customer-initiated)

When a customer asks to close their ZevSend team:

  • The team owner can request deletion via the dashboard's account-settings page (in progress; until shipped, requests come through support and are actioned by admin).
  • On deletion, the team's domains are de-registered from SES, API keys are revoked, templates are archived, and webhook subscriptions are disabled.
  • Suppression list rows for the team are retained — they're regulatory operational metadata, not customer-controlled data. If a recipient on a suppressed list later signs up to a different team and that team wants to send to them, the suppression is per-team, not platform-wide, so it does not block the new relationship.
  • Financial records (invoices, subscriptions) are retained per legal-obligation basis.
  • Cross-product cascade: ZevSend writes a de-enrollment record back to ZevID.

Deletion via subject-rights request

See subject-rights.md. The DPO office is the entry point for formal NDPA erasure requests.

Backups

Neon's point-in-time recovery covers the database. The retention horizon of the PITR window is governed by the Neon plan we're on (the DPO can confirm with Daniel for the current value).

When this will be formalised

When the DPO requests a retention schedule with concrete windows + a deletion cron, engineering will:

  1. Agree windows per category with the DPO (special attention to send-log rows: balancing customer deliverability-debugging value against minimum-necessary retention).
  2. Implement a cleanup.service.ts-style cron in zevsend-backend (mirroring the ZevID pattern).
  3. Update this page with the cron's name, schedule, and what it deletes.
  4. Add a row to change-log.md.