Not legal advice
dpdpkit is software that helps you implement obligations under India's Digital Personal Data Protection Act, 2023 and the DPDP Rules, 2025. It does not provide legal advice and does not guarantee compliance.
Retention and erasure¶
retention
¶
Erasure schedules, pre-erasure warnings, legal holds and processor fan-out (DPDP Rules, Rule 8).
Fail-safe rules enforced here:
- An erasure runs only after its warning is recorded as delivered, and only once the full warning
period (
retention.erasure_pre_notice_hours) has passed since delivery. - A failed or impossible warning, or a failing erasure handler, moves the schedule to
needs_attention. Nothing is retried destructively without a person resolving it. - Activity by the principal after the warning cancels an inactivity-based erasure.
- A legal hold blocks warning and erasure until it is released.
- Ledger and audit rows are never erased; they are kept at least
min_log_retention_days.
ErasureHandler
module-attribute
¶
ErasureHandler = Callable[[str, list[str]], None]
handler(principal, purposes) deletes or anonymises the principal's data. Raise to signal failure.
RetentionService
¶
RetentionService(kit: Kit)
register_handler
¶
register_handler(handler: ErasureHandler) -> ErasureHandler
Register a function that erases a principal's data for the given purposes.
record_activity
¶
record_activity(principal: str, at: datetime | None = None) -> list[ErasureSchedule]
The principal logged in or contacted the fiduciary. Restarts inactivity clocks and cancels any inactivity erasure that has already been warned about (Rule 8(2)).
purpose_completed
¶
purpose_completed(principal: str, purpose_id: str, *, actor: str = 'system') -> ErasureSchedule | None
schedule
¶
schedule(principal: str, purpose_id: str, erase_at: datetime, *, reason: str, actor: str) -> ErasureSchedule
place_hold
¶
place_hold(principal: str, reason: str, *, actor: str, purpose: str | None = None) -> LegalHold
run
¶
run(now: datetime | None = None) -> ErasureRun
Send due warnings and perform due erasures. Safe to call as often as you like.
confirm_warning_delivery
¶
confirm_warning_delivery(receipt_id: str, delivered: bool, *, at: datetime | None = None, error: str | None = None) -> ErasureSchedule
Called when a queued transport reports the final outcome of a warning.
erase_now
¶
erase_now(principal: str, *, reason: str, actor: str, purposes: Iterable[str] | None = None) -> ErasureResult
Erase immediately because the principal asked (rights request). No warning is needed, but legal holds and purposes retained under a legal obligation are respected.
preview
¶
preview(now: datetime | None = None, horizon: timedelta = timedelta(days=7)) -> list[PreviewItem]
schedules
¶
schedules(principal: str | None = None, *, active_only: bool = True) -> list[ErasureSchedule]
resolve
¶
resolve(schedule_id: str, action: Literal['retry', 'cancel'], *, actor: str, reason: str = '') -> ErasureSchedule
A person decides what happens to a needs_attention schedule.
log_floor
¶
log_floor(occurred_at: datetime) -> datetime
Earliest time a ledger, audit or processing-log row written at occurred_at may be purged.
ErasureResult
dataclass
¶
ErasureResult(erased_purposes: list[str] = list(), held_purposes: list[str] = list(), retained_purposes: list[str] = list(), failed: list[str] = list())
erased_purposes
class-attribute
instance-attribute
¶
erased_purposes: list[str] = field(default_factory=list)
held_purposes
class-attribute
instance-attribute
¶
held_purposes: list[str] = field(default_factory=list)
retained_purposes
class-attribute
instance-attribute
¶
retained_purposes: list[str] = field(default_factory=list)