Skip to content

caseHoldPolicies

Case Hold Policies. Legal-hold policies preserving content for an eDiscovery case. The caseHoldPolicies entity set lives under /exchange/v1.0/caseHoldPolicies in the eDiscovery & audit category.

Fact Value
Path /exchange/v1.0/caseHoldPolicies
Category eDiscovery & audit
Tier Tier 5, Compliance
Read role Compliance.IPPS.Read.All (Community)
Write role none (read-only)
Backing cmdlets Get-CaseHoldPolicy, Set-CaseHoldPolicy, New-CaseHoldPolicy, Remove-CaseHoldPolicy
Method Path Synopsis Role Plan
GET /exchange/v1.0/caseHoldPolicies List caseHoldPolicies entries within a specific compliance case. Compliance.IPPS.Read Community
POST /exchange/v1.0/caseHoldPolicies Create a new caseHoldPolicies entry. Body keys map 1:1 to New-CaseHoldPolicy parameter names (camelCased). Required: name + case (parent compliance case identifier). Returns 201 + Location + the freshly-fetched policy. Compliance.IPPS.ReadWrite Enterprise
GET /exchange/v1.0/caseHoldPolicies/{key} Get a single caseHoldPolicies entry by Identity (Name or GUID). Compliance.IPPS.Read Community
PATCH /exchange/v1.0/caseHoldPolicies/{key} Partial update of the caseHoldPolicies at /caseHoldPolicies/{key}. Body keys map 1:1 to Set-CaseHoldPolicy parameters. Use Add*/Remove* location params for delta updates rather than replacing whole lists. Returns 200 + the post-Set policy. Compliance.IPPS.ReadWrite Enterprise
DELETE /exchange/v1.0/caseHoldPolicies/{key} Remove the caseHoldPolicies at /caseHoldPolicies/{key}. Pre-checks distributionStatus and returns 409 PolicyDistributionPending while the policy is still distributing to workloads (per MS SCC docs - Remove-CaseHoldPolicy fails opaquely in that state). Returns 204 on success. Compliance.IPPS.ReadWrite Enterprise

List caseHoldPolicies entries within a specific compliance case.

Backing cmdlet: Get-CaseHoldPolicy. Role: Compliance.IPPS.Read (Community).

Parameter In Type Required Description
case query string Compliance case Name or GUID. REQUIRED - the cmdlet rejects list-all without a case scope.
distributionDetail query bool (switch) Per-location membership breakdown. Slower; opt in only when needed.
includeBindings query bool (switch) Include policy bindings on locations.

List caseHoldPolicies without ?case= - 400 MissingRequiredQuery

Section titled “List caseHoldPolicies without ?case= - 400 MissingRequiredQuery”

Get the caseHoldPolicies list endpoint without scoping to a compliance case. Get-CaseHoldPolicy rejects list-all (raises ErrorMustSpecifyCaseOrPolicyException server-side); the controller pre-checks and returns 400 with a canonical envelope instead of letting the cmdlet error escape as 500. Fix: pass ?case=<caseName|caseGuid>, or fetch a single policy via GET /caseHoldPolicies/{key}.

GET /exchange/v1.0/caseHoldPolicies HTTP/1.1
Authorization: Bearer {token}
HTTP/1.1 400 Bad Request
{
"error": {
"code": "MissingRequiredQuery",
"message": "The 'case' query parameter is required for listing caseHoldPolicies. Get-CaseHoldPolicy rejects list-all - scope to a case (e.g. ?case=MyCase) or fetch a single policy via GET /caseHoldPolicies/{key}."
}
}

Related: GET /exchange/v1.0/caseHoldPolicies/{key}

Create a new caseHoldPolicies entry. Body keys map 1:1 to New-CaseHoldPolicy parameter names (camelCased). Required: name + case (parent compliance case identifier). Returns 201 + Location + the freshly-fetched policy.

Creates a legal-hold policy scoped to a parent compliance case. The case field is the Name or GUID of an existing compliance case - the API does NOT expose a /complianceCases entity set; callers must create the parent case via the Purview portal or Microsoft Graph eDiscovery API first. New-CaseHoldPolicy will fail if the referenced case doesn’t exist. At least one location (exchangeLocation, sharePointLocation, or publicFolderLocation) is typically required for a meaningful hold; an empty policy is allowed but holds nothing.After create, the policy distributes asynchronously - distributionStatus begins as “Pending”. DELETE is blocked until distribution completes (the controller’s pre-DELETE guard returns 409 PolicyDistributionPending); call PATCH with {“retryDistribution”: true} to expedite.IPPS-side - controller passes omitConfirm: true (per Phase E batch 1A finding that IPPS write cmdlets reject the universally-injected -Confirm:$false). Pass ?dryRun=true to map to -WhatIf.

