On /doc/[id], the **Scheduled deletion** section (admin / owner only) lets you set a future date when the document should auto-tombstone. Common patterns: NDAs that expire 5 years post-signing, marketing collateral with campaign deadlines, contractor records with mandatory purge dates.
**The flow:**
1. Open the document detail page. Find the "Scheduled deletion" section (above Template, above Danger zone). 2. Pick a future date (max 10 years out) and optionally type a reason for the audit log. 3. Click **Schedule deletion**. The doc carries the date in `auto_delete_at` until the cron runs or you cancel. 4. The daily Inngest sweep at 02:00 UTC finds every live doc whose date has passed and invokes the standard tombstone path with reason="Auto-delete scheduled on YYYY-MM-DD: <your reason>".
**Cancel any time before the date.** The "Cancel scheduled deletion" button clears the date and emits `document.auto-delete-cleared` on the audit chain.
**Legal-hold deny-wins still applies.** A held doc that passes its auto-delete date does NOT get tombstoned — `tombstoneDocumentTool` refuses regardless of caller (operator or cron). The sweep catches the refusal and emits a dedicated `document.auto-delete-blocked-by-hold` event so the audit log captures the attempt + the reason for the refusal. The auto_delete_at value is PRESERVED on the held doc; once the hold releases, the next sweep catches it.
**Distinct from retention classes.** Retention classes are class-level POLICY ("every doc tagged tax-records is kept 7 years") with human-confirmed disposal via /retention/review. Auto-delete is a one-off automatic action ("THIS specific NDA expires 2030-01-15") with no human in the loop at deletion time. Both can apply to the same doc — they don't conflict; the earlier deletion event wins.
**14-day-out reminder email.** The daily sweep ALSO finds docs whose auto_delete_at is between now and now + 14 days where last_auto_delete_warning_sent_at is null OR older than 13 days. For each, emails the doc creator + workspace owners + admins (deduped, capped at 50 recipients) with the doc name, ID, scheduled date, reason, and a deep link. Stamps the warning timestamp BEFORE the send so a transient retry doesn't double-fire. Re-armed on every setAutoDeleteAction / clearAutoDeleteAction so a freshly-rotated date generates a fresh 14-day-out reminder later. Closes the "I forgot it was scheduled" failure mode where operators returning from PTO discovered their NDA / contract / record had silently auto-deleted.
**Audit chain captures the full lifecycle:** - `document.auto-delete-scheduled` — operator set the date - `document.auto-delete-cleared` — operator cancelled before execution - `document.auto-delete-warning-sent` — cron emailed the doc creator + admins inside the 14-day pre-deletion window - `document.tombstoned` (with reason starting "Auto-delete scheduled") — cron successfully executed - `document.auto-delete-blocked-by-hold` — cron tried but legal hold refused
Audit consumers asking "every doc auto-deleted this quarter" filter on the tombstone reason; "every scheduled deletion ever set" filter on .auto-delete-scheduled; "every doc auto-deleted without a warning preceding" filter on tombstones lacking a preceding warning event on the same stream. All narratives are queryable.
**How the sweep scales (D282)**
The cron processes up to 2,000 eligible docs per run in `(autoDeleteAt, id)` ASC order, with durable cursor state stored in the `cron_checkpoints` table. If a single daily run fills the cap, the cursor is saved and the next day's run resumes where the prior run left off — consecutive daily runs incrementally drain a backlog instead of always restarting at row 0. When a run comes up short of the cap, the cursor resets so the next run starts from the beginning (catches any past-set autoDeleteAt written between runs). The cursor advances on every processed row including hold-blocked + failed, so a stuck row never wedges progress on subsequent runs.