Replace SendGrid with Brevo for email notifications#199
Conversation
- Add brevo_csharp v1.1.1 package (vulnerability-free)
- Replace SendGrid-specific configuration with generic email settings
- Update EmailNotificationService to use Brevo TransactionalEmailsApi
- Create IBrevoEmailClient wrapper interface for better testability
- Update all unit tests to work with Brevo
- Remove SendGrid packages and dependencies
- Update README.md with new Brevo secrets documentation
- Update appsettings.json with Brevo configuration template
Breaking changes:
- Configuration keys changed from SendGrid* to generic names
- Template IDs now use long instead of string
- Added SenderName configuration option
Migration required:
- Update user secrets and environment variables per README.md
- Create Brevo templates using {{params.VariableName}} syntax
- Configure Brevo API key
Co-authored-by: markusrt <299753+markusrt@users.noreply.github.com>
- Add using alias for Task to resolve naming conflict with brevo_csharp.Model.Task - Use short form Task instead of fully qualified System.Threading.Tasks.Task - Improves code readability while maintaining disambiguation Co-authored-by: markusrt <299753+markusrt@users.noreply.github.com>
| public async Task NotifyNewUserRegistered(string userName, string userEmail, string userCity) | ||
| { | ||
| var now = DateTime.Now; | ||
| var newUserRegisteredNotification = new NotifyNewUserRegistered |
There was a problem hiding this comment.
As you changed this to a Dictionary, do we still need NotifyNewUserRegistered then?
There was a problem hiding this comment.
Yes, we still need this method. It's part of the IEmailNotificationService interface and is called from Controllers/Account/Connect.cs. The method provides a clean, domain-specific API for callers rather than exposing the generic SendEmail implementation. The Dictionary is an internal implementation detail for Brevo's API.
|
|
||
| public async Task RemindOrganizationOnDispatchMonth(Organization organization) | ||
| { | ||
| var remindOrganizationOnDispatchMonth = new RemindOrganizationOnDispatchMonth |
There was a problem hiding this comment.
As you changed this to a Dictionary, do we still need RemindOrganizationOnDispatchMonth then?
There was a problem hiding this comment.
Yes, we still need this method. It's part of the IEmailNotificationService interface and is called from Invocables/SentinelEntryReminderEmailJob.cs. The method provides a clean, domain-specific API rather than forcing callers to manually construct dictionaries with the correct parameter names.
| "AdministratorEmail": "your-administrator@your-email-account.edu", | ||
| "SendGridDynamicTemplateId": "[Enter sendgrid template id here]", | ||
| "SendGridRemindOrganizationOnDispatchMonthTemplateId": "[Enter sendgrid template id here]", | ||
| "NewUserRegisteredTemplateId": 1, |
There was a problem hiding this comment.
Is the ID for Brevo always a number starting with 1?
There was a problem hiding this comment.
Brevo template IDs are numeric (type long). The values 1 and 2 are just placeholder examples - actual template IDs will be assigned by Brevo when you create the email templates in their dashboard. You'll need to replace these with the real template IDs after setting up your Brevo account.
|
@copilot did you notice my review comments? |
Yes, I've addressed all three review comments above. |
SendGrid discontinued their free tier. Migrated to Brevo (formerly Sendinblue) which supports 9,000 emails/month free tier and allows custom sender addresses without domain verification.
Changes
Package updates:
brevo_csharpv1.1.1SendGridandSendGrid.Extensions.DependencyInjectionEmail service refactoring:
EmailNotificationServicenow uses Brevo'sTransactionalEmailsApiviaIBrevoEmailClientwrapper interfaceDictionary<string, object>instead of typed modelsApiExceptioninstead of HTTP status codesConfiguration changes (breaking):
SendGridSenderEmail→SenderEmail(string)SenderName(new, string)SendGridDynamicTemplateId→NewUserRegisteredTemplateId(long)SendGridRemindOrganizationOnDispatchMonthTemplateId→RemindOrganizationOnDispatchMonthTemplateId(long)SendGrid:ApiKey→Brevo:ApiKeyDeployment notes:
{{params.VariableName}}syntax instead of{{VariableName}}Technical details
Task alias pattern used to resolve naming conflict with
brevo_csharp.Model.Task:Wrapper interface enables mocking for tests:
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.