Skip to content

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.

Models and errors

Pydantic v2 models for every dpdpkit aggregate. Every model carries tenant_id.

No ORM type ever crosses the repository boundary; adapters convert rows to these models.

DEFAULT_TENANT module-attribute

DEFAULT_TENANT = 'default'

OPEN_REQUEST_STATUSES module-attribute

ACTIVE_SCHEDULE_STATUSES module-attribute

TenantModel

Bases: _Model

tenant_id class-attribute instance-attribute

tenant_id: str = DEFAULT_TENANT

LegalBasis

Bases: str, Enum

Why processing is lawful: consent, or one of the legitimate uses in DPDP Act s.7.

CONSENT class-attribute instance-attribute

CONSENT = 'consent'

S7_A_VOLUNTARY class-attribute instance-attribute

S7_A_VOLUNTARY = 's7_a'

S7_B_STATE_BENEFIT class-attribute instance-attribute

S7_B_STATE_BENEFIT = 's7_b'

S7_C_STATE_FUNCTION class-attribute instance-attribute

S7_C_STATE_FUNCTION = 's7_c'
S7_D_LEGAL_OBLIGATION = 's7_d'

S7_E_COURT_ORDER class-attribute instance-attribute

S7_E_COURT_ORDER = 's7_e'

S7_F_MEDICAL_EMERGENCY class-attribute instance-attribute

S7_F_MEDICAL_EMERGENCY = 's7_f'

S7_G_PUBLIC_HEALTH class-attribute instance-attribute

S7_G_PUBLIC_HEALTH = 's7_g'

S7_H_DISASTER class-attribute instance-attribute

S7_H_DISASTER = 's7_h'

S7_I_EMPLOYMENT class-attribute instance-attribute

S7_I_EMPLOYMENT = 's7_i'

ConsentAction

Bases: str, Enum

GRANT class-attribute instance-attribute

GRANT = 'grant'

DENY class-attribute instance-attribute

DENY = 'deny'

WITHDRAW class-attribute instance-attribute

WITHDRAW = 'withdraw'

ConsentStatus

Bases: str, Enum

GRANTED class-attribute instance-attribute

GRANTED = 'granted'

DENIED class-attribute instance-attribute

DENIED = 'denied'

WITHDRAWN class-attribute instance-attribute

WITHDRAWN = 'withdrawn'

RequestKind

Bases: str, Enum

ACCESS class-attribute instance-attribute

ACCESS = 'access'

CORRECTION class-attribute instance-attribute

CORRECTION = 'correction'

COMPLETION class-attribute instance-attribute

COMPLETION = 'completion'

UPDATING class-attribute instance-attribute

UPDATING = 'updating'

ERASURE class-attribute instance-attribute

ERASURE = 'erasure'

NOMINATION class-attribute instance-attribute

NOMINATION = 'nomination'

GRIEVANCE class-attribute instance-attribute

GRIEVANCE = 'grievance'

RequestStatus

Bases: str, Enum

RECEIVED class-attribute instance-attribute

RECEIVED = 'received'

IDENTITY_PENDING class-attribute instance-attribute

IDENTITY_PENDING = 'identity_pending'

IN_PROGRESS class-attribute instance-attribute

IN_PROGRESS = 'in_progress'

COMPLETED class-attribute instance-attribute

COMPLETED = 'completed'

REJECTED class-attribute instance-attribute

REJECTED = 'rejected'

ScheduleStatus

Bases: str, Enum

SCHEDULED class-attribute instance-attribute

SCHEDULED = 'scheduled'

WARNING_PENDING class-attribute instance-attribute

WARNING_PENDING = 'warning_pending'

WARNED class-attribute instance-attribute

WARNED = 'warned'

HELD class-attribute instance-attribute

HELD = 'held'

NEEDS_ATTENTION class-attribute instance-attribute

NEEDS_ATTENTION = 'needs_attention'

CANCELLED class-attribute instance-attribute

CANCELLED = 'cancelled'

ERASED class-attribute instance-attribute

ERASED = 'erased'

RetentionTrigger

Bases: str, Enum

