Skip to main content

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

EmailTriggerRecipientNotes
Welcome to RoundTripNew tenant user invitedInvited user's emailSent via SendInviteEmailJob after Entra user creation
InvoiceDispatcher clicks "Send Invoice"Client email on fileSent 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 NameConfig KeyPurpose
SendGrid--ApiKeySendGrid:ApiKeyAPI key for all outbound email

App Service Setting

Setting NameValue
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

ItemDetail
LoginBitwarden → "SendGrid"
Activity feedShows all sent, delivered, bounced, and failed emails with timestamps
API keysSettings → API Keys — production key is named roundtrip-production
Sender authenticationVerify 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.

  1. Go to Azure Portal → app-roundtrip-production → Environment variables
  2. Check that SendGrid__ApiKey exists and shows Key vault with a green checkmark
  3. If missing — add the App Service setting (see Configuration above)
  4. If red/yellow — check that SendGrid--ApiKey secret exists in kv-roundtrip-production
  5. After fixing — full stop → start the App Service (not restart)
  6. 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.

  1. Check SendGrid Activity feed — find the email by recipient address or time
  2. 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:

  1. Go to the invoice in the RoundTrip app
  2. Download the PDF directly (if generated)
  3. Or resend the invoice once the PDF is confirmed in blob storage

Planned Enhancements

EnhancementNotes
Email templates in SendGridMove HTML email templates from code into SendGrid Dynamic Templates for easier updates without deployment
Overdue invoice reminder emailsAutomated reminder at 7, 14, 30 days past due
Technician job summary emailsDaily digest of completed jobs for technicians