Data retention
Rungs never deletes data on its own. How long you keep approvals, and how you honor requests to erase personal data (for example under GDPR or Indonesia’s UU PDP), is your policy; the PostgreSQL store gives you the functions to carry it out (EC-61).
// A scheduled job: keep closed requests two years, per tenant. Log what was deleted.const result = await purgeRequests(db, { tenantId: "acme", // or allTenants: true; one of the two is required closedBefore: "2026-01-01T00:00:00.000Z",});console.log(result); // { requests: 1, auditEvents: …, outboxEvents: …, idempotencyKeys: 1 }Runs in CI:examples/docs-snippets/src/guides/retention.ts
purgeRequests deletes closed requests (approved, rejected or withdrawn) with their audit trail,
outbox events, inbox rows and idempotency records, in one transaction, at most limit (default
1,000) per call. The tenant scope is always explicit: a forgotten option never deletes another
tenant’s data.
// A request to erase one person's data: purge their closed requests one by one.// An open request is refused with InvalidTransitionError; withdraw or decide it first.const refused = await purgeRequest(db, open.id).catch((error: unknown) => error);Runs in CI:examples/docs-snippets/src/guides/retention.ts
Housekeeping
Section titled “Housekeeping”await purgeIdempotency(db, { olderThan: "2026-01-01T00:00:00.000Z" });await purgeDispatchedEvents(db, { olderThan: "2026-01-01T00:00:00.000Z" });Runs in CI:examples/docs-snippets/src/guides/retention.ts
After purgeIdempotency, repeating one of those commands runs it again, so keep keys at least as
long as clients may retry. purgeDispatchedEvents never touches undelivered events.
Outside these functions, audit rows cannot be updated or deleted: a database trigger refuses it. Data your application copied elsewhere (emails, logs, your own tables) is yours to delete.