A robust, type-safe .NET 8.0+ client library for the Microsoft Graph Mail API that streamlines email operations with an intuitive, async-first design.
MSGraph.Mail.Client provides a comprehensive abstraction layer over the Microsoft Graph Mail API, eliminating boilerplate code and reducing complexity. Whether you're building enterprise email solutions, automation workflows, or integrations, this library offers production-ready functionality with a focus on reliability and developer experience.
Key Capabilities:
- 📧 Mailbox Operations - Read, search, and manage emails with advanced filtering
- 🔐 Flexible Authentication - Support for both interactive and daemon authentication flows
- 📎 Attachment Handling - Seamless attachment retrieval and sending
- 🔄 Efficient Pagination - Built-in support for large email collections
- ⚡ Fully Asynchronous - Non-blocking I/O with Task-based API
- 🎯 Type-Safe Models - Strongly-typed C# models with IntelliSense support
- 🛡️ Production-Ready - Comprehensive error handling and security best practices
- .NET: 8.0 or higher
- Azure: Active Azure AD/Entra ID tenant
- Permissions: Microsoft Graph API access with mail scopes
Install-Package MSGraph.Mail.Clientdotnet add package MSGraph.Mail.ClientDownload directly from NuGet.org
Register your application in Microsoft Entra Admin Center:
- Navigate to App registrations → New registration
- Enter an application name and select the appropriate account type
- Configure the Redirect URI:
- Interactive apps:
http://localhost - Daemon apps: Leave blank
- Interactive apps:
- Copy the Application (client) ID and Directory (tenant) ID
- Enable Allow public client flows under Authentication → Advanced settings
Add the following permissions in API permissions:
openid,profile,offline_access(for authentication)user.read(user profile access)mail.readbasic,mail.read(email retrieval)mail.send(sending emails)
Ideal for desktop applications and user-driven scenarios:
var settings = new Settings
{
ClientId = "your-client-id",
TenantId = "your-tenant-id",
GraphUserScopes = new[]
{
"openid",
"profile",
"offline_access",
"user.read",
"mail.read",
"mail.send"
}
};
IAuthenticationProvider authProvider = new AuthenticationInteractiveProvider(settings);
IEmailGraphService emailService = new EmailGraphService(authProvider);Best for background services and scheduled jobs:
var settings = new Settings
{
ClientId = "your-client-id",
TenantId = "your-tenant-id",
SecretId = "your-client-secret" // Store securely!
};
IAuthenticationProvider authProvider = new AuthenticationClientSecretProvider(settings);
IEmailGraphService emailService = new EmailGraphService(authProvider);// Retrieve unread emails with attachments
var emails = await emailService.GetEmailsAsync(
top: 20, // Items per page
limit: 100, // Total items to return (-1 for all)
requestInformation: new EmailRequestParameterInformation
{
IsRead = false,
IncludeAttachments = true,
EmailOrderby = new[]
{
new EmailOrderby
{
OrderbyField = EmailOrderbyField.receivedDateTime,
OrderbyType = EmailOrderbyType.Desc
}
}
}
);
foreach (var email in emails)
{
Console.WriteLine($"From: {email.From}");
Console.WriteLine($"Subject: {email.Subject}");
Console.WriteLine($"Received: {email.ReceivedDateTime}");
}// Search emails by keyword
var results = await emailService.GetEmailsAsync(
top: 25,
limit: 50,
requestInformation: new EmailRequestParameterInformation
{
Search = "project status",
IsRead = false
}
);
// Custom OData filter
var filtered = await emailService.GetEmailsAsync(
requestInformation: new EmailRequestParameterInformation
{
Filter = "from/emailAddress/address eq 'manager@company.com'"
}
);// Retrieve attachments from email
var attachments = await emailService.GetEmailAttachments(messageId);
foreach (var attachment in attachments)
{
Console.WriteLine($"Attachment: {attachment.Name}");
Console.WriteLine($"Size: {attachment.Size} bytes");
Console.WriteLine($"Type: {attachment.ContentType}");
// Save to disk
File.WriteAllBytes($"downloads/{attachment.Name}", attachment.ContentBytes);
}// Send email with HTML content and attachments
var emailMessage = new EmailMessage
{
ToRecipients = new[] { "recipient@example.com" },
CcRecipients = new[] { "cc@example.com" },
Subject = "Project Update",
BodyType = EmailBodyType.Html,
BodyContent = @"
<h2>Q3 Status Report</h2>
<p>Please find the attached project update.</p>
<p>Best regards,<br/>Project Team</p>
",
HasAttachments = true,
FileAttachments = new[]
{
new EmailFileAttachment
{
Name = "Q3_Report.pdf",
ContentType = "application/pdf",
ContentBytes = File.ReadAllBytes("reports/Q3_Report.pdf")
}
}
};
await emailService.SendEmail(emailMessage);// Process large batches efficiently
const int batchSize = 50;
int processed = 0;
var emails = await emailService.GetEmailsAsync(
top: batchSize,
limit: -1, // Get all emails
requestInformation: new EmailRequestParameterInformation
{
IsRead = false
}
);
foreach (var email in emails)
{
// Process each email
await ProcessEmail(email);
processed++;
if (processed % 100 == 0)
Console.WriteLine($"Processed {processed} emails...");
}// Find and categorize emails by sender
var importantDomains = new[] { "@executive.company.com", "@board.company.com" };
var allEmails = await emailService.GetEmailsAsync(
top: 50,
limit: -1
);
var importantEmails = allEmails
.Where(e => importantDomains.Any(d => e.From?.EndsWith(d) == true))
.OrderByDescending(e => e.ReceivedDateTime)
.ToList();
Console.WriteLine($"Found {importantEmails.Count} important emails");// Mark emails as read in batches
var unreadEmails = await emailService.GetEmailsAsync(
top: 50,
limit: 500,
requestInformation: new EmailRequestParameterInformation
{
IsRead = false
},
markRead: true // Automatically mark as read
);
Console.WriteLine($"Archived {unreadEmails.Count} emails");🎯 Major Updates:
- Framework: Upgraded to .NET 8.0 LTS (from .NET 6.0)
- Documentation: Complete XML documentation for IntelliSense support
- Security: All dependencies updated to latest secure versions
- Azure.Identity: 1.13.1
- Microsoft.Identity.Client: 4.66.1
- Microsoft.Graph: 5.60.0
✨ Quality Improvements:
- Zero compiler warnings with strict null-safety checks
- Refactored orderby logic for improved maintainability
- Enhanced exception handling with better error propagation
- Comprehensive inline documentation for all public APIs
- README and LICENSE included in NuGet package
🔧 Technical Enhancements:
- Improved null reference handling
- Cleaner code patterns with reduced duplication
- Better validation of input parameters
- Production-ready error scenarios
Represents a complete email message with headers and body content:
public class EmailMessage
{
public string? Id { get; set; }
public string? Subject { get; set; }
public string? From { get; set; }
public IList<string?>? ToRecipients { get; set; }
public IList<string?>? CcRecipients { get; set; }
public IList<string?>? BccRecipients { get; set; }
public string? BodyContent { get; set; }
public EmailBodyType? BodyType { get; set; } // Text or Html
public bool? HasAttachments { get; set; }
public IList<EmailFileAttachment>? FileAttachments { get; set; }
public DateTimeOffset? ReceivedDateTime { get; set; }
public DateTimeOffset? CreatedDateTime { get; set; }
public DateTimeOffset? LastModifiedDateTime { get; set; }
}Represents a file attachment within an email:
public class EmailFileAttachment
{
public string? Id { get; set; }
public string? Name { get; set; }
public string? ContentType { get; set; } // MIME type
public int? Size { get; set; } // Bytes
public bool? IsInline { get; set; } // Embedded vs attached
public byte[]? ContentBytes { get; set; } // Binary content
}Advanced query parameters for email retrieval:
public class EmailRequestParameterInformation
{
public bool? IsRead { get; set; } // Filter by read status
public string? Search { get; set; } // Full-text search
public string? Filter { get; set; } // OData filter expression
public bool? IncludeAttachments { get; set; }
public IList<EmailOrderby>? EmailOrderby { get; set; }
}| Issue | Solution |
|---|---|
| "Invalid client ID" | Verify Application ID in Azure Portal matches your settings |
| "Tenant not found" | Ensure Directory ID is correct (use "consumer" for personal accounts) |
| "Insufficient privileges" | Check API permissions in Azure Portal; ensure all required scopes are granted and admin consent is provided |
| "AADSTS50105: User hasn't consented" | Request admin consent for the application in Azure Portal |
| Issue | Solution |
|---|---|
| No emails returned | Verify authenticated user has mailbox access; check IsRead filter |
| Attachment size too large | Graph API limits attachments to 25MB; consider chunked uploads |
| SendEmail timeout | Large attachments require longer timeout periods; increase timeout value |
| "Mail.send permission missing" | Add mail.send scope to application permissions and request consent |
- Batch sizes: Use
topparameter of 50-100 for optimal performance - Pagination: Use
limit: -1carefully; consider implementing incremental loading - Rate limiting: Graph API enforces rate limits; implement exponential backoff for retries
✅ Do:
- Store credentials securely (use Azure Key Vault for production)
- Implement proper error handling and retry logic
- Use appropriate batch sizes for large operations
- Dispose of
IEmailGraphServiceandIAuthenticationProviderafter use - Cache user profiles to minimize API calls
❌ Don't:
- Hardcode credentials in source code
- Ignore pagination limits on large datasets
- Make synchronous wrappers around async methods
- Share authentication provider instances across threads without synchronization
- Store sensitive data in logs
Contributions are welcome! Whether it's bug reports, feature requests, or code contributions:
- Report Issues: Use GitHub Issues with detailed reproduction steps
- Submit PRs: Follow existing code style and include tests
- Documentation: Help improve examples and guides
- Feedback: Share your use cases and improvement suggestions
See CONTRIBUTING.md for detailed guidelines.
For complete working examples, refer to the EmailDemoApp project.
Licensed under the Apache License 2.0 - see LICENSE file for details.
Permissions: commercial use, modification, distribution, private use Conditions: license and copyright notice Limitations: liability, warranty
- Documentation: Microsoft Graph API Docs
- Mail API Reference: Message Resource
- Authentication: Azure Identity Library
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Made with ❤️ for the .NET community