Manage Sophos Email Post-Delivery Quarantine through the API
Post-Delivery Quarantine contains messages that were delivered first and then removed from the mailbox by Post-Delivery Protection. Its API therefore uses the separate /post-delivery-quarantine path family. The similarly named /quarantine endpoints concern ordinary quarantine before delivery and are not interchangeable.
The safe sequence is to search narrowly, verify the hit and recipient, inspect the message or attachments, change only approved IDs, and then query the state again. Get started with the Email Management API maps the API families. Prepare OAuth2, tenant, and regional routing supplies the access values used here.
Establish prerequisites and the request base
Before the first call, Post-Delivery Protection must be enabled for the affected domain, a service principal must be configured, and its Post-Delivery Quarantine permission must be confirmed. Bring forward only these validated values from the authentication workflow:
SOPHOS_ACCESS_TOKEN: current bearer token;SOPHOS_TENANT_ID: UUID of the target tenant;SOPHOS_API_HOST: regional host discovered for that exact tenant.
EMAIL_API_HOST="${SOPHOS_API_HOST%/}/email/v1"
Every call sends the Authorization header with Bearer followed by the current SOPHOS_ACCESS_TOKEN, the X-Tenant-ID header with the SOPHOS_TENANT_ID value, and Accept: application/json; JSON requests also send Content-Type: application/json. Tenant ID, token, and regional host must belong together. Resolve a 403 by checking API permission and tenant assignment, not by trying other tenants. The examples are based on the reviewed Email Management API v1.4.0; check the current contract again before implementation.
Search narrowly for post-delivery messages
POST /post-delivery-quarantine/messages/search requires beginDate and endDate timestamps. Optional fields are page (starting at 1), pageSize (default 50, maximum 100), sort, and filter. Documented filters are id, fromContains, toContains, subjectContains, attachmentNameContains, sizeInMBGreaterThan, sizeInMBLowerThan, productType, reason, and hasAnyAttachment. productType accepts mailflow, gateway, or ems; reason accepts malware, maliciousUrl, or onDemand.
Start a controlled investigation with a short UTC window and known attributes:
POST /email/v1/post-delivery-quarantine/messages/search
{
"beginDate": "2026-09-15T08:00:00.000Z",
"endDate": "2026-09-15T09:00:00.000Z",
"page": 1,
"pageSize": 50,
"sort": ["quarantinedAt:DESC"],
"filter": {
"toContains": "user@example.com",
"reason": ["onDemand"]
}
}
Replace the sample time and address with the approved investigation scope. The response contains items and pages. Starting at page 1, increment page by exactly one. If pages.total is present, stop after that total has been reached; otherwise stop at the first empty page or one containing fewer than the requested pageSize items. Set maximum page-count and runtime limits before starting, and fail the search as incomplete if pagination is malformed, repeats or does not advance, or either guard is reached. Before a mutation, compare at least id, forRecipient, quarantinedAt, reason, subject, and expected tenant. The id is the value from the X-Sophos-Email-ID MIME header, not the MIME Message-ID or o365MessageId.
Inspect content and attachments
Two read operations are available for one discovered id:
GET /post-delivery-quarantine/messages/{id}/previewreturnsheadersand, when present,htmlBodyandtextBody.GET /post-delivery-quarantine/messages/{id}/attachments?page=1&pageSize=50returns attachment names andsizeInByteswithpages. The defaults are page 1 and 50 items; the response can support up to 500 items per page.
A preview is potentially malicious and confidential message content. Do not render HTML actively, open links, or log response data indiscriminately. The list call does not download a file.
To download, send the required attachment names to POST /post-delivery-quarantine/messages/{id}/attachments/download. Omitting attachments tells the job to download all message attachments, so preferably provide an explicit list:
{
"attachments": ["suspect-document.zip"]
}
The response contains a job id and status. This id becomes the downloadId for:
GET /email/v1/post-delivery-quarantine/downloads/{downloadId}/status
While status is processing, poll at a bounded interval. With completed, the response contains attachment names and url; password can also be present for a password-protected ZIP. With failed, inspect error rather than restarting the same request indefinitely. Open downloaded files only in an isolated analysis environment, and do not put the URL or password in logs or tickets.
Release or delete
Both write operations accept at most 50 elements in items. Each element needs the post-delivery id; forRecipients limits the action to specific recipients. If forRecipients is absent, the API guide applies the action to all recipients of that message.
{
"items": [
{
"id": "11111111-1111-4111-8111-111111111111",
"forRecipients": ["user@example.com"]
}
]
}
POST /post-delivery-quarantine/messages/releasequeues one or more messages for release. HTTP202means accepted, not confirmed in the mailbox.POST /post-delivery-quarantine/messages/deletedeletes one or more messages from Post-Delivery Quarantine. The reviewed specification returns HTTP200on success, so this deletion is not treated as a download job.
Before release, document content, recipient scope, and approval. Preserve or export the evidence required for the investigation before either write action. A successful delete has no documented API undo or restore operation; require deletion approval and escalate any uncertainty before that irreversible step. A release redelivers the message, but it does not make the original mailbox clawback repeatable. Do not retry blindly after a timeout or 5xx: the server-side effect may already have occurred.
Validate results and partial failures
Release and deletion return separate items and errors arrays. Expand the approved request into concrete (id, recipient) pairs before sending it, then reconcile every requested pair exactly once across both arrays: a successful items entry contains only id and recipient, while a failed errors entry contains id, recipient, and error. Treat the response as invalid and fail closed if a requested pair is missing, duplicated, appears in both arrays, or a returned pair was not requested. Log the outcome and returned error for each pair, without message content or tokens. Retry only failed pairs that are still valid after the state check and have been approved again; never retry successful or ambiguous pairs.
Then search the same id and affected recipient again in Post-Delivery Quarantine. After successful deletion, the item must no longer appear in the selected post-delivery scope; this validation confirms the destructive outcome, not a recovery path. After an accepted release, wait for the state change and also confirm in the destination mailbox that the message is present again for the intended recipient. That delivery confirmation does not prove that the original clawback can be run again. If the item remains in search or the message is absent from the mailbox, inspect the recipient, PDP state, and returned error before evaluating that part again.
These boundaries help troubleshooting:
- no hit: check the UTC window, page, tenant, ID type, and PDP state;
404for preview, attachments, or job status: check the regional host, post-delivery path, and applicable ID;- download
failed: inspecterrorand compare attachment names with the current list; - mixed bulk response: reconcile all requested pairs and retry only still-valid, reapproved failed pairs; fail closed on missing, duplicate, contradictory, or unexpected results;
- hit only in ordinary quarantine: continue with
/quarantine; do not transfer the message into the post-delivery family.
This keeps automation tenant-scoped, state-aware, and repeatable without blurring the boundary between ordinary and post-delivery quarantine.