Skip to content

Replace SendGrid with Brevo for email notifications#199

Draft
Copilot wants to merge 3 commits into
masterfrom
copilot/find-alternative-to-sendgrid-again
Draft

Replace SendGrid with Brevo for email notifications#199
Copilot wants to merge 3 commits into
masterfrom
copilot/find-alternative-to-sendgrid-again

Conversation

Copilot AI commented Nov 5, 2025

Copy link
Copy Markdown
Contributor

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:

  • Added brevo_csharp v1.1.1
  • Removed SendGrid and SendGrid.Extensions.DependencyInjection

Email service refactoring:

  • EmailNotificationService now uses Brevo's TransactionalEmailsApi via IBrevoEmailClient wrapper interface
  • Template data passed as Dictionary<string, object> instead of typed models
  • Error handling via ApiException instead of HTTP status codes

Configuration changes (breaking):

  • SendGridSenderEmailSenderEmail (string)
  • SenderName (new, string)
  • SendGridDynamicTemplateIdNewUserRegisteredTemplateId (long)
  • SendGridRemindOrganizationOnDispatchMonthTemplateIdRemindOrganizationOnDispatchMonthTemplateId (long)
  • SendGrid:ApiKeyBrevo:ApiKey

Deployment notes:

  • Brevo templates must use {{params.VariableName}} syntax instead of {{VariableName}}
  • Update secrets per README.md
  • Create Brevo templates matching IDs in configuration

Technical details

Task alias pattern used to resolve naming conflict with brevo_csharp.Model.Task:

using Task = System.Threading.Tasks.Task;

Wrapper interface enables mocking for tests:

public interface IBrevoEmailClient
{
    Task<CreateSmtpEmail> SendTransacEmailAsync(SendSmtpEmail sendSmtpEmail);
}
Original prompt

This section details on the original issue you should resolve

<issue_title>Find and implement alternative to Sendgrid</issue_title>
<issue_description>SendGrid decided to drop it's free service and we need an Alternative. It should support simple dynamic templates like this one

{{UserName}} ({{UserEmail}}) aus {{UserCity}} hat sich am {{Date}} um {{Time}} neu registiert. Der Account muss noch einer Organisation zugeordnet werden, um das System nutzen zu können.

And allow us to send emails as "xxx@domain.com" without proof of domain Ownership as supported by SendGrid with "Send via Sendgrid".

The volume is low as we send less then 100 emails.

Existing templates can be found as text files in folder Infrastructure</issue_description>

Comments on the Issue (you are @copilot in this section)

Custom agent used: software-engineer
Focuses on development, code quality, and unit test coverage while keeping the code clean


💡 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.

Copilot AI and others added 2 commits November 5, 2025 08:02
- 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>
Copilot AI changed the title [WIP] Find and implement alternative to SendGrid Replace SendGrid with Brevo for email notifications Nov 5, 2025
Copilot AI requested a review from markusrt November 5, 2025 08:11
public async Task NotifyNewUserRegistered(string userName, string userEmail, string userCity)
{
var now = DateTime.Now;
var newUserRegisteredNotification = new NotifyNewUserRegistered

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you changed this to a Dictionary, do we still need NotifyNewUserRegistered then?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you changed this to a Dictionary, do we still need RemindOrganizationOnDispatchMonth then?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the ID for Brevo always a number starting with 1?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@markusrt

markusrt commented Nov 5, 2025

Copy link
Copy Markdown
Owner

@copilot did you notice my review comments?

Copilot AI commented Nov 5, 2025

Copy link
Copy Markdown
Contributor Author

@copilot did you notice my review comments?

Yes, I've addressed all three review comments above.

Copilot AI requested a review from markusrt November 5, 2025 08:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Find and implement alternative to Sendgrid

2 participants