An n8n flow that orchestrates a litigation-hold issue cycle — issuing the hold notice via email and Slack to named custodians, tracking acknowledgements with reminders on a configurable cadence, escalating non-acknowledgers to the legal-ops lead and the custodian’s manager, logging every action to an immutable audit table the firm uses if the hold’s adequacy is later questioned. Replaces the legal-ops admin’s spreadsheet-and-Outlook-rules manual cycle (typically a 2-4 hour-per-week ongoing burden once a few holds are active) with a deterministic flow that can’t drop a custodian.
When to use
The firm issues litigation holds at a frequency where manual tracking starts to slip — usually >3 active holds at any time.
You have a custodian-of-record source: a custodian-management table in the e-discovery platform (Relativity, Everlaw, Logikcull) or in a maintained legal-ops CSV.
The firm needs a defensible audit trail for hold adequacy. The flow’s audit table is the answer to “show us you acted reasonably to preserve.”
The legal-ops lead and outside counsel sign off on hold issuance; the flow handles the ongoing tracking, not the decision to issue.
When NOT to use
Single-hold engagements where manual tracking is fine. The setup cost (120 minutes, plus integration with custodian source) earns back at the ~3-active-holds mark, not at one.
Auto-escalation that bypasses counsel. The flow’s escalation paths are configurable, but the default sends to the legal-ops lead, NOT directly to the GC or outside counsel. The lead applies judgment about which non-acknowledgements warrant counsel attention.
Hold notices that need bespoke language per custodian. The flow templates per matter; if every notice needs hand-crafted language, the bottleneck isn’t the orchestration.
Replacing counsel’s judgment on hold scope. The flow tracks the hold to the custodians counsel named. Adding or removing custodians is a counsel decision, not a flow decision.
Setup
Import the flow. Drop apps/web/public/artifacts/litigation-hold-orchestration-n8n/litigation-hold-orchestration-n8n.json into your n8n instance.
Wire credentials. Four required: PLACEHOLDER_CUSTODIAN_DB_CRED_ID (read access to custodian source), PLACEHOLDER_SMTP_CRED_ID (SMTP for hold notice email), PLACEHOLDER_SLACK_CRED_ID (Slack for in-channel notification), PLACEHOLDER_AUDIT_DB_CRED_ID (write access to immutable audit table).
Author the hold-notice template. Per matter, write a Markdown template under n8n/data/hold-notices/<matter-id>.md. The template includes counsel-approved language about preservation scope, prohibited actions (deletion, alteration), and acknowledgement instructions.
Configure escalation paths. The flow’s default: reminder at +3, +7, +14 days; escalation to legal-ops lead at +14; escalation to custodian’s manager at +21. Tune per the firm’s risk posture.
Set up the audit table. A Postgres / Snowflake table keyed on (hold_id, custodian_id, action, timestamp) with an append-only constraint enforced at the DB level (immutable; counsel needs to demonstrate the audit log can’t be retroactively edited).
Dry-run on a closed hold. Replay a closed hold’s custodian list. Confirm the notification, reminder, and escalation timings match what the legal-ops admin previously did manually.
What the flow does
Seven nodes, in two phases. Phase 1 (issuance) fires once per hold. Phase 2 (tracking) is a daily cron checking for non-acknowledgers and dispatching reminders / escalations.
Issue Trigger — manual or webhook trigger from the legal-ops platform when counsel marks a hold ready to issue.
Load Custodian List — pulls the custodian list from the configured source for the matter.
Send Hold Notice — email + Slack to each custodian. The email includes the hold-notice template and a unique acknowledgement link per custodian. Audit log: one row per custodian per notice_sent.
Daily Cron Tracker (separate workflow trigger) — Mondays-Fridays at 9am office TZ. Checks the audit table for custodians who have not acknowledged within the configured window.
Determine Action — Code node. For each non-acknowledger, determines: send reminder (at +3, +7), escalate to legal-ops lead (at +14), escalate to manager (at +21).
Dispatch Reminder / Escalation — sends reminder email or escalation, depending on the determined action. Audit log entry per dispatch.
Acknowledgement Webhook — separate webhook that receives the custodian’s acknowledgement click. Records to audit table; stops further reminders for that custodian.
Cost reality
n8n cost — self-hosted free; n8n Cloud at the workflow execution count this generates (~3-5/day per active hold) is comfortably in the Starter plan.
LLM tokens — none. The flow is deterministic.
SMTP / Slack — within standard quotas.
Legal-ops admin time — the win. Manual tracking of 5-10 active holds is 4-8 hours/week. Flow operation is monitoring the audit table for true exceptions, ~30 min/week.
Setup time — 120 minutes including the audit table provisioning, plus 30-60 minutes per matter for the hold-notice template authoring.
Success metric
Time-to-issue from counsel’s “issue” decision — should drop to under 1 hour (manual is often a day for large custodian lists).
Acknowledgement rate at +14 days — should exceed 95% on routine holds. Below that, the notice template needs reworking or the custodian list has stale records.
Audit-completeness on counsel review — share of holds where counsel can produce a complete defensible audit chain on demand. Should be 100%; the audit table is the source.
vs alternatives
vs e-discovery platform’s built-in hold module (Relativity Legal Hold, Logikcull, Everlaw). Pick the platform module if you live in the e-discovery tool. Pick the flow if your custodians span Slack, email, and HR systems and you need notification surfaces beyond the platform’s defaults.
vs SaaS hold-management tools (Onna, Exterro Legal Hold). Pick those for advanced custodian-self-service and integrated preservation. Pick the flow if you want the orchestration in your own infrastructure with the audit log in your own DB.
vs spreadsheet + Outlook rules. The default and the source of dropped custodians at scale. The flow is the deterministic replacement.
Watch-outs
Custodian list drift.Guard: the flow re-pulls the custodian list per check, with the source’s last_updated_at checked. If the list has drifted (custodian added/removed) without counsel approval, the flow surfaces the diff to the legal-ops lead rather than silently acting on the new list.
Audit-table mutability.Guard: the audit table must be append-only at the DB level (Postgres: REVOKE UPDATE, DELETE FROM ALL). The flow does not enforce this — the DB does. The README documents the schema with the constraint inline.
Notice-text drift.Guard: per-matter notice templates are SHA’d at issuance time; the audit log captures the SHA. If counsel wants to amend the notice, the amendment is a separate action in the flow, not a silent re-issue.
Custodian opt-out / silent ignore.Guard: escalation to the manager at +21 days converts the issue from a custodian problem to a management problem. Beyond that, the legal-ops lead may need to involve outside counsel — the flow surfaces but does not act.
Cross-jurisdiction hold differences.Guard: the flow assumes US-style litigation hold semantics. EU-style “duty to preserve” under the upcoming AI Act and GDPR has different scope; the per-matter notice template handles those.
Privacy posture on custodian email.Guard: hold notices may flag the existence of a litigation matter to the custodian; the SMTP path uses the firm’s mail relay (not a third-party SaaS) for confidentiality.
Stack
The bundle lives at apps/web/public/artifacts/litigation-hold-orchestration-n8n/:
litigation-hold-orchestration-n8n.json — the flow export
audit-table-schema.sql — DDL for the immutable audit table
hold-notice-template.md — fillable hold-notice template per matter
Tools: n8n (orchestration), Slack (in-channel notification), Claude (optional — for the daily summary of audit-log activity to the legal-ops lead, not for any decision step).
# Litigation hold orchestration — n8n flow
Orchestrates a litigation-hold issue cycle: notification, acknowledgement tracking, reminder cadence, escalation paths, and an immutable audit log. Replaces the spreadsheet-and-Outlook-rules manual cycle with a deterministic flow that can't drop a custodian.
## Import
1. Import `litigation-hold-orchestration-n8n.json` into n8n.
2. Set workflow timezone to your office TZ.
3. Provision the audit table (DDL in `audit-table-schema.sql`).
4. Wire credentials.
5. Do NOT enable until dry-run confirms behavior on a closed hold.
## What ships in this bundle
This n8n export covers the **issuance** half of the cycle (Phase 1). The tracking + escalation half (Phase 2 — daily cron, reminders, escalation) is intentionally a separate workflow you wire to the same audit table; that lets the issuance flow be triggered fresh per hold while the tracker runs continuously across all active holds.
The Phase 2 tracker is a 4-node workflow:
1. Cron daily 9am office TZ.
2. Postgres query against the audit table: select hold/custodian pairs without `acknowledged` action.
3. Code node: determine action (reminder at +3, +7; escalation to legal-ops lead at +14; escalation to manager at +21).
4. Email send + audit-log append.
The DDL and the audit-query are in this README's appendix below; the Phase 2 export is left to the legal-ops engineer to assemble per the firm's escalation paths (which vary by jurisdiction and matter type more than the issuance step does).
## Credentials
### `PLACEHOLDER_CUSTODIAN_DB_CRED_ID` — Custodian source
Read access to wherever the custodian list lives:
- **In-house Postgres** — the default. Schema documented in the `Load Custodian List` node.
- **Relativity / Everlaw / Logikcull** — replace the Postgres node with an HTTP node calling the platform's custodian endpoint.
- **HRIS** — possible if HRIS is the source of truth, but legal-ops typically maintains the custodian list separately.
### `PLACEHOLDER_SMTP_CRED_ID` — SMTP
Use the **firm's own mail relay**, not a third-party SaaS like SendGrid. Hold notices may flag the existence of a litigation matter; routing them through a third party expands the privilege exposure.
### `PLACEHOLDER_SLACK_CRED_ID` — Slack bot token
`chat:write` scope. The bot needs to be invited to the workspaces where custodians live; per-custodian DMs require user-level scope which Slack handles automatically when the bot is in the workspace.
### `PLACEHOLDER_AUDIT_DB_CRED_ID` — Audit table
Write access to the audit table. The table itself must be append-only at the DB level (DDL below).
## Audit table schema
```sql
CREATE TABLE hold_audit (
audit_id BIGSERIAL PRIMARY KEY,
hold_id TEXT NOT NULL,
custodian_id TEXT NOT NULL,
action TEXT NOT NULL CHECK (action IN (
'notice_sent',
'reminder_sent',
'escalated_to_lead',
'escalated_to_manager',
'acknowledged',
'released'
)),
template_sha TEXT,
payload_json JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX hold_audit_lookup_idx ON hold_audit (hold_id, custodian_id, action);
-- Immutability: the audit table is the firm's defensibility chain.
-- Counsel needs to be able to demonstrate the log can't be edited.
REVOKE UPDATE, DELETE, TRUNCATE ON hold_audit FROM PUBLIC;
REVOKE UPDATE, DELETE, TRUNCATE ON hold_audit FROM <legal_ops_app_role>;
GRANT INSERT, SELECT ON hold_audit TO <legal_ops_app_role>;
```
The `REVOKE` statements are NOT optional. Without them the table is editable, and counsel cannot demonstrate the audit log's defensibility under e-discovery scrutiny.
## Hold-notice template
Save per-matter templates as `n8n/data/hold-notices/<matter-id>.md`. The template uses these placeholders, replaced per custodian by the `Personalize Notice` node:
- `{{custodian_name}}` — custodian's full name
- `{{matter_id}}` — matter identifier
- `{{hold_id}}` — hold identifier
- `{{ack_url}}` — per-custodian acknowledgement URL
Sample template structure (counsel approves the actual language):
```
Dear {{custodian_name}},
This is a litigation hold notice for Matter {{matter_id}}.
You are required to preserve all documents, communications, and electronic
data related to [counsel-defined scope]. This includes [specific systems
and document types].
You must NOT delete, alter, or destroy any potentially relevant material,
even if it would be deleted under normal retention policy.
Please acknowledge receipt of this notice by clicking the link below
within 7 business days:
{{ack_url}}
If you have questions about scope or what to preserve, contact [legal-ops
contact].
This hold remains in effect until you receive a written release notice
from the legal department.
[counsel signature block]
```
## Phase 2 tracker query
The Phase 2 daily cron uses this query to find non-acknowledgers:
```sql
WITH last_action AS (
SELECT hold_id, custodian_id, MAX(created_at) AS last_action_at,
BOOL_OR(action = 'acknowledged') AS acknowledged
FROM hold_audit
WHERE action IN ('notice_sent', 'reminder_sent', 'acknowledged', 'released')
GROUP BY hold_id, custodian_id
)
SELECT
la.hold_id,
la.custodian_id,
la.last_action_at,
EXTRACT(EPOCH FROM (NOW() - la.last_action_at)) / 86400 AS days_since_last_action,
(SELECT MIN(created_at) FROM hold_audit ha
WHERE ha.hold_id = la.hold_id AND ha.custodian_id = la.custodian_id
AND ha.action = 'notice_sent') AS first_notice_at
FROM last_action la
WHERE la.acknowledged = false
AND NOT EXISTS (
SELECT 1 FROM hold_audit ha
WHERE ha.hold_id = la.hold_id AND ha.custodian_id = la.custodian_id
AND ha.action = 'released'
);
```
The tracker computes `days_since_first_notice`, decides action, dispatches.
## Dry-run procedure
1. Provision the audit table on a non-production DB.
2. Wire credentials against staging endpoints (test SMTP, test Slack workspace).
3. Replay a closed hold's custodian list manually.
4. Confirm: notification fires once per custodian; audit log records each send; per-custodian acknowledgement URLs are unique.
5. Switch to production DB. Issue your next real hold via the flow.
## Known limits
- Phase 1 issuance only ships in this n8n export; Phase 2 tracker is documented but not bundled.
- The flow assumes per-custodian email + Slack. Custodians without Slack accounts (contractors, alumni) get email only — the Slack node fails gracefully but the failure is silent in the audit log. The Phase 2 tracker should treat email-only custodians the same as Slack+email.
- Acknowledgement URL routing requires a separate webhook endpoint (the `/ack/<hold>/<custodian>/<token>` URL) — deploy that as a small Express / Flask endpoint that writes `action: acknowledged` to the audit table. The endpoint is NOT bundled.
- The flow does not handle hold-release. Release is its own action — when counsel releases, a separate webhook writes `action: released` to the audit table per custodian. The tracker stops sending reminders to released custodian/hold pairs.
-- Litigation hold audit table — append-only DDL.
-- Counsel's defensibility chain depends on this table being immutable.
CREATE TABLE hold_audit (
audit_id BIGSERIAL PRIMARY KEY,
hold_id TEXT NOT NULL,
custodian_id TEXT NOT NULL,
action TEXT NOT NULL CHECK (action IN (
'notice_sent',
'reminder_sent',
'escalated_to_lead',
'escalated_to_manager',
'acknowledged',
'released'
)),
template_sha TEXT,
payload_json JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX hold_audit_lookup_idx
ON hold_audit (hold_id, custodian_id, action);
CREATE INDEX hold_audit_created_idx
ON hold_audit (created_at);
-- Immutability constraints. NOT optional.
-- Replace <legal_ops_app_role> with the DB role your n8n flow uses.
REVOKE UPDATE, DELETE, TRUNCATE ON hold_audit FROM PUBLIC;
GRANT INSERT, SELECT ON hold_audit TO <legal_ops_app_role>;
-- Optional: row-level revisioning if you anticipate legitimate
-- corrections (e.g. a custodian acknowledged offline and the lead
-- needs to record it). Even then, NEVER UPDATE existing rows —
-- INSERT a 'corrected_offline' action with a payload_json reference
-- to the prior row's audit_id, preserving the original record.
# Hold notice template
Per-matter template the litigation-hold flow personalizes per custodian. Copy this file to `n8n/data/hold-notices/<matter-id>.md` per matter and customize counsel-approved language.
The template uses these placeholders, replaced per custodian by the `Personalize Notice` node:
- `{{custodian_name}}`
- `{{matter_id}}`
- `{{hold_id}}`
- `{{ack_url}}`
---
# Litigation Hold Notice — Matter {{matter_id}} — Hold {{hold_id}}
Dear {{custodian_name}},
The firm's legal department is issuing a litigation hold related to Matter {{matter_id}}. As a custodian whose records may be relevant, you are required to take immediate steps to preserve potentially relevant material.
## What you must preserve
You must preserve all documents, communications, and electronic data related to [counsel-defined scope — replace with matter-specific scope]. This includes, but is not limited to:
- Email (work and any personal email used for work-related communication on this matter)
- Slack messages, including DMs and channel posts
- Documents (Google Drive, SharePoint, OneDrive, local files)
- Calendar entries
- [Other systems specific to this matter — replace with named systems]
The scope covers material from [counsel-defined date range] to the date this hold is released in writing.
## What you must NOT do
You must NOT delete, alter, modify, or destroy any potentially relevant material, even if the firm's normal retention policy would otherwise permit deletion.
You must NOT discuss this hold's scope or substance with anyone outside the legal team and your direct counsel of record.
## What this means in practice
- **Auto-deletion settings:** check email, Slack, and document-system retention settings. If any are set to delete material in the relevant scope, contact [legal-ops contact] immediately.
- **Personal devices:** if you have used personal devices or accounts for any work related to this matter, [legal-ops contact] will work with you on appropriate preservation.
- **Departing the firm:** if you leave the firm while this hold is in effect, [legal-ops contact] will work with HR and IT to preserve your records before any account decommissioning.
- **Replacement of equipment:** do not destroy or recycle hardware in scope until [legal-ops contact] confirms preservation.
## Acknowledgement
You must acknowledge receipt of this notice within 7 business days. Please click the link below:
[{{ack_url}}]({{ack_url}})
If you have not received an automated reminder by [date + 14 days], please contact [legal-ops contact] to confirm your acknowledgement was recorded.
## Questions
For questions about scope or what to preserve, contact:
- [legal-ops contact name and email]
- [outside counsel contact, if applicable]
Do NOT discuss the substance of the matter outside this channel.
## When this hold ends
This hold remains in effect until you receive a written release notice from the legal department. Verbal release is not sufficient. Until you receive written release, you must continue to preserve material in scope.
---
[counsel signature block]
[firm letterhead — match firm's standard hold-notice formatting]