bulk_delete
Delete multiple iCloud emails at once using filters like sender, date, or subject. Preview deletions with dry run mode for safe bulk email management.
Instructions
Delete emails matching any combination of filters. Processes in chunks of 250 with per-chunk timeouts for reliability. Use dryRun: true to preview without making changes.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| sourceMailbox | No | Mailbox to delete from (default INBOX) | |
| dryRun | No | If true, preview what would be deleted without actually deleting | |
| sender | No | Match exact sender email address | |
| domain | No | Match any sender from this domain (e.g. substack.com) | |
| subject | No | Keyword to match in subject | |
| before | No | Only emails before this date (YYYY-MM-DD) | |
| since | No | Only emails since this date (YYYY-MM-DD) | |
| unread | No | True for unread only, false for read only | |
| flagged | No | True for flagged only, false for unflagged only | |
| larger | No | Only emails larger than this size in KB | |
| smaller | No | Only emails smaller than this size in KB | |
| hasAttachment | No | Only emails with attachments (client-side BODYSTRUCTURE scan — must be combined with other filters that narrow results to under 500 emails first) | |
| account | No | Account name to use (e.g. 'icloud', 'gmail'). Defaults to first configured account. Use list_accounts to see available accounts. |
Implementation Reference
- lib/imap.js:1749-1791 (handler)The `bulk_delete` function performs a chunked deletion of emails based on provided filters, utilizing a rate-limited IMAP client and per-chunk timeouts for robustness.
export async function bulkDelete(filters, sourceMailbox = 'INBOX', dryRun = false, creds = null) { const client = createRateLimitedClient(creds); await client.connect(); await client.mailboxOpen(sourceMailbox); const query = buildQuery(filters); let uids = (await client.search(query, { uid: true })) ?? []; if (filters.hasAttachment) { if (uids.length > ATTACHMENT_SCAN_LIMIT) { await client.logout(); return { error: `hasAttachment requires narrower filters first — ${uids.length} candidates exceeds scan limit of ${ATTACHMENT_SCAN_LIMIT}.` }; } uids = await filterUidsByAttachment(client, uids); } if (dryRun) { await client.logout(); return { dryRun: true, wouldDelete: uids.length, sourceMailbox, filters }; } if (uids.length === 0) { await client.logout(); return { deleted: 0, sourceMailbox }; } let deleted = 0; for (let i = 0; i < uids.length; i += CHUNK_SIZE) { const chunk = uids.slice(i, i + CHUNK_SIZE); const chunkIndex = Math.floor(i / CHUNK_SIZE); try { await withTimeout(`bulk_delete chunk ${chunkIndex}`, TIMEOUT.BULK_OP, async () => { await client.messageDelete(chunk, { uid: true }); }); deleted += chunk.length; } catch (err) { await safeClose(client); return { deleted, failed: uids.length - deleted, sourceMailbox, filters, error: `Chunk ${chunkIndex} failed: ${err.message}. ${deleted} deleted so far, ${uids.length - deleted} remaining.` }; } } await client.logout(); return { deleted, sourceMailbox, filters }; }