SendGrid
SendGrid is RoundTrip's email delivery provider. It handles all outbound transactional email — user invitations, welcome messages, and invoice delivery. SendGrid was chosen for its reliable delivery infrastructure, detailed activity logs, and straightforward API.
What SendGrid Sends
| Trigger | Recipient | Notes | |
|---|---|---|---|
| Welcome to RoundTrip | New tenant user invited | Invited user's email | Sent via SendInviteEmailJob after Entra user creation |
| Invoice | Dispatcher clicks "Send Invoice" | Client email on file | Sent via SendInvoiceEmailJob with PDF attachment |
:::info Entra Invitation Email is Suppressed
The Microsoft Graph invitation flow (InviteUserAsync) has SendInvitationMessage set to false. Entra does not send its own invitation email. SendGrid handles the welcome email instead, giving full control over the email design and content.
:::
Configuration
Key Vault Secret
| Secret Name | Config Key | Purpose |
|---|---|---|
SendGrid--ApiKey | SendGrid:ApiKey | API key for all outbound email |
App Service Setting
| Setting Name | Value |
|---|---|
SendGrid__ApiKey | @Microsoft.KeyVault(VaultName=kv-roundtrip-production;SecretName=SendGrid--ApiKey) |
Code Reference
// SendGridEmailService constructor — throws if key missing
var apiKey = configuration["SendGrid:ApiKey"]
?? throw new InvalidOperationException("SendGrid:ApiKey is not configured.");
Architecture
SendGrid is called exclusively from Hangfire background jobs — never inline in a request handler. This keeps API response times fast and provides automatic retry if delivery fails.
Request Handler
│
│ Hangfire.Enqueue<SendInvoiceEmailJob>(job => job.ExecuteAsync(invoiceId, email, token))
│
▼
Hangfire Worker (background)
│
│ SendInvoiceEmailJob.ExecuteAsync()
│ 1. Load invoice from database
│ 2. Fetch PDF from Azure Blob Storage (if available)
│ 3. Call SendGridEmailService.SendInvoiceEmailAsync()
│
▼
SendGridEmailService
│ Builds SendGrid message (to, from, subject, HTML body, PDF attachment)
│ Calls SendGrid API
│ Logs result
▼
SendGrid → Recipient inbox
PDF Attachment Behaviour
If the invoice PDF has not yet been generated and stored in Azure Blob Storage at the time the email job runs, the email is sent without the PDF attachment and a warning is logged:
[WRN] PDF not yet available in blob for invoice {invoiceId} — sending without attachment
This is intentional — the invoice email is sent promptly and the PDF follows in a subsequent process. The Hangfire job does not wait for PDF generation.
SendGrid Dashboard
| Item | Detail |
|---|---|
| Login | Bitwarden → "SendGrid" |
| Activity feed | Shows all sent, delivered, bounced, and failed emails with timestamps |
| API keys | Settings → API Keys — production key is named roundtrip-production |
| Sender authentication | Verify that noreply@roundtrips.app (or configured sender) is authenticated |
Reading the Activity Feed
The SendGrid activity feed is the fastest way to diagnose email delivery issues. Each entry shows:
- Processed At — when SendGrid received the request
- Message ID — unique identifier for the email
- Recipient Email — who it was sent to
- Subject Line — confirms which template fired
- Status — Delivered, Bounced, Blocked, Spam Report
- Response — SMTP response code (250 = success)
Troubleshooting
Emails not sending — "SendGrid:ApiKey is not configured"
This means the App Service environment variable is missing or the Key Vault reference is not resolving.
- Go to Azure Portal →
app-roundtrip-production→ Environment variables - Check that
SendGrid__ApiKeyexists and shows Key vault with a green checkmark - If missing — add the App Service setting (see Configuration above)
- If red/yellow — check that
SendGrid--ApiKeysecret exists inkv-roundtrip-production - After fixing — full stop → start the App Service (not restart)
- Go to Hangfire dashboard → Failed jobs → Requeue the failed send jobs
Emails not arriving — no error in logs
The email was sent successfully by the application but not received by the user.
- Check SendGrid Activity feed — find the email by recipient address or time
- Check the Status column:
- Delivered — SendGrid delivered to the receiving mail server. Check spam folder.
- Bounced — invalid email address or receiving server rejected it
- Blocked — sending domain or IP blocked by recipient server
- Spam Report — recipient marked as spam
Emails sending without PDF attachment
See the PDF Attachment Behaviour section above. This is a timing issue — the PDF was not in blob storage when the job ran. If the client needs the PDF:
- Go to the invoice in the RoundTrip app
- Download the PDF directly (if generated)
- Or resend the invoice once the PDF is confirmed in blob storage
Planned Enhancements
| Enhancement | Notes |
|---|---|
| Email templates in SendGrid | Move HTML email templates from code into SendGrid Dynamic Templates for easier updates without deployment |
| Overdue invoice reminder emails | Automated reminder at 7, 14, 30 days past due |
| Technician job summary emails | Daily digest of completed jobs for technicians |