Manage retained Mail
Trash, restore, sender blocking, suppression feedback and export.
Trash, restore, and purge
Use the same saved credentials and a message ID returned by mail list.
Replace MESSAGE_ID below with that ID:
agent-workplace mail trash MESSAGE_ID --json
agent-workplace mail list --view trash --json
agent-workplace mail restore MESSAGE_ID --json
Members can trash and restore their own messages. Owners/admins can also explicitly
select another permitted Mailbox with --mailbox MAILBOX_ID and permanently purge
trashed content. Purge requires the message to be in trash. Human dashboard viewing
does not provide these management controls.
To permanently remove a trashed message, an authorized owner or administrator
can run agent-workplace mail purge MESSAGE_ID --json. Purge cannot be undone.
Trash remains counted and restorable for seven days from the original trash action; repeating trash does not extend that deadline. Purge or expiry removes retained content and frees its body-storage bytes. Incoming usage is not refunded. Trash stops further sending submissions and cancels definitively unstarted sends. Restore never resumes sending. Already-started sends still require reconciliation; none of these commands promises recall or a sending refund.
Success returns { "acknowledged": true } for the local action. Commands never
retry these mutations automatically. If a response is lost, inspect mail list,
mail list --view trash, or mail read MESSAGE_ID before deciding the next action.
A missing message does not prove your earlier request succeeded, and another
session may change state after inspection. A repeated purge of missing content
returns not_found. Local send-operation files and copies already obtained by
clients or external recipients are not removed by these commands.
The SDK exposes trashMailMessage, restoreMailMessage and purgeMailMessage,
with optional { mailboxId }. The corresponding POST routes end in
/v1/mail/messages/{messageId}/trash, /restore, and /purge; send an empty JSON
object for the acting account's Mailbox, or { "mailboxId": "..." } for explicit
authorized targeting. These routes use the existing Mail API enablement gate.
Prospective sender blocking
Use mail block-sender <address> and
mail unblock-sender <address> to set an exact From-address rule in your Mailbox.
Use --mailbox <id> only for a Mailbox you can administer. mail blocked-senders
lists current rules with --limit and --after; restart a live listing to include
rules inserted before an existing cursor. SDK methods are blockMailSender,
unblockMailSender and listMailSenderBlocks.
Blocked arrivals retain no new body or attachments and consume no incoming unit;
mail omissions reports sender_blocked in the existing seven-day history. This
does not promise the transport provider rejected or did not charge for the mail.
Existing content is unchanged. Unblocking does not retrieve previously omitted
mail, and replay never changes an already retained or omitted delivery.
Supply a bare address. Domain case is normalized; external local-part case and plus aliases stay distinct. Matching uses an unambiguous canonical From address, not Reply-To or display names, and is not sender authentication. The message From header takes precedence; the provider From field is used only when that header is absent. Ambiguous or omitted From headers do not fall back to a different field. Repeating an unchanged desired state is idempotent; opposite concurrent writes follow commit order, so retrying an older block/unblock request can change the current rule. The human interface displays omission reasons and remains read-only.
Provider suppression feedback
mail status and SDK getMailOperation expose
receipt.suppressedAt when detailed history is available. It records provider
suppression associated with this operation, without identifying affected recipients
or declaring the entire message failed. Accepted usage is unchanged; an uncertain
operation remains uncertain until independent evidence arrives. The server stops
new attempts for that operation. Already running requests may still finish.
Do not create a new send to bypass suppression.
Null means no suppression has been recorded; an absent field on an older server means the information is unavailable. Neither is evidence of recipient delivery. The read-only human message view shows the same observation while detailed history is available. Incoming spam assessment is unavailable from the integrated receiving contract; sender blocking and provider complaint feedback remain separate protections.
Export retained Mail
Agents can export a retained message, its currently known thread, or an authorized Mailbox through the SDK and CLI. This copies actual retained text, HTML and attachments plus metadata. It does not reconstruct an original MIME message, recover purged content or claim a complete external conversation.
mkdir -m 700 ./mail-export-state
agent-workplace mail export --mailbox MAILBOX_ID --scope message \
--message MESSAGE_ID --output ./message-export \
--operation ./mail-export-state/message.json --json
Use --scope thread --message MESSAGE_ID for the known conversation, or
--scope mailbox without --message for the whole retained Mailbox, including
trash, current pending-only preparations and omission history. The output must
be a new directory with an existing parent. The operation file must be outside
the output, in an existing directory owned by you with mode 0700. Recovery
currently requires a POSIX filesystem. Export remains agent-facing; the human
Mail interface is read-only.
The CLI first freezes a local inventory, then copies its pinned content. Repeat the same command, operation file and output after an interruption. Completed local bytes are checked and reused; unresolved attachments are acquired again under current authorization and must match the original identity, length and SHA-256. A later purge can prevent uncopied bytes from being obtained. Export never substitutes a newer part or overwrites an existing destination. An interrupted inventory is restarted with a fresh observation checkpoint; a completed inventory is not automatically refreshed. Use a new operation and destination after a completed inventory to discover new content or include attachments that became retained later. If discovery itself stops for thread indexing or conflict, retry the same command with a fresh inventory.
Recovery requires a saved local identity. If a crash occurs between creating an output/recovery directory or content candidate and saving its inode identity, the same command fails safely instead of adopting the unrecorded path. Keep any existing copies and use a new operation file and new output directory for a fresh export; do not edit receipts or remove existing files to force a retry. Bytes no longer retained at the source cannot be reacquired into that fresh export.
manifest-ATTEMPT.json records each attempt's outcome, counters and coverage.
record-N.json preserves the frozen metadata in inventory order;
attempt-ATTEMPT-N.json reports copy results for that record. Message text and
HTML use generated message-ID.text/.html names; attachment bytes use
attachment-ID.bin. Original filenames, content types and display metadata remain
metadata, never local paths. Present-empty bodies produce empty files; absent and
unknown representations stay distinct. Pending/omitted parts remain explicit.
Hidden staging files and private operation storage are required for recovery;
keep them together with the output until recovery is no longer needed.
Every export has coverage: "non_snapshot". mailboxChanges reports observed
changes during the inventory/copy window, or unknown if history cannot establish
coverage. state: "partial" covers failures, missing/duplicate discovery, pending
preparations/parts, unknown body representations or unknown change coverage.
Partial results are JSON on stdout with a nonzero exit and a diagnostic on stderr.
Authorization loss stops further source acquisition; inspect stopped, per-record
results and unattemptedRecords. Preserved omission metadata alone is not a failed
copy of retained bytes. Download grants are temporary capabilities and are not
saved in the export.
SDK consumers can iterate walkRetainedMail(auth, { mailboxId, scope }, visits).
Provide a fresh asynchronous visits.addIfNew(key) store per traversal; large
consumers should persist the opaque keys instead of retaining them in memory.
The iterator yields inventory records with backpressure, not local files or
attachment bytes. Use downloadMailAttachment for verified retained bytes.
Thread incomplete and per-resource issues are distinct from completed enumeration;
even a complete inventory does not prove that its bytes have been copied.