LAST_ACTIVITY class-attribute instance-attribute

LAST_ACTIVITY = 'last_activity'

PURPOSE_COMPLETE class-attribute instance-attribute

PURPOSE_COMPLETE = 'purpose_complete'

WITHDRAWAL class-attribute instance-attribute

WITHDRAWAL = 'withdrawal'

Channel

Bases: str, Enum

EMAIL class-attribute instance-attribute

EMAIL = 'email'

SMS class-attribute instance-attribute

SMS = 'sms'

WHATSAPP class-attribute instance-attribute

WHATSAPP = 'whatsapp'

DeliveryStatus

Bases: str, Enum

DELIVERED class-attribute instance-attribute

DELIVERED = 'delivered'

QUEUED class-attribute instance-attribute

QUEUED = 'queued'

FAILED class-attribute instance-attribute

FAILED = 'failed'

Role

Bases: str, Enum

VIEWER class-attribute instance-attribute

VIEWER = 'viewer'

HANDLER class-attribute instance-attribute

HANDLER = 'handler'

ADMIN class-attribute instance-attribute

ADMIN = 'admin'

allows

allows(needed: Role) -> bool

DataItem

Bases: TenantModel

id instance-attribute

id: str

name instance-attribute

name: str

description class-attribute instance-attribute

description: str = ''

category class-attribute instance-attribute

category: str | None = None

sensitive class-attribute instance-attribute

sensitive: bool = False

Processor

Bases: TenantModel

id instance-attribute

id: str

name instance-attribute

name: str

country class-attribute instance-attribute

country: str = 'IN'

contact class-attribute instance-attribute

contact: str | None = None

erasure_webhook class-attribute instance-attribute

erasure_webhook: str | None = None

DataStore

Bases: TenantModel

id instance-attribute

id: str

name instance-attribute

name: str

region class-attribute instance-attribute

region: str = 'IN'

kind class-attribute instance-attribute

kind: str = 'database'

RetentionRule

Bases: TenantModel

trigger class-attribute instance-attribute

period_days class-attribute instance-attribute

period_days: int | None = Field(default=None, ge=0)

third_schedule_class class-attribute instance-attribute

third_schedule_class: str | None = None

When set, the period comes from the policy pack's Third Schedule entry for this class.

Purpose

Bases: TenantModel

id instance-attribute

id: str

title instance-attribute

title: str

description class-attribute instance-attribute

description: str = ''

data_items class-attribute instance-attribute

data_items: list[str] = Field(default_factory=list)

legal_basis class-attribute instance-attribute

legal_basis: LegalBasis = LegalBasis.CONSENT

required class-attribute instance-attribute

required: bool = False

Necessary to provide the service. Still itemised in the notice; never pre-ticked.

processors class-attribute instance-attribute

processors: list[str] = Field(default_factory=list)

data_stores class-attribute instance-attribute

data_stores: list[str] = Field(default_factory=list)

retention class-attribute instance-attribute

retention: RetentionRule | None = None

NoticePurpose

Bases: _Model

id instance-attribute

id: str

title instance-attribute

title: str

description class-attribute instance-attribute

description: str = ''

data_items class-attribute instance-attribute

data_items: list[str] = Field(default_factory=list)

legal_basis class-attribute instance-attribute

legal_basis: LegalBasis = LegalBasis.CONSENT

required class-attribute instance-attribute

required: bool = False

NoticeContent

Bases: _Model

title instance-attribute

title: str

body class-attribute instance-attribute

body: str = ''

purposes instance-attribute

purposes: list[NoticePurpose]
links: dict[str, str]

contact class-attribute instance-attribute

contact: dict[str, str] = Field(default_factory=dict)

Notice

Bases: TenantModel

version class-attribute instance-attribute

version: int = Field(ge=1)

locale instance-attribute

locale: str

content instance-attribute

content: NoticeContent

content_hash instance-attribute

content_hash: str

published_at instance-attribute

published_at: datetime

ConsentEvent

Bases: TenantModel

id instance-attribute

id: str

seq class-attribute instance-attribute

seq: int = Field(ge=1)