Backing cmdlet: New-CaseHoldPolicy. Role: Compliance.IPPS.ReadWrite (Enterprise).

Parameter In Type Required Description
case body yes
name body yes
comment body
enabled body
exchangeLocation body
publicFolderLocation body
sharePointLocation body
dryRun query bool (switch)

Create a new case hold policy via New-CaseHoldPolicy on the IPPS pool. Required: name + case (parent compliance case Name or GUID). The parent case must exist before this call - the API does NOT expose a /complianceCases entity set, so create the case via the Purview portal or Microsoft Graph eDiscovery API first. At least one location (exchangeLocation / sharePointLocation / publicFolderLocation) is typically required for a meaningful hold. Tier 5 IPPS - backed by Compliance.IPPS.ReadWrite.All; the cmdlet ignores -Confirm so the controller omits it.

POST /exchange/v1.0/caseHoldPolicies HTTP/1.1
Authorization: Bearer {token}
Content-Type: application/json
{
"name": "Q1-2026-Litigation-Hold",
"case": "case-litigation-houston",
"comment": "Legal hold for Q1 2026 Houston litigation matter.",
"enabled": true,
"exchangeLocation": [
],
"sharePointLocation": [
"https://contoso.sharepoint.com/sites/legal-q1-2026"
]
}
HTTP/1.1 201 Created
Location: /exchange/v1.0/caseHoldPolicies/Q1-2026-Litigation-Hold
{
"@odata.context": "https://api.contoso.com/exchange/v1.0/$metadata#caseHoldPolicies/$entity",
"Id": "hold-q1-2026-litigation",
"Name": "Q1-2026-Litigation-Hold",
"Identity": "hold-q1-2026-litigation",
"Enabled": true,
"Mode": "Enforce",
"DistributionStatus": "Pending",
"CaseId": "case-litigation-houston",
"Comment": "Legal hold for Q1 2026 Houston litigation matter.",
"ExchangeLocation": [
],
"SharePointLocation": [
"https://contoso.sharepoint.com/sites/legal-q1-2026"
],
"OneDriveLocation": [],
"PublicFolderLocation": []
}

Related: GET /exchange/v1.0/caseHoldPolicies?case={caseId}, GET /exchange/v1.0/caseHoldPolicies/{key}, PATCH /exchange/v1.0/caseHoldPolicies/{key}, DELETE /exchange/v1.0/caseHoldPolicies/{key}, POST /exchange/v1.0/caseHoldRules

Get a single caseHoldPolicies entry by Identity (Name or GUID).

Backing cmdlet: Get-CaseHoldPolicy. Role: Compliance.IPPS.Read (Community).

Get a single legal-hold case policy by Identity (Name or GUID). Backed by Get-CaseHoldPolicy on the IPPS pool. Note: the list endpoint at GET /caseHoldPolicies returns 400 unless ?case=<name|guid> is supplied - the cmdlet rejects list-all and requires either Identity OR Case scope.

GET /exchange/v1.0/caseHoldPolicies/{key} HTTP/1.1
Authorization: Bearer {token}
HTTP/1.1 200 OK
{
"@odata.context": "https://api.contoso.com/exchange/v1.0/$metadata#caseHoldPolicies/$entity",
"Id": "hold-q1-2026",
"Name": "Q1 2026 Hold Policy",
"Identity": "hold-q1-2026",
"Enabled": true,
"Mode": "Enforce",
"CaseId": "case-litigation-houston",
"Comment": "Litigation hold for Q1 2026 estate matter.",
"CreatedBy": "John Woods",
"LastModifiedBy": "Ayla Kol",
"WhenCreated": "2026-01-15T09:30:00Z",
"WhenChanged": "2026-02-04T14:22:11Z"
}

Related: GET /exchange/v1.0/caseHoldPolicies?case={caseId}, GET /exchange/v1.0/caseHoldRules?policy={policyId}

PATCH /exchange/v1.0/caseHoldPolicies/{key}

Section titled “PATCH /exchange/v1.0/caseHoldPolicies/{key}”

Partial update of the caseHoldPolicies at /caseHoldPolicies/{key}. Body keys map 1:1 to Set-CaseHoldPolicy parameters. Use Add/Remove location params for delta updates rather than replacing whole lists. Returns 200 + the post-Set policy.**

Set-CaseHoldPolicy has TWO mutually-exclusive parameter sets: Identity mode (default) - delta-style addExchangeLocation / removeExchangeLocation / etc. + comment + enabled. Use this to adjust the hold scope or toggle the policy on/off.RetryDistribution mode - pass ONLY {“retryDistribution”: true} to expedite SCC distribution if the policy is stuck in distributionStatus: “Pending”. The cmdlet rejects this switch when mixed with Identity-mode params.IPPS-side - controller passes omitConfirm: true.

