SellVia Docs — menu
DocsSecurityData Retention Policy Engine

Data Retention Policy Engine

Security/Data Retention Policy Engine.md
backendUpdated Aug 7, 2026

Data Retention Policy Engine

Purpose

A configurable engine that enforces data retention per category, maps those periods toward real legal/regulatory obligations, and produces an audit trail proving the policy was actually followed — not just stated. Retention periods below are defensible defaults, explicitly not verified legal advice — same status as the tax/VAT and India IT Rules items already deferred to end-of-build compliance review.

Jurisdiction note: Market is Pakistan-only, PKR only (05. Payment Edge Cases' 2026-08-23 update) — relevant scoping context for which jurisdiction's legal/regulatory retention obligations actually apply, pending the legal/accounting confirmation flagged throughout this doc.

Architecture: Policy-as-Data, Not Policy-as-Code

Retention rules live in a retention_policies table, not scattered as hardcoded logic across the codebase — a policy change (once legal confirms real numbers) is a data update, not a code change or redeploy.

retention_policies
  id
  category            (e.g. "financial_records", "user_pii", "audit_logs")
  retention_period    (e.g. interval, or "indefinite")
  legal_basis         (nullable — filled in once confirmed by counsel, e.g. "IRS 7-year record rule")
  action_on_expiry    (anonymize / hard_delete / archive)
  confirmed           (boolean, default false — distinguishes a real legal-confirmed policy from a placeholder default)

Retention Categories and Working Defaults (unconfirmed unless noted)

CategoryDefaultAction on expiryStatus
Financial chain (Sales, Commissions, Payouts, Platform Fees, Refunds)7 years (commonly-cited baseline, matches 03. Database → Audit Log Design's existing "indefinite until confirmed" stance)Archive to cold storage, never hard-deleted⚠️ Placeholder — needs accountant/legal confirmation against actual operating jurisdictions
Audit logsSame as financial chain (they exist to support disputes about it)Archive⚠️ Placeholder
User PII (name, email, profile data)While account active; on deletion/closure, anonymized, not hard-deletedAnonymize (see below)Mechanism confirmed, exact trigger timing not yet set
Operational/application logs (06. Infrastructure → Logging)30–90 days (already decided there, unchanged)Hard deleteConfirmed, low legal sensitivity
Session history (04. Security → Session Management)90 days (working default)Hard delete⚠️ Placeholder
AI/token usage events (11. Analytics)1–2 yearsHard deleteBusiness choice, not a legal driver — lower stakes to get wrong
Email suppression list (unsubscribes, bounces)Indefinite — this one is NOT a placeholder. Anti-spam regulations (CAN-SPAM, CASL, and similar) generally require honoring unsubscribes permanently; purging a suppression list risks re-emailing someone who opted out, which is the actual violationNever purgedConfirmed — well-established practice, low ambiguity

The Right-to-Erasure Tension, Resolved by Anonymization

A real user request to delete their data (common under privacy regimes) conflicts directly with the legal obligation to retain financial records — SellVia can't simply delete a Sale/Commission row because the Creator who earned it asks to be forgotten. Resolution: anonymize PII fields (name, email, profile data) while retaining the transactional skeleton (amounts, dates, relationships) needed for the financial retention period above. This extends 03. Database → Soft Delete Policy's existing "never hard-delete the financial chain" principle with the specific mechanism for handling an erasure request against it.

The Engine (Scheduled Enforcement)

A Celery job (02. Background Jobs) runs on a schedule (e.g. daily), walks every retention_policies row, and for any data past its retention window, executes the configured action_on_expiry. This is what makes the policy real rather than aspirational — a documented policy nobody enforces provides no actual compliance value.

Audit Trail: Proving the Policy Was Followed

Every retention sweep logs its own execution to a retention_audit_log:

retention_audit_log
  id
  policy_id           (FK -> retention_policies)
  run_at
  records_affected
  action_taken
  status              (success / partial / failed)

This is what actually proves compliance during an audit or dispute — not the policy document itself, but a record that the engine ran, on schedule, and did what the policy said. Ties into 03. Database → Audit Log Design's existing framework rather than duplicating it as a separate system.

What This Doc Does NOT Do

It does not assert that the default periods above are legally correct for SellVia's actual operating jurisdictions — the confirmed flag on each policy exists specifically to distinguish "the engine is enforcing a placeholder" from "the engine is enforcing counsel-verified law." Treat every ⚠️ row as active technical debt against the compliance review already deferred to end of build, not as settled.

Open Questions

  • All ⚠️-marked periods above, pending real legal/accounting input — the engine is ready to receive the real numbers the moment they exist
  • Exact PII anonymization trigger: on explicit user request only, or also automatically after a period of account inactivity/closure — not yet decided

Update (2026-08-04): User-Initiated Deletion Pipeline

The retention engine above handles time-based expiry automatically. This adds the user-initiated flow — what actually happens when someone requests account deletion, built on 02. Async Job Pattern & Idempotency since this is exactly the kind of heavy, multi-step operation that pattern exists for.

Flow

flowchart TD
    A[User requests account deletion] --> B[Confirmation + grace period, e.g. 14 days]
    B --> C{User cancels within grace period?}
    C -- Yes --> D[Request cancelled, nothing happens]
    C -- No --> E[Deletion job created - Async Job Pattern]
    E --> F[Anonymize PII: name, email, profile data]
    F --> G[Retain financial-chain skeleton per confirmed retention period]
    G --> H[Revoke all sessions - Ory Kratos]
    H --> I[Cascade to third parties]
    I --> J[Job completed, confirmation sent to last-known contact]

Grace Period — Working Default, Please Confirm

14 days between request and actual processing, cancellable by the user at any point in that window. Protects against accidental requests and account-takeover-driven malicious deletion (someone else requesting deletion on a compromised account) — not previously specified anywhere.

What Happens to Each Data Category (per 04. Data Inventory & Disclosure)

  • Account identity, profile data: anonymized (name/email replaced with a non-identifying placeholder), never hard-deleted — unchanged from this doc's existing right-to-erasure resolution
  • Financial chain (Sales, Commissions, Payouts): retained in full per the confirmed retention period (currently a placeholder pending legal confirmation, same as elsewhere in this doc) — the transactional skeleton survives even though the identifying PII around it doesn't
  • Sessions (Ory Kratos): fully revoked, not retained
  • Uploaded files (product images, profile photos): deleted from object storage, except where a product image is still referenced by an active Campaign another party depends on — flagged as a genuine edge case not yet resolved (see Open Questions)

Cascading to Third Parties — Not Automatic Everywhere

This is the part most likely to be incomplete without explicit per-provider work:

  • Ory Kratos: identity deletion via its own API — straightforward
  • Swich (updated 2026-08-23, was Paddle): cannot simply delete an account with payment history — Swich's own retention rules apply independently of SellVia's, exact policy unconfirmed pending real integration; the SellVia-side profile anonymizes, but Swich-side transaction records (and any KYC data, if Swich collects it — unconfirmed, see 05. Tax Considerations) follow Swich's own policy, outside SellVia's control
  • Email ESPs: address moves to the suppression list (04. Data Retention Policy Engine's existing "never purged" rule for suppression data) — deliberately NOT deleted, since removing it risks re-emailing someone who explicitly left
  • AI/embeddings provider: depends on the specific provider's own data handling — not yet verified per-provider, flagged as an open item

Audit Trail

Every deletion request and its outcome logged to retention_audit_log (04. Data Retention Policy Engine's existing audit mechanism) — proves the pipeline actually ran, not just that a policy exists.

Open Questions

  • Grace period length (14 days) — working default, needs confirmation
  • Product image handling when still referenced by an active Campaign after the owning Creator/Merchant requests deletion — genuinely unresolved
  • Per-provider data deletion verification (AI/embeddings provider specifically) — not yet done

Update (2026-08-07): RESOLVED — Immediate Removal, Placeholder Shown

Founder-confirmed: when an account is deleted, its product images are removed from object storage immediately as part of the deletion pipeline — not deferred until a referencing campaign ends. Any campaign (active, ended, or historical) that referenced the image now displays a placeholder image instead of a broken link or a lingering copy. This is simpler than the alternatives considered (keep-until-campaign-ends, keep-permanently) and consistent with the deletion pipeline's overall bias toward actually removing what a user asked to be removed, rather than a wide range of "keep it around just in case" carve-outs.