principal instance-attribute

principal: str

purpose instance-attribute

purpose: str

action instance-attribute

action: ConsentAction

notice_version class-attribute instance-attribute

notice_version: int | None = None

notice_hash class-attribute instance-attribute

notice_hash: str | None = None

locale class-attribute instance-attribute

locale: str | None = None

source class-attribute instance-attribute

source: str = 'api'

occurred_at instance-attribute

occurred_at: datetime

metadata class-attribute instance-attribute

metadata: dict[str, Any] = Field(default_factory=dict)

prev_hash instance-attribute

prev_hash: str

hash instance-attribute

hash: str

AuditEntry

Bases: TenantModel

id instance-attribute

id: str

seq class-attribute instance-attribute

seq: int = Field(ge=1)

actor instance-attribute

actor: str

action instance-attribute

action: str

subject_type instance-attribute

subject_type: str

subject_id instance-attribute

subject_id: str

occurred_at instance-attribute

occurred_at: datetime

data class-attribute instance-attribute

data: dict[str, Any] = Field(default_factory=dict)

prev_hash instance-attribute

prev_hash: str

hash instance-attribute

hash: str

ConsentState

Bases: TenantModel

principal instance-attribute

principal: str

purpose instance-attribute

purpose: str

status instance-attribute

status: ConsentStatus

notice_version instance-attribute

notice_version: int | None

updated_at instance-attribute

updated_at: datetime

event_id instance-attribute

event_id: str

ConsentReceipt

Bases: TenantModel

receipt_id instance-attribute

receipt_id: str

principal instance-attribute

principal: str

issued_at instance-attribute

issued_at: datetime

consents instance-attribute

consents: list[ConsentState]

ledger_head instance-attribute

ledger_head: str

algorithm class-attribute instance-attribute

algorithm: Literal['HMAC-SHA256'] = 'HMAC-SHA256'

signature instance-attribute

signature: str

LedgerVerification

Bases: TenantModel

ok instance-attribute

ok: bool

checked instance-attribute

checked: int

consent_head instance-attribute

consent_head: str

audit_head instance-attribute

audit_head: str

failed_row class-attribute instance-attribute

failed_row: str | None = None

chain class-attribute instance-attribute

chain: str | None = None

reason class-attribute instance-attribute

reason: str | None = None

verified_at instance-attribute

verified_at: datetime

RootHash

Bases: TenantModel

consent_head instance-attribute

consent_head: str

consent_seq instance-attribute

consent_seq: int

audit_head instance-attribute

audit_head: str

audit_seq instance-attribute

audit_seq: int

root instance-attribute

root: str

exported_at instance-attribute

exported_at: datetime

RightsRequest

Bases: TenantModel

id instance-attribute

id: str

principal instance-attribute

principal: str

kind instance-attribute

status instance-attribute

status: RequestStatus

opened_at instance-attribute

opened_at: datetime

due_at instance-attribute

due_at: datetime

max_due_at instance-attribute

max_due_at: datetime

details class-attribute instance-attribute

details: dict[str, Any] = Field(default_factory=dict)

identity_verified class-attribute instance-attribute

identity_verified: bool | None = None

assignee class-attribute instance-attribute

assignee: str | None = None

response class-attribute instance-attribute

response: str | None = None

response_contact class-attribute instance-attribute

response_contact: dict[str, str] | None = None

outcome class-attribute instance-attribute

outcome: str | None = None

closed_at class-attribute instance-attribute

closed_at: datetime | None = None

updated_at instance-attribute

updated_at: datetime

Grievance

Bases: RightsRequest

A rights request of kind grievance; kept as a distinct type for adapters that want one.

kind class-attribute instance-attribute

Nominee

Bases: TenantModel

id instance-attribute

id: str

principal instance-attribute

principal: str

name instance-attribute

name: str

contact instance-attribute

contact: str

relationship class-attribute instance-attribute

relationship: str | None = None

created_at instance-attribute

created_at: datetime

revoked_at class-attribute instance-attribute

revoked_at: datetime | None = None

IdentityCheck

Bases: TenantModel

request_id instance-attribute

