Legal Holds
A legal hold preserves documents that may be relevant to litigation, an investigation, or an audit. While a hold is active, nothing it covers can be deleted by any path in the system, including retention expiry and full workspace deletion. A hold is not a workflow suggestion: it is enforced by the platform. Every deletion path checks for active holds and refuses to proceed while one applies, and the check fails closed. If the system cannot positively confirm that no hold covers a document, the deletion does not happen.
A hold covers documents at one of three scopes. A tenant hold covers every document in the workspace: broad preservation at the start of a matter, before scope is clear. A source connection hold covers every document ingested through one connection: "preserve everything from the finance mailbox". A document set hold covers an explicit list of documents: targeted preservation once the relevant records are identified. Documents can be added to an existing document-set hold as the matter develops, and a document can be covered by multiple holds at once; it remains protected until every hold covering it is released.
Each hold carries metadata that ties it back to the real-world matter: a name (for example "Miller v. Acme preservation"), a case reference (your matter number or internal case ID), a custodian (the person or team responsible for the held material), and a free-text reason recorded for the audit trail. These fields make the hold self-documenting: anyone reviewing your audit trail later can see why each hold existed, who owned it, and which matter it belonged to.
Applying a hold
Workspace admins create holds from Settings under Archive or through the API. Choose the scope, fill in the name, case reference, custodian, and reason, and create the hold. It is active immediately. For document-set holds, attach documents at creation or add them later; each attachment takes effect immediately. Applying a hold emits a hold.applied audit event recording who created it, when, and its scope. Held documents carry an on_hold flag in the document list and API, so you can verify coverage with a filter.
# Create a document-set hold (admin only)
curl -X POST "$API_URL/records/legal-holds" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Miller v. Acme preservation",
"case_reference": "2026-CV-1847",
"custodian": "Legal / J. Winters",
"reason": "Preservation duty arising from litigation notice dated 2026-07-01",
"scope_type": "document_set",
"document_ids": ["<doc-id-1>", "<doc-id-2>"]
}'
# Attach more documents as the matter develops
curl -X POST "$API_URL/records/legal-holds/$HOLD_ID/documents" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"document_ids": ["<doc-id-3>"]}'Releasing a hold
Releasing a hold is deliberately harder than applying one, because release re-enables destruction. A workspace admin initiates the release, and the system requires a typed confirmation: you type the hold's exact name to prove the release is intentional, not an accidental click. A release reason is required and recorded, along with who released the hold and when, and a hold.released audit event is written. Workspaces that enable the step-up requirement additionally demand a fresh TOTP code (the X-Step-Up-Code header) on hold release, disposition approval, erasure actions, and tenant export, so possession of a logged-in session alone is not enough for the highest-impact actions. After release, documents protected only by that hold return to their normal lifecycle. If a document's retention period expired while it was held, it is not destroyed automatically: it enters the standard disposition review queue and still requires explicit approval.
curl -X POST "$API_URL/records/legal-holds/$HOLD_ID/release" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"confirm_name": "Miller v. Acme preservation",
"reason": "Matter settled; preservation duty ended 2026-07-15"
}'
# A confirm_name that does not exactly match the hold name is rejected
# with a validation error and nothing is released.Holds and retention: the hold always wins
Retention policies and legal holds can point in opposite directions, and the rule is simple: an active hold always overrides retention expiry. A document past its retain_until date that is covered by a hold is not destroyed and does not leave the system. The retention engine may mark it pending disposition, but the disposition path re-checks holds immediately before destruction and fails closed. The hold does not change the retention clock, and it does not extend retention for its own sake: when the hold is released, the document's retention state is evaluated as it stands, and an already-expired document goes to the disposition review queue.
What is blocked while a hold is active
| Parameter | Type | Description |
|---|---|---|
| User document deletion | UI or API | Refused with an explicit hold error. In archive mode even the soft delete is refused, so a held document does not disappear from listings. |
| Disposition destruction | records workflow | Refused. The document stays in the queue and the failed hold check is recorded. |
| Storage-level file deletion | privileged path | The file store performs its own hold check before deleting blobs and fails closed if the check cannot complete. |
| Workspace deletion | admin operation | Refused entirely while any active hold exists in the workspace. |
Workspace deletion deserves emphasis: even a request to delete the entire workspace is refused while holds are active, and the refusal is recorded as a tenant.deletion_blocked_by_hold audit event. A platform superadmin can override this in exceptional circumstances, but the override requires its own typed confirmation and writes a hold.override_released audit event identifying who overrode which hold and why. There is no silent path around a hold.
hold.applied with scope and case reference, hold.released with the release reason, and superadmin overrides as separately recorded hold.override_released events. Because audit events are hash-chained and survive document destruction, you can demonstrate after the fact that a hold was in place for a given period and that nothing covered by it was destroyed.