Backing cmdlet: Set-CaseHoldPolicy. Role: Compliance.IPPS.ReadWrite (Enterprise).

Parameter In Type Required Description
addExchangeLocation body
addPublicFolderLocation body
addSharePointLocation body
comment body
enabled body
removeExchangeLocation body
removePublicFolderLocation body
removeSharePointLocation body
dryRun query bool (switch)

Partial update of a legal-hold case policy via Set-CaseHoldPolicy. Body keys map 1:1 to Set-CaseHoldPolicy parameters (camelCased); use the Add*/Remove* location fields for delta scope changes and comment/enabled to adjust metadata or toggle the hold. Returns 200 + the post-Set policy.

PATCH /exchange/v1.0/caseHoldPolicies/{key} HTTP/1.1
Authorization: Bearer {token}
Content-Type: application/json
{
"comment": "Updated by Exonaut sample automation",
"enabled": true,
"addExchangeLocation": "[email protected]"
}
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
{
"Id": "exonaut-sample-caseHoldPolicies",
"Identity": "exonaut-sample-caseHoldPolicies",
"Name": "exonaut-sample-caseHoldPolicies",
"Comment": "Updated by Exonaut sample automation",
"Enabled": true,
"ExchangeLocation": [
],
"DistributionStatus": "Pending"
}

Notes: Doc-derived from Wiki + write-param allowlist; not live-captured. Body fields are Wiki-valid; values are illustrative. Set-CaseHoldPolicy has two mutually-exclusive parameter sets - pass ONLY {“retryDistribution”: true} to expedite a stuck distribution; that switch is rejected when mixed with the Add*/Remove*/comment/enabled fields shown here.

Related: GET /exchange/v1.0/caseHoldPolicies

DELETE /exchange/v1.0/caseHoldPolicies/{key}

Section titled “DELETE /exchange/v1.0/caseHoldPolicies/{key}”

Remove the caseHoldPolicies at /caseHoldPolicies/{key}. Pre-checks distributionStatus and returns 409 PolicyDistributionPending while the policy is still distributing to workloads (per MS SCC docs - Remove-CaseHoldPolicy fails opaquely in that state). Returns 204 on success.

State-machine guard ahead of the cmdlet (same pattern as retentionCompliancePolicies and labelPolicies): a GET-CaseHoldPolicy fetch checks distributionStatus first. While distribution is “Pending”, the SCC backend refuses Remove operations with an opaque “server side error” - this guard converts that into a clear 409 envelope (code: “PolicyDistributionPending”) BEFORE the cmdlet is invoked. MS-recommended remediation: wait until Distribution finishes (typically minutes), or call PATCH with {“retryDistribution”: true}. Removing all rules (DELETE each /caseHoldRules/{key}) is NOT a prerequisite for Remove-CaseHoldPolicy - child rules are removed cascadingly.IPPS-side - omitConfirm: true. Note: this cmdlet uses -ForceDeletion (not the usual -Force); we don’t expose it in the surface - the standard DELETE flow covers the practical case.

Backing cmdlet: Remove-CaseHoldPolicy. Role: Compliance.IPPS.ReadWrite (Enterprise).

Parameter In Type Required Description
dryRun query bool (switch)

Delete blocked while DistributionStatus is Pending

Section titled “Delete blocked while DistributionStatus is Pending”

Delete the case-hold policy at GET /caseHoldPolicies/{key}. The controller pre-checks DistributionStatus via Get-CaseHoldPolicy: if the policy is still distributing to workloads (Pending), the SCC backend refuses Remove-CaseHoldPolicy with an opaque “server side error”. The guard converts that into a clear 409 PolicyDistributionPending envelope BEFORE the cmdlet is invoked. Either wait for distribution to complete (typically minutes), or call PATCH /caseHoldPolicies/{key} with {“retryDistribution”: true} to expedite.

DELETE /exchange/v1.0/caseHoldPolicies/{key} HTTP/1.1
Authorization: Bearer {token}
HTTP/1.1 409 Conflict
{
"error": {
"code": "PolicyDistributionPending",
"message": "Cannot remove case hold policy 'Q1-2026-Litigation-Hold' while DistributionStatus is 'Pending'. SCC distributes case hold policies to Exchange / SharePoint / OneDrive asynchronously and refuses Remove operations during distribution. Wait for the policy to settle (typically a few minutes) or call PATCH /caseHoldPolicies/Q1-2026-Litigation-Hold with {\"retryDistribution\": true} to expedite, then retry the DELETE."
}
}

Related: GET /exchange/v1.0/caseHoldPolicies/{key}, PATCH /exchange/v1.0/caseHoldPolicies/{key}

?dryRun=true previews of any write stay on Community.