Save Attachments
Saves email attachments from Microsoft Exchange Server to local disk. This node downloads attachments from a specific email retrieved via the Get Mail node and stores them in a specified directory.
Common Properties
- Name - The custom name of the node.
- Color - The custom color of the node.
- Delay Before (sec) - Waits in seconds before executing the node.
- Delay After (sec) - Waits in seconds after executing node.
- Continue On Error - Automation will continue regardless of any error. The default value is false.
If ContinueOnError property is true, no error is caught when the project is executed even if Catch node is used.
Input
- Exchange Web Service Url - The EWS endpoint URL (e.g.,
https://mail.company.com/EWS/Exchange.asmxorhttps://outlook.office365.com/EWS/Exchange.asmx). Required. - Mail Object - Mail object from Get Mail node containing email ID and attachments metadata. Required.
- Save Path - Directory path where attachments will be saved (e.g.,
C:\\Downloads\\Attachments). The directory will be created if it doesn't exist. Required.
Output
- Result - List of file paths (string array) where attachments were saved. Each path is an absolute path to the saved file.
Options
- Exchange Version - Exchange Server version for compatibility:
- Exchange2007_SP1 (default)
- Exchange2010
- Exchange2010_SP1
- Exchange2010_SP2
- Exchange2013
- Exchange2013_SP1
- Credentials - Exchange Server login credentials (username/password) from vault. Required.
How It Works
The Save Attachments node:
- Validates the mail object, save path, and credentials
- Creates the save directory if it doesn't exist
- Connects to Exchange Server using provided credentials and EWS URL
- Binds to the specific email using the email ID from the mail object
- Iterates through all attachments in the email
- Downloads each attachment's content from Exchange Server
- Handles filename conflicts by appending numbers (e.g.,
file_1.pdf,file_2.pdf) - Saves each attachment to the specified directory
- Returns a list of all saved file paths
- Implements automatic retry logic for transient errors (3 attempts with exponential backoff)
The node uses a 100-second timeout for Exchange operations. Files with duplicate names are automatically renamed with numeric suffixes to avoid overwriting existing files. The node handles large attachments efficiently and provides robust error handling for network issues and server throttling.
Examples
Save All Attachments from Email
Download all attachments from retrieved emails:
// 1. Get emails with Get Mail node
exchange_url = "https://mail.company.com/EWS/Exchange.asmx"
mail_folder = "Inbox"
number_of_messages = 10
// Get Mail node
// Output: emails
emails = message.Result
// 2. Process each email
for (email of emails) {
// Check if email has attachments
if (email.Attachments && email.Attachments.length > 0) {
console.log(`Saving ${email.Attachments.length} attachments from: ${email.Subject}`)
// Save Attachments node
// Mail Object: email
// Save Path: "C:\\Downloads\\EmailAttachments"
saved_files = message.Result
console.log(`Saved files: ${saved_files.join(", ")}`)
}
}
Organize Attachments by Sender
Save attachments in folders organized by sender email:
// Get Mail node
emails = message.Result
base_path = "C:\\Downloads\\AttachmentsBySender"
for (email of emails) {
if (email.Attachments && email.Attachments.length > 0) {
// Create sender-specific folder
sender_folder = email.From.replace("@", "_at_").replace(".", "_")
save_path = `${base_path}\\${sender_folder}`
// Save Attachments node
// Exchange Web Service Url: exchange_url
// Mail Object: email
// Save Path: save_path
saved_files = message.Result
console.log(`Saved ${saved_files.length} files from ${email.From}`)
}
}
Process Invoice Attachments
Extract and save invoice PDFs from emails:
// Get emails with "Invoice" in subject
// Get Mail node with specific folder
exchange_url = "https://mail.company.com/EWS/Exchange.asmx"
mail_folder = "Invoices"
emails = message.Result
invoice_path = "C:\\Invoices\\Pending"
for (email of emails) {
// Check for PDF attachments
has_pdf = email.Attachments.some(att => att.Name.toLowerCase().endsWith(".pdf"))
if (has_pdf) {
// Save Attachments node
// Mail Object: email
// Save Path: invoice_path
saved_files = message.Result
// Process each saved invoice
for (file_path of saved_files) {
if (file_path.toLowerCase().endsWith(".pdf")) {
console.log(`Processing invoice: ${file_path}`)
// Extract data from PDF, update database, etc.
}
}
}
}
Save Attachments with Date Organization
Organize attachments by date received:
// Get Mail node
emails = message.Result
base_path = "C:\\Downloads\\AttachmentsByDate"
for (email of emails) {
if (email.Attachments && email.Attachments.length > 0) {
// Create date-based folder structure
email_date = new Date(email.DateTime)
year = email_date.getFullYear()
month = String(email_date.getMonth() + 1).padStart(2, '0')
day = String(email_date.getDate()).padStart(2, '0')
save_path = `${base_path}\\${year}\\${month}\\${day}`
// Save Attachments node
// Mail Object: email
// Save Path: save_path
saved_files = message.Result
console.log(`Saved to: ${save_path}`)
console.log(`Files: ${saved_files.length}`)
}
}
Filter and Save Specific File Types
Save only specific attachment types (e.g., Excel files):
// Get Mail node
emails = message.Result
excel_path = "C:\\Downloads\\ExcelFiles"
for (email of emails) {
// Check for Excel attachments
has_excel = email.Attachments.some(att => {
const name = att.Name.toLowerCase()
return name.endsWith(".xlsx") || name.endsWith(".xls")
})
if (has_excel) {
// Save all attachments (will filter after saving)
// Save Attachments node
// Mail Object: email
// Save Path: excel_path
saved_files = message.Result
// Log Excel files
for (file_path of saved_files) {
const ext = file_path.toLowerCase().slice(-5)
if (ext.includes(".xls")) {
console.log(`Excel file saved: ${file_path}`)
// Process Excel file
} else {
// Optionally delete non-Excel files
console.log(`Skipping non-Excel file: ${file_path}`)
}
}
}
}
Save and Archive Attachments
Save attachments and track them in a database:
// Get Mail node
emails = message.Result
archive_path = "C:\\Archive\\EmailAttachments"
attachment_log = []
for (email of emails) {
if (email.Attachments && email.Attachments.length > 0) {
// Save Attachments node
// Mail Object: email
// Save Path: archive_path
saved_files = message.Result
// Create archive record
for (i = 0; i < saved_files.length; i++) {
attachment_record = {
file_path: saved_files[i],
original_name: email.Attachments[i].Name,
email_subject: email.Subject,
email_from: email.From,
email_date: email.DateTime,
saved_date: new Date().toISOString()
}
attachment_log.push(attachment_record)
}
}
}
// Save log to database or file
console.log(`Total attachments archived: ${attachment_log.length}`)
Handle Large Attachments
Process emails with large attachments carefully:
// Get Mail node (limit messages for large attachments)
exchange_url = "https://mail.company.com/EWS/Exchange.asmx"
mail_folder = "Inbox"
number_of_messages = 5 // Smaller batch for large files
emails = message.Result
large_files_path = "C:\\Downloads\\LargeFiles"
for (email of emails) {
if (email.Attachments && email.Attachments.length > 0) {
try {
console.log(`Processing ${email.Attachments.length} attachments`)
// Save Attachments node
// Mail Object: email
// Save Path: large_files_path
saved_files = message.Result
console.log(`Successfully saved ${saved_files.length} files`)
// Add delay to avoid throttling
await delay(3000)
} catch (error) {
console.error(`Failed to save attachments from email: ${email.Subject}`)
console.error(`Error: ${error.message}`)
// Continue to next email
}
}
}
Extract Specific Attachment by Name
Find and save a specific attachment by name pattern:
// Get Mail node
emails = message.Result
target_attachment = "report" // Looking for files containing "report"
save_path = "C:\\Reports\\Downloaded"
for (email of emails) {
// Check if email has target attachment
has_target = email.Attachments.some(att =>
att.Name.toLowerCase().includes(target_attachment.toLowerCase())
)
if (has_target) {
// Save Attachments node
// Mail Object: email
// Save Path: save_path
saved_files = message.Result
// Find the specific file
for (file_path of saved_files) {
const filename = require('path').basename(file_path)
if (filename.toLowerCase().includes(target_attachment.toLowerCase())) {
console.log(`Found target attachment: ${file_path}`)
// Process the specific file
}
}
}
}
Tips for Effective Use
- Mail object source: Always use mail objects from Get Mail node
- Directory creation: Save path directory is created automatically if it doesn't exist
- File naming: Duplicate filenames are handled automatically with numeric suffixes
- Check attachments: Verify email has attachments before calling Save Attachments
- Error handling: Use Try-Catch for robust automation
- Retry logic: Node automatically retries on transient errors (3 attempts)
- Large files: Add delays when processing emails with large attachments
- Permissions: Ensure write permissions for the save path directory
- Path format: Use absolute paths with proper directory separators
- Batch processing: Process emails in batches to avoid memory issues
- File validation: Check saved files exist and have expected size
Common Errors and Solutions
"ErrInvalidArg: Exchange Web Service Url cannot be empty"
Cause: No EWS URL was provided.
Solution: Provide the correct Exchange Web Services URL:
exchange_url = "https://mail.company.com/EWS/Exchange.asmx"
"ErrInvalidArg: Mail object cannot be null"
Cause: No mail object was provided to the node.
Solution: Ensure you're passing a mail object from Get Mail node:
// Get Mail node first
emails = message.Result
// Then use mail object in Save Attachments
for (email of emails) {
// Save Attachments node
// Mail Object: email // Must be a mail object
}
"ErrInvalidArg: Save path cannot be empty"
Cause: No save path was specified.
Solution: Provide a valid directory path:
save_path = "C:\\Downloads\\Attachments"
// Or use dynamic path
save_path = `C:\\Downloads\\${new Date().toISOString().split('T')[0]}`
"ErrInvalidArg: Mail object has no ID"
Cause: The mail object doesn't contain an email ID.
Solution: Verify the mail object is from Get Mail node and contains an ID:
// Get Mail node
emails = message.Result
for (email of emails) {
if (email.Id) {
// Save Attachments node
// Mail Object: email
} else {
console.error("Email has no ID")
}
}
"ErrNoAttachments: Email has no attachments to save"
Cause: The email doesn't contain any attachments.
Solution: Check for attachments before saving:
for (email of emails) {
// Check if email has attachments
if (email.Attachments && email.Attachments.length > 0) {
// Save Attachments node
// Mail Object: email
} else {
console.log("Email has no attachments, skipping")
}
}
"ErrFileSystem: Permission denied creating directory"
Cause: Insufficient permissions to create the save directory.
Solution:
- Use a directory where you have write permissions
- Run Robomotion with appropriate permissions
- Check if path is valid and accessible
// Use a path with write permissions
save_path = "C:\\Users\\YourUsername\\Documents\\Attachments"
"ErrFileSystem: Permission denied writing file"
Cause: No write permissions for the save path.
Solution:
- Verify write permissions for the directory
- Check if the file is locked by another process
- Ensure disk space is available
"ErrTransientData: Failed to bind email after 3 attempts"
Cause: Exchange server throttling, connection limits, or temporary network issues.
Solution:
- Wait a few moments and retry
- Check network connectivity to Exchange Server
- Add delays when processing multiple emails
- Reduce batch size for large attachments
// Add delay between processing emails
for (email of emails) {
if (email.Attachments && email.Attachments.length > 0) {
// Save Attachments node
saved_files = message.Result
// Wait before next email
await delay(2000) // 2 second delay
}
}
"ErrAttachmentLoad: Failed to load attachment after 3 attempts"
Cause: Exchange server issues, network problems, or attachment is corrupted.
Solution:
- Retry the operation after waiting
- Check Exchange Server status
- Verify the attachment is not corrupted
- Check if attachment size exceeds limits
"ErrConnection: Network error"
Cause: Network connectivity issues to Exchange Server.
Solution:
- Verify the EWS URL is accessible
- Check firewall settings
- Ensure DNS resolution works
- Test connectivity using a browser
Best Practices
- Validate mail objects: Always verify mail object has ID and attachments before saving
- Check attachments: Filter emails with attachments to avoid unnecessary operations
- Error handling: Implement Try-Catch blocks for robust automation
- Directory organization: Use logical folder structures (by date, sender, type)
- File naming: Let the node handle duplicate names automatically
- Batch processing: Process emails in manageable batches
- Delay between operations: Add delays when processing many emails
- Disk space: Monitor available disk space for large attachments
- Logging: Track saved files for audit and troubleshooting
- File validation: Verify saved files after download
Attachment Processing Workflow
// 1. Retrieve emails with Get Mail
exchange_url = "https://mail.company.com/EWS/Exchange.asmx"
mail_folder = "Inbox"
number_of_messages = 20
// Get Mail node
// Only Unread Mails: true
emails = message.Result
// 2. Filter emails with attachments
emails_with_attachments = emails.filter(email =>
email.Attachments && email.Attachments.length > 0
)
console.log(`Found ${emails_with_attachments.length} emails with attachments`)
// 3. Process each email
base_save_path = "C:\\Downloads\\ProcessedAttachments"
for (email of emails_with_attachments) {
try {
// Log attachment info
console.log(`Processing email: ${email.Subject}`)
console.log(`Attachments: ${email.Attachments.map(a => a.Name).join(", ")}`)
// Organize by date
email_date = new Date(email.DateTime)
date_folder = email_date.toISOString().split('T')[0] // YYYY-MM-DD
save_path = `${base_save_path}\\${date_folder}`
// Save Attachments node
// Exchange Web Service Url: exchange_url
// Mail Object: email
// Save Path: save_path
saved_files = message.Result
console.log(`Saved ${saved_files.length} files to: ${save_path}`)
// 4. Process saved files
for (file_path of saved_files) {
const filename = require('path').basename(file_path)
const ext = require('path').extname(file_path).toLowerCase()
// Process based on file type
if (ext === ".pdf") {
// Process PDF
console.log(`Processing PDF: ${filename}`)
} else if (ext === ".xlsx" || ext === ".xls") {
// Process Excel
console.log(`Processing Excel: ${filename}`)
} else {
console.log(`File saved: ${filename}`)
}
}
// Add delay to avoid throttling
await delay(2000)
} catch (error) {
console.error(`Error processing email: ${error.message}`)
// Log error but continue processing
}
}
console.log("Attachment processing complete")