request_id: str

verified instance-attribute

verified: bool

method class-attribute instance-attribute

method: str | None = None

checked_by instance-attribute

checked_by: str

checked_at instance-attribute

checked_at: datetime

SlaStatus

Bases: _Model

due_at instance-attribute

due_at: datetime

max_due_at instance-attribute

max_due_at: datetime

remaining_seconds instance-attribute

remaining_seconds: int

overdue instance-attribute

overdue: bool

ceiling_breached instance-attribute

ceiling_breached: bool

ErasureSchedule

Bases: TenantModel

id instance-attribute

id: str

principal instance-attribute

principal: str

purpose instance-attribute

purpose: str

trigger instance-attribute

status instance-attribute

erase_at instance-attribute

erase_at: datetime

warn_at instance-attribute

warn_at: datetime

reason class-attribute instance-attribute

reason: str = ''

warning_receipt_id class-attribute instance-attribute

warning_receipt_id: str | None = None

warned_at class-attribute instance-attribute

warned_at: datetime | None = None

attention_reason class-attribute instance-attribute

attention_reason: str | None = None

cancelled_reason class-attribute instance-attribute

cancelled_reason: str | None = None

erased_at class-attribute instance-attribute

erased_at: datetime | None = None

processor_fanout class-attribute instance-attribute

processor_fanout: dict[str, str] = Field(default_factory=dict)

created_at instance-attribute

created_at: datetime

updated_at instance-attribute

updated_at: datetime

LegalHold

Bases: TenantModel

id instance-attribute

id: str

principal instance-attribute

principal: str

purpose class-attribute instance-attribute

purpose: str | None = None

None holds every purpose for the principal.

reason instance-attribute

reason: str

placed_by instance-attribute

placed_by: str

placed_at instance-attribute

placed_at: datetime

released_at class-attribute instance-attribute

released_at: datetime | None = None

ErasureRun

Bases: TenantModel

id instance-attribute

id: str

started_at instance-attribute

started_at: datetime

finished_at instance-attribute

finished_at: datetime

warned class-attribute instance-attribute

warned: int = 0

warning_pending class-attribute instance-attribute

warning_pending: int = 0

erased class-attribute instance-attribute

erased: int = 0

held class-attribute instance-attribute

held: int = 0

needs_attention class-attribute instance-attribute

needs_attention: int = 0

cancelled class-attribute instance-attribute

cancelled: int = 0

PreviewItem

Bases: _Model

schedule_id instance-attribute

schedule_id: str

principal instance-attribute

principal: str

purpose instance-attribute

purpose: str

action instance-attribute

action: Literal['warn', 'erase']

at instance-attribute

at: datetime

status instance-attribute

Contact

Bases: _Model

email class-attribute instance-attribute

email: str | None = None

phone class-attribute instance-attribute

phone: str | None = None

whatsapp class-attribute instance-attribute

whatsapp: str | None = None

locale class-attribute instance-attribute

locale: str | None = None

Message

Bases: _Model

channel instance-attribute

channel: Channel

to instance-attribute

to: str

subject instance-attribute

subject: str

body instance-attribute

body: str

template instance-attribute

template: str

locale class-attribute instance-attribute

locale: str = 'en'

principal class-attribute instance-attribute

principal: str | None = None

DeliveryReceipt

Bases: TenantModel

id instance-attribute

id: str

principal instance-attribute

principal: str | None

channel instance-attribute

channel: Channel

to_masked instance-attribute

to_masked: str

template instance-attribute

template: str

status instance-attribute

provider instance-attribute

provider: str

provider_ref class-attribute instance-attribute

provider_ref: str | None = None

error class-attribute instance-attribute

error: str | None = None

created_at instance-attribute

created_at: datetime

delivered_at class-attribute instance-attribute

delivered_at: datetime | None = None

deprecation

Deprecation helpers used by every dpdpkit package (see "Deprecation policy" in the docs).

A deprecated API keeps working for at least two minor releases (and at least 90 days) after the release that deprecates it, warns with DpdpkitDeprecationWarning every time it is used, and is listed under "Deprecated" in the changelog.

Functions, methods and classes are marked with PEP 702 so type checkers report callers too. The message must be a string literal (mypy ignores computed messages) in the standard wording::

from typing_extensions import deprecated

@deprecated(
    "Ledger.export_roots() is deprecated since dpdpkit 0.3 and will be removed in 0.5; "
    "use Ledger.export_root() instead",
    category=DpdpkitDeprecationWarning,
)
def export_roots(self): ...

Parameters, config keys and renamed module attributes use warn_deprecated and moved.

DpdpkitDeprecationWarning

Bases: DeprecationWarning

Something you use will be removed in a later dpdpkit release. The message says what replaces it.

Python hides DeprecationWarning outside __main__ and test runners; to see these everywhere, run with -W always::dpdpkit.DpdpkitDeprecationWarning.

warn_deprecated

warn_deprecated(name: str, *, since: str, removed_in: str, use: str | None = None, stacklevel: int = 3) -> None

Warn about a deprecated parameter, config key or value from inside the code that still accepts it.

stacklevel=3 points at the caller of the function that calls this helper.

moved

moved(module_name: str, renames: dict[str, tuple[str, Any, str, str]]) -> Callable[[str], Any]

Build a module __getattr__ that keeps old names importable with a warning.

renames maps an old attribute name to (new dotted name, object, since, removed_in)::

__getattr__ = moved(__name__, {"OldName": ("dpdpkit.models.NewName", NewName, "0.3", "0.5")})

deprecation_message

deprecation_message(name: str, *, since: str, removed_in: str, use: str | None = None) -> str

Exceptions raised by dpdpkit. Each carries a stable code used in REST error bodies.

DpdpkitError

DpdpkitError(message: str, **details: Any)

Bases: Exception

code class-attribute instance-attribute

code = 'dpdpkit_error'

http_status class-attribute instance-attribute

http_status = 500

message instance-attribute

message = message

details instance-attribute

details = details

to_dict

to_dict() -> dict[str, Any]

PolicyError

PolicyError(message: str, **details: Any)

Bases: DpdpkitError

The policy pack is missing or invalid. The kit refuses to start.

code class-attribute instance-attribute

code = 'policy_invalid'

ConfigError

ConfigError(message: str, **details: Any)

Bases: DpdpkitError

code class-attribute instance-attribute

code = 'config_invalid'

NotFound

NotFound(message: str, **details: Any)

Bases: DpdpkitError

code class-attribute instance-attribute

code = 'not_found'

http_status class-attribute instance-attribute

http_status = 404

ValidationFailed

ValidationFailed(message: str, **details: Any)

Bases: DpdpkitError

code class-attribute instance-attribute

code = 'invalid_request'

http_status class-attribute instance-attribute

http_status = 422

NoticeInvalid

NoticeInvalid(message: str, **details: Any)

Bases: ValidationFailed

code class-attribute instance-attribute

code = 'notice_invalid'

InvalidTransition

InvalidTransition(message: str, **details: Any)

Bases: DpdpkitError

code class-attribute instance-attribute

code = 'invalid_transition'

http_status class-attribute instance-attribute

http_status = 409

ConsentRequired

ConsentRequired(purpose: str)

Bases: DpdpkitError

code class-attribute instance-attribute

code = 'consent_required'

http_status class-attribute instance-attribute

http_status = 403

purpose instance-attribute

purpose = purpose

SequenceConflict

SequenceConflict(message: str, **details: Any)

Bases: DpdpkitError

Raised by a repository when two writers race for the same ledger sequence number.

code class-attribute instance-attribute

code = 'sequence_conflict'

http_status class-attribute instance-attribute

http_status = 409

LedgerTampered

LedgerTampered(row_id: str, chain: str, reason: str)

Bases: DpdpkitError

verify() found a row whose hash or chain link does not match.

code class-attribute instance-attribute

code = 'ledger_tampered'

row_id instance-attribute

row_id = row_id

chain instance-attribute

chain = chain

reason instance-attribute

reason = reason

details instance-attribute

details = {'row_id': row_id, 'chain': chain, 'reason': reason}