|
| 1 | +# Legal Holds: User Interface Guide |
| 2 | + |
| 3 | +The legal holds management interface is located at **Dashboard → Compliance → Legal Holds**. It provides a complete view of all configured holds and tools for creating, applying, releasing, and deactivating them. Per-email hold controls are also available on each archived email's detail page. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +Legal holds suspend all automated and manual deletion for specific emails, regardless of any retention labels or policies that might otherwise govern them. They are the highest-priority mechanism in the data lifecycle and are intended for use by compliance officers and legal counsel responding to litigation, investigations, or audit requests. |
| 8 | + |
| 9 | +## Holds Table |
| 10 | + |
| 11 | +The main page displays a table of all legal holds with the following columns: |
| 12 | + |
| 13 | +- **Name:** The hold name and its UUID displayed underneath for reference. |
| 14 | +- **Reason:** A short excerpt of the hold's reason/description. Shows _"No reason provided"_ if omitted. |
| 15 | +- **Emails:** A badge showing how many archived emails are currently linked to this hold. |
| 16 | +- **Status:** A badge indicating whether the hold is: |
| 17 | + - **Active** (red badge): The hold is currently granting deletion immunity to linked emails. |
| 18 | + - **Inactive** (gray badge): The hold is deactivated; linked emails are no longer immune. |
| 19 | +- **Created At:** The date the hold was created, in local date format. |
| 20 | +- **Actions:** Dropdown menu with options depending on the hold's state (see below). |
| 21 | + |
| 22 | +The table is sorted by creation date in ascending order. |
| 23 | + |
| 24 | +## Creating a Hold |
| 25 | + |
| 26 | +Click the **"Create New"** button above the table to open the creation dialog. New holds are always created in the **Active** state. |
| 27 | + |
| 28 | +### Form Fields |
| 29 | + |
| 30 | +- **Name** (Required): A unique, descriptive name. Maximum 255 characters. |
| 31 | + Examples: `"Project Titan Litigation — 2026"`, `"SEC Investigation Q3 2025"` |
| 32 | +- **Reason** (Optional): A free-text description of the legal basis for the hold. Maximum 2 000 characters. This appears in the audit log and is visible to other compliance officers. |
| 33 | + |
| 34 | +### After Creation |
| 35 | + |
| 36 | +The hold immediately becomes active. No emails are linked to it yet — use Bulk Apply or the individual email detail page to add emails. |
| 37 | + |
| 38 | +## Editing a Hold |
| 39 | + |
| 40 | +Click **Edit** from the actions dropdown to modify the hold's name or reason. The `isActive` state is changed separately via the **Activate / Deactivate** action. |
| 41 | + |
| 42 | +## Activating and Deactivating a Hold |
| 43 | + |
| 44 | +The **Deactivate** / **Activate** option appears inline in the actions dropdown. Changing the active state does not remove any email links — it only determines whether those links grant deletion immunity. |
| 45 | + |
| 46 | +> **Important:** Deactivating a hold means that all emails linked *solely* to this hold lose their deletion immunity immediately. If any such emails have an expired retention period, they will be permanently deleted on the very next lifecycle worker cycle. |
| 47 | +
|
| 48 | +## Deleting a Hold |
| 49 | + |
| 50 | +A hold **cannot be deleted while it is active**. Attempting to delete an active hold returns a `409 Conflict` error with the message: _"Cannot delete an active legal hold. Deactivate it first..."_ |
| 51 | + |
| 52 | +To delete a hold: |
| 53 | +1. **Deactivate** it first using the Activate/Deactivate action. |
| 54 | +2. Click **Delete** from the actions dropdown. |
| 55 | +3. Confirm in the dialog. |
| 56 | + |
| 57 | +Deletion permanently removes the hold record and, via database CASCADE, all `email_legal_holds` link rows. The emails themselves are not deleted — they simply lose the protection that this hold was providing. Any other active holds on those emails continue to protect them. |
| 58 | + |
| 59 | +## Bulk Apply |
| 60 | + |
| 61 | +The **Bulk Apply** option (available only on active holds) opens a search dialog that lets you cast a preservation net across potentially thousands of emails in a single operation. |
| 62 | + |
| 63 | +### Search Fields |
| 64 | + |
| 65 | +- **Full-text query:** Keywords to match against email subject, body, and attachment content. This uses Meilisearch's full-text engine with typo tolerance. |
| 66 | +- **From (sender):** Filter by sender email address. |
| 67 | +- **Start date / End date:** Filter by the date range of the email's `sentAt` field. |
| 68 | + |
| 69 | +At least one of these fields must be filled before the **Apply Hold** button becomes enabled. |
| 70 | + |
| 71 | +### What Happens During Bulk Apply |
| 72 | + |
| 73 | +1. The system pages through all Meilisearch results matching the query (1 000 hits per page). |
| 74 | +2. Each hit's email ID is validated against the database to discard any stale index entries. |
| 75 | +3. New hold links are inserted in batches of 500. Emails already linked to this hold are skipped (idempotent). |
| 76 | +4. A success notification shows **how many emails were newly placed under the hold** (already-protected emails are not counted again). |
| 77 | +5. The exact search query JSON is written to the audit log as GoBD proof of the scope of protection. |
| 78 | + |
| 79 | +> **Warning:** Bulk Apply is a wide-net operation. Review your query carefully — there is no per-email confirmation step. Use the search page first to preview results before applying. |
| 80 | +
|
| 81 | +### Bulk Apply and the Audit Log |
| 82 | + |
| 83 | +The audit log entry for a bulk apply contains: |
| 84 | +- `action: "BulkApplyHold"` |
| 85 | +- `searchQuery`: the exact JSON query used |
| 86 | +- `emailsLinked`: number of emails newly linked |
| 87 | +- `emailsAlreadyProtected`: number of emails that were already under this hold |
| 88 | + |
| 89 | +## Release All Emails |
| 90 | + |
| 91 | +The **Release All** option (available when the hold has at least one linked email) removes every `email_legal_holds` link for this hold in a single operation. |
| 92 | + |
| 93 | +> **Warning:** This immediately lifts deletion immunity for all emails that were solely protected by this hold. Emails with expired retention periods will be deleted on the next lifecycle worker cycle. |
| 94 | +
|
| 95 | +A confirmation dialog is shown before the operation proceeds. On success, a notification reports how many email links were removed. |
| 96 | + |
| 97 | +## Per-Email Hold Controls |
| 98 | + |
| 99 | +### Viewing Holds on a Specific Email |
| 100 | + |
| 101 | +On any archived email's detail page, the **Legal Holds** card lists all holds currently applied to that email, showing: |
| 102 | +- Hold name and active/inactive badge |
| 103 | +- Date the hold was applied |
| 104 | + |
| 105 | +### Applying a Hold to a Specific Email |
| 106 | + |
| 107 | +In the Legal Holds card, a dropdown lists all currently **active** holds. Select a hold and click **Apply**. The operation is idempotent — applying the same hold twice has no effect. |
| 108 | + |
| 109 | +### Removing a Hold from a Specific Email |
| 110 | + |
| 111 | +Each linked hold in the card has a **Remove** button. Clicking it removes only the link between this email and that specific hold. The hold itself remains and continues to protect other emails. |
| 112 | + |
| 113 | +> **Note:** Removing the last active hold from an email means the email is no longer immune. If its retention period has expired, it will be deleted on the next lifecycle worker cycle. |
| 114 | +
|
| 115 | +### Delete Button Behaviour Under a Hold |
| 116 | + |
| 117 | +The **Delete Email** button on the email detail page is not disabled in the UI, but the backend will reject the request if the email is under an active hold. An error toast is displayed: _"Deletion blocked by retention policy (Legal Hold or similar)."_ |
| 118 | + |
| 119 | +## Permissions Reference |
| 120 | + |
| 121 | +| Operation | Required Permission | |
| 122 | +| -------------------------------- | ------------------- | |
| 123 | +| View holds table | `manage:all` | |
| 124 | +| Create / edit / delete a hold | `manage:all` | |
| 125 | +| Activate / deactivate a hold | `manage:all` | |
| 126 | +| Bulk apply | `manage:all` | |
| 127 | +| Release all emails from a hold | `manage:all` | |
| 128 | +| View holds on a specific email | `read:archive` | |
| 129 | +| Apply / remove a hold from email | `manage:all` | |
| 130 | + |
| 131 | +## Workflow: Responding to a Litigation Notice |
| 132 | + |
| 133 | +1. **Receive the litigation notice.** Identify the relevant custodians, date range, and keywords. |
| 134 | +2. **Create a hold**: Navigate to Dashboard → Compliance → Legal Holds and click **Create New**. Name it descriptively (e.g., `"Doe v. Acme Corp — 2026"`). Add the legal matter reference as the reason. |
| 135 | +3. **Bulk apply**: Click **Bulk Apply** on the new hold. Enter keywords, the custodian's email address in the **From** field, and the relevant date range. Submit. |
| 136 | +4. **Verify**: Check the email count badge on the hold row. Review the audit log to confirm the search query was recorded. |
| 137 | +5. **Individual additions**: If specific emails not captured by the bulk query need to be preserved, open each email's detail page and apply the hold manually. |
| 138 | +6. **When the matter concludes**: Click **Deactivate** on the hold, then **Release All** to remove all email links, and finally **Delete** the hold record if desired. |
| 139 | + |
| 140 | +## Troubleshooting |
| 141 | + |
| 142 | +### Cannot Delete Hold — "Cannot delete an active legal hold" |
| 143 | +**Cause:** The hold is still active. |
| 144 | +**Solution:** Use the **Deactivate** option from the actions dropdown first. |
| 145 | + |
| 146 | +### Bulk Apply Returns 0 Emails |
| 147 | +**Cause 1:** The search query matched no documents in the Meilisearch index. |
| 148 | +**Solution:** Verify the query in the main Search page to preview results before applying. |
| 149 | +**Cause 2:** All Meilisearch results were stale (emails deleted from the archive before this operation). |
| 150 | +**Solution:** This is a data state issue; the stale index entries will be cleaned up on the next index rebuild. |
| 151 | + |
| 152 | +### Delete Email Returns an Error Instead of Deleting |
| 153 | +**Cause:** The email is under one or more active legal holds. |
| 154 | +**Solution:** This is expected behavior. Deactivate or remove the hold(s) from this email before deleting. |
| 155 | + |
| 156 | +### Hold Emails Count Shows 0 After Bulk Apply |
| 157 | +**Cause:** The `emailCount` field is fetched when the page loads. If the bulk operation was just completed, refresh the page to see the updated count. |
0 commit comments