To integrate SendGrid with Lovable, store your SendGrid API key in Cloud → Secrets, then ask Lovable to create an Edge Function that calls SendGrid's v3 Mail Send API. The Edge Function runs on Deno, keeps your API key server-side, and lets your app send transactional emails — welcome messages, password resets, receipts, and notifications — without exposing credentials to the browser.
Send transactional emails from Lovable using SendGrid's v3 API
Lovable Cloud includes built-in email capabilities — it handles Supabase Auth emails like magic links, email confirmations, and password resets automatically using its own SendGrid-powered infrastructure. For those standard authentication flows, you do not need to configure SendGrid yourself. But the moment you need custom transactional emails — a branded welcome message, an order receipt, a shipping notification, a weekly digest — you need your own SendGrid account and your own Edge Function to send them.
SendGrid is the right choice here because it is purpose-built for one-to-one triggered emails rather than bulk campaigns. When a user signs up, you want a single welcome email delivered immediately to that one person. When an order ships, you want a single receipt sent the moment the database record updates. This is the transactional model: one event triggers one email. SendGrid's v3 API handles this pattern with a simple POST request to /v3/mail/send, and it gives you delivery analytics, bounce tracking, and template management on top.
The integration pattern in Lovable is straightforward. Your React frontend calls a Lovable Edge Function (which runs on Deno at the edge). The Edge Function reads your SendGrid API key from Cloud → Secrets using Deno.env.get(), constructs the email payload, and forwards it to SendGrid's API. The key never touches your frontend code, never appears in your Git repository, and is never visible in Lovable's chat history. Lovable's security infrastructure already blocks approximately 1,200 hardcoded API keys per day — this pattern is why.
Integration method
SendGrid has no native Lovable connector, so all API calls are proxied through an Edge Function. The function runs on Deno, retrieves your SendGrid API key from Cloud → Secrets via Deno.env.get(), calls the SendGrid v3 /mail/send endpoint, and returns the result to your frontend. This keeps your API key in the server-side red zone and prevents CORS errors.
Prerequisites
- A Lovable project with at least one deployed app (Edge Functions require Lovable Cloud)
- A SendGrid account (free tier at sendgrid.com — 100 emails/day at no cost)
- A verified sender identity in SendGrid (either a single sender address or a verified domain)
- Your SendGrid API key with 'Mail Send' permission enabled (SendGrid Dashboard → Settings → API Keys)
- Basic understanding of what an Edge Function does in Lovable (server-side code that runs on Deno)
Step-by-step guide
Create a SendGrid API key with Mail Send permission
Create a SendGrid API key with Mail Send permission
Before touching Lovable, you need a SendGrid API key scoped to email sending only. Log in to your SendGrid account at app.sendgrid.com. In the left sidebar, navigate to Settings → API Keys. Click the 'Create API Key' button in the top-right corner. Give your key a descriptive name like 'Lovable Production' so you can identify it later. Under API Key Permissions, select 'Restricted Access' rather than Full Access — this follows the principle of least privilege. Expand the Mail Send section and set it to 'Full Access'. All other permissions should remain at 'No Access'. Click 'Create & View'. SendGrid will display your API key exactly once — it starts with 'SG.' and is a long alphanumeric string. Copy it immediately and paste it somewhere temporary (like a local text file that you will delete afterward). You cannot retrieve this key again after closing the dialog; you would need to create a new one. Also verify your sender identity before using the API. In SendGrid, go to Settings → Sender Authentication. For testing, you can use Single Sender Verification (verify a specific email address). For production, set up Domain Authentication (verify your entire domain with DNS records) for better deliverability and to avoid spam filters. If you skip sender verification, SendGrid will reject your API calls with a 403 error.
Pro tip: Always use Restricted Access API keys, not Full Access. If the key is ever compromised, an attacker can only send emails — not access your account settings, billing, or contact lists.
Expected result: A SendGrid API key starting with 'SG.' is created and copied. Your sender email address or domain is verified in SendGrid's Sender Authentication settings.
Add your SendGrid API key to Lovable Cloud Secrets
Add your SendGrid API key to Lovable Cloud Secrets
With your API key ready, the next step is storing it securely in Lovable's Cloud Secrets panel. Secrets are encrypted environment variables that are only accessible inside Edge Functions via Deno.env.get() — they are never exposed to your frontend React code, never committed to your GitHub repository, and never visible in Lovable's chat history. In your Lovable project, click the '+' icon at the top of the editor panel (next to the Preview label). This opens the Cloud panel with multiple tabs: Database, Auth, Storage, Edge Functions, AI, Secrets, Logs, and Usage. Click the 'Secrets' tab. Click 'Add new secret'. In the Name field, type SENDGRID_API_KEY exactly (case-sensitive — your Edge Function code will reference this exact name). In the Value field, paste the API key you copied in Step 1. Click Save. Optionally, add a second secret for your sender email address: Name: SENDGRID_FROM_EMAIL, Value: the verified email you set up in SendGrid (e.g., noreply@yourapp.com). Storing the sender address as a secret makes it easy to change without touching code. Critical: Never paste your SendGrid API key directly into Lovable's chat prompt. On free-tier workspaces, chat history is publicly visible. Even on paid plans, keys pasted in chat can end up in your Git commit history. Lovable blocks approximately 1,200 hardcoded API keys per day — but the safest approach is to never expose them in the first place.
Pro tip: After adding secrets, you do not need to restart anything. Secrets are available to all Edge Functions in your project immediately.
Expected result: SENDGRID_API_KEY (and optionally SENDGRID_FROM_EMAIL) appear in your Cloud → Secrets panel with masked values. The Edge Function you create in the next step will be able to access them.
Create the SendGrid email Edge Function
Create the SendGrid email Edge Function
Now you will create the Edge Function that acts as a secure proxy between your Lovable frontend and SendGrid's API. Open Lovable's chat and paste a prompt describing the email function you need. Lovable will generate the TypeScript code, create the file at supabase/functions/send-email/index.ts, and deploy it to Lovable Cloud automatically. The function needs to: accept a POST request from your frontend, validate the request body, retrieve the SendGrid API key from environment variables, construct the SendGrid v3 email payload, send a POST request to https://api.sendgrid.com/v3/mail/send, and return a success or error response. Two important implementation details for Deno Edge Functions: First, always include CORS headers in your response so the browser does not block the response from your frontend. Second, handle the OPTIONS preflight request that browsers send before cross-origin POST requests. The code block below shows the complete, production-ready pattern. After Lovable generates the function, review it in the Code panel (click '+' then Code) at supabase/functions/send-email/index.ts. Verify that it uses Deno.env.get('SENDGRID_API_KEY') — not a hardcoded key — and that it returns proper CORS headers for both the OPTIONS preflight and the actual POST response.
Create an Edge Function at supabase/functions/send-email/index.ts that accepts a POST request with JSON body containing { to, subject, html, text, fromName }. It should retrieve SENDGRID_API_KEY and SENDGRID_FROM_EMAIL from Deno environment variables, then POST to https://api.sendgrid.com/v3/mail/send with proper SendGrid v3 payload format. Return a 200 response on success or a 500 with error message on failure. Include CORS headers for all responses and handle the OPTIONS preflight request.
Paste this in Lovable chat
1import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'23const corsHeaders = {4 'Access-Control-Allow-Origin': '*',5 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',6}78serve(async (req) => {9 // Handle CORS preflight10 if (req.method === 'OPTIONS') {11 return new Response('ok', { headers: corsHeaders })12 }1314 try {15 const { to, subject, html, text, fromName } = await req.json()1617 const apiKey = Deno.env.get('SENDGRID_API_KEY')18 const fromEmail = Deno.env.get('SENDGRID_FROM_EMAIL') || 'noreply@example.com'1920 if (!apiKey) {21 throw new Error('SENDGRID_API_KEY secret is not set')22 }2324 if (!to || !subject || (!html && !text)) {25 return new Response(26 JSON.stringify({ error: 'Missing required fields: to, subject, and html or text' }),27 { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }28 )29 }3031 const payload = {32 personalizations: [{ to: [{ email: to }] }],33 from: { email: fromEmail, name: fromName || 'MyApp' },34 subject,35 content: [36 ...(text ? [{ type: 'text/plain', value: text }] : []),37 ...(html ? [{ type: 'text/html', value: html }] : []),38 ],39 }4041 const response = await fetch('https://api.sendgrid.com/v3/mail/send', {42 method: 'POST',43 headers: {44 'Authorization': `Bearer ${apiKey}`,45 'Content-Type': 'application/json',46 },47 body: JSON.stringify(payload),48 })4950 if (!response.ok) {51 const errorBody = await response.text()52 throw new Error(`SendGrid API error ${response.status}: ${errorBody}`)53 }5455 return new Response(56 JSON.stringify({ success: true, message: 'Email sent successfully' }),57 { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }58 )59 } catch (error) {60 return new Response(61 JSON.stringify({ error: error.message }),62 { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }63 )64 }65})Pro tip: SendGrid returns a 202 Accepted status (not 200) on success. The code above correctly handles this by checking response.ok, which is true for any 2xx status.
Expected result: Lovable creates and deploys supabase/functions/send-email/index.ts. You can see it listed in Cloud → Edge Functions. The function accepts POST requests with email parameters and calls SendGrid's API using the stored secret.
Call the Edge Function from your React frontend
Call the Edge Function from your React frontend
With the Edge Function deployed, the next step is wiring it to your React components. You will call the Edge Function using the Supabase client's functions.invoke() method, which handles authentication headers and the correct endpoint URL automatically. This approach is cleaner than a raw fetch() call and integrates with your existing Supabase session. Identify where in your app emails should be triggered — the most common spots are: the signup success handler (for welcome emails), the Stripe webhook confirmation handler (for order receipts), or a button click in an admin panel (for manual notifications). In that component, add a call to supabase.functions.invoke('send-email', { body: { ... } }). The function name 'send-email' must match exactly the folder name you used in supabase/functions/send-email/. Lovable's AI knows this convention and will generate the correct invoke call if you describe it in a prompt. For error handling: wrap the invoke call in a try-catch and check the error property of the response. SendGrid failures (invalid email, unverified sender, API key issues) will come back as error messages in the response body. Log these during development and show appropriate feedback to users — for example, 'We had trouble sending your confirmation email. Please check your email address or contact support.'
On my signup page, after the user successfully creates an account with Supabase Auth, call the send-email Edge Function using supabase.functions.invoke() with their email address, a welcome subject, and a personalized HTML welcome message. Handle loading state and any errors. Show a toast notification confirming the email was sent.
Paste this in Lovable chat
1import { supabase } from '@/lib/supabase'2import { toast } from '@/components/ui/use-toast'34const sendWelcomeEmail = async (email: string, firstName: string) => {5 try {6 const { data, error } = await supabase.functions.invoke('send-email', {7 body: {8 to: email,9 subject: `Welcome to MyApp, ${firstName}!`,10 html: `11 <div style="font-family: sans-serif; max-width: 600px; margin: 0 auto;">12 <h1>Welcome, ${firstName}!</h1>13 <p>Thanks for joining MyApp. Your account is ready.</p>14 <a href="https://myapp.com/dashboard"15 style="background: #6366f1; color: white; padding: 12px 24px;16 border-radius: 6px; text-decoration: none; display: inline-block;">17 Go to Dashboard18 </a>19 </div>20 `,21 fromName: 'MyApp Team',22 },23 })2425 if (error) throw error2627 toast({28 title: 'Welcome email sent!',29 description: 'Check your inbox for a welcome message.',30 })31 } catch (err) {32 console.error('Failed to send welcome email:', err)33 // Don't block the user flow if email fails34 }35}Pro tip: Never block the user's main flow on email delivery. If sendWelcomeEmail() fails, log the error but let the user continue. Email delivery is a secondary concern — authentication and onboarding should proceed regardless.
Expected result: When a user signs up, the Edge Function is invoked, SendGrid sends the welcome email, and the user sees a success toast. Check Cloud → Logs to see the Edge Function execution and confirm it returned a 200 status.
Test the integration and verify delivery
Test the integration and verify delivery
Testing SendGrid delivery requires a deployed Lovable app — Edge Functions do not run in Lovable's preview mode. Use the Publish button in the top-right corner to deploy your app, then test using the live URL. For initial testing, use your own email address as the recipient. Trigger the email flow (sign up, click a button, etc.) and wait for the email to arrive in your inbox. Check both inbox and spam folder — if this is your first test, the email may land in spam until your domain reputation improves. If the email does not arrive, check Cloud → Logs for the Edge Function execution details. Look for error responses from SendGrid — the most common are: 401 Unauthorized (API key is invalid or not set correctly in secrets), 403 Forbidden (sender email address is not verified in SendGrid), and 400 Bad Request (malformed payload, usually a missing required field). You can also test your Edge Function directly from Cloud → Edge Functions by selecting the function and using the built-in test panel. Paste a sample request body like {"to": "test@youremail.com", "subject": "Test", "html": "<p>Test email</p>"} and click Invoke. The raw response from SendGrid will appear, making it easy to diagnose issues without triggering your full UI flow. For production readiness: set up SendGrid's Event Webhook to track bounces, opens, and clicks. This requires a separate Edge Function that receives POST notifications from SendGrid and stores delivery events in your Supabase database for analytics.
Pro tip: SendGrid has a free tier of 100 emails/day forever. For testing, this is more than enough. When you are ready for production volume, the Essentials plan starts at $19.95/month for 50,000 emails.
Expected result: Test emails arrive in the target inbox within seconds. Cloud → Logs shows the Edge Function executed successfully with no errors. In your SendGrid Dashboard → Activity Feed, you can see the sent message with delivery status.
Common use cases
Send a welcome email when a user signs up
When a new user completes registration via Supabase Auth, trigger a branded welcome email through SendGrid. The Edge Function listens for a POST from your frontend signup flow, personalizes the email with the user's name, and sends it immediately via the SendGrid API.
Create an Edge Function called send-welcome-email that accepts a POST request with { email, firstName } in the body. It should call the SendGrid v3 /mail/send API using SENDGRID_API_KEY from secrets, send a welcome email from noreply@myapp.com with subject 'Welcome to MyApp!' and a personalized HTML body. Call this Edge Function from my signup page after Supabase Auth confirms the user's email.
Copy this prompt to try it in Lovable
Send order confirmation emails after a Stripe payment
After a successful Stripe checkout, send a detailed order receipt to the customer via SendGrid. Combine your Stripe webhook Edge Function with a SendGrid email dispatch — when payment_intent.succeeded fires, compile the order details and send a formatted receipt using a SendGrid dynamic template.
Update my Stripe webhook Edge Function so that after a successful payment_intent.succeeded event, it sends an order confirmation email via SendGrid. Use SENDGRID_API_KEY from secrets. The email should include the order amount, items purchased, and a support contact. Use dynamic template ID d-xxxxxxxxx with template data containing orderAmount, customerName, and itemsList.
Copy this prompt to try it in Lovable
Send password reset emails with custom branding
While Supabase Auth sends basic password reset emails, you may want fully branded reset emails that match your app's design. Create an Edge Function that bypasses the default Supabase email and sends a styled reset email through SendGrid with your logo, colors, and support links.
Create an Edge Function called send-password-reset that accepts { email, resetLink } and sends a branded password reset email via SendGrid using SENDGRID_API_KEY. The email should come from support@myapp.com with subject 'Reset your MyApp password' and include the resetLink as a styled CTA button. Then wire this to my forgot password form instead of the default Supabase reset email.
Copy this prompt to try it in Lovable
Troubleshooting
Edge Function returns 'SENDGRID_API_KEY secret is not set' or 401 Unauthorized from SendGrid
Cause: The secret name in Cloud → Secrets does not match the name used in Deno.env.get() in the Edge Function code, or the secret was added after the function was last deployed.
Solution: Go to Cloud → Secrets and verify the secret is named SENDGRID_API_KEY exactly (case-sensitive, no spaces). Then open the Edge Function in Cloud → Edge Functions and redeploy it — secrets are injected at deploy time, so adding a secret after deployment requires a redeploy. In Lovable, the easiest way to trigger a redeploy is to make a minor edit to the function code via a chat prompt.
SendGrid returns 403 Forbidden with message 'The from address does not match a verified Sender Identity'
Cause: The email address in your 'from' field has not been verified in SendGrid's Sender Authentication settings. SendGrid requires all sender addresses to be verified before allowing API sends.
Solution: Log in to SendGrid at app.sendgrid.com and go to Settings → Sender Authentication. Either add Single Sender Verification for the specific email address you are using, or set up Domain Authentication for your entire sending domain (recommended for production). Domain Authentication also significantly improves email deliverability. After verification is complete, try sending again — no changes to your Lovable code are needed.
Emails are being delivered to spam folders instead of the inbox
Cause: New SendGrid accounts and unverified domains have low sender reputation. Without proper email authentication records (SPF, DKIM, DMARC), receiving mail servers treat messages as potentially suspicious.
Solution: In SendGrid, go to Settings → Sender Authentication and complete Domain Authentication for your sending domain. This process involves adding DNS records (SPF, DKIM) to your domain registrar. SendGrid provides step-by-step instructions and verifies the records automatically. Also ensure your email content does not use spam trigger phrases, includes a physical address (required by CAN-SPAM), and provides an unsubscribe link for marketing messages. For transactional emails, these spam issues usually resolve once domain authentication is complete.
CORS error in the browser when calling the Edge Function from the frontend
Cause: The Edge Function is missing CORS headers, or the OPTIONS preflight request is not being handled before the main POST logic.
Solution: Ensure the Edge Function handles OPTIONS requests first and includes Access-Control-Allow-Origin, Access-Control-Allow-Headers, and Access-Control-Allow-Methods in all responses. The code example in Step 3 shows the correct pattern. If you generated the function via Lovable's chat and it is missing CORS handling, ask Lovable to 'add proper CORS headers to the send-email Edge Function, including handling the OPTIONS preflight request'.
1// Add this at the top of your serve() handler, before any other logic:2if (req.method === 'OPTIONS') {3 return new Response('ok', {4 headers: {5 'Access-Control-Allow-Origin': '*',6 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',7 'Access-Control-Allow-Methods': 'POST, OPTIONS',8 }9 })10}Best practices
- Always use Restricted Access API keys scoped only to Mail Send — never Full Access keys — so a leaked key cannot compromise your SendGrid account settings or contact lists.
- Store the sender email address (SENDGRID_FROM_EMAIL) as a secret alongside the API key, so you can update it without changing Edge Function code.
- Set up Domain Authentication in SendGrid rather than Single Sender Verification for production apps — it improves deliverability, removes SendGrid branding from email headers, and is required for sending above ~100 emails/day.
- Never block the user's main application flow on email delivery success. Wrap all email sends in try-catch and handle failures gracefully — log them, optionally retry, but always let the user continue.
- Use SendGrid dynamic templates for branded emails rather than building HTML strings in your Edge Function — templates are easier to update, support version history, and can be edited by non-technical team members without code changes.
- Monitor your SendGrid Activity Feed regularly for bounces and spam reports. High bounce rates damage your sender reputation, which affects all future emails. Remove bounced addresses from your sending list promptly.
- For high-volume sending, implement a queuing pattern: store email jobs in a Supabase table and process them with a scheduled Edge Function rather than sending synchronously on every user action.
- Use separate SendGrid API keys for development and production environments, and rotate the production key every 90 days as part of your security hygiene.
Alternatives
Choose Mailchimp if your primary use case is email marketing campaigns and list management rather than triggered transactional emails.
Choose Mailgun if you want a developer-focused pay-per-email pricing model and simpler REST API without SendGrid's visual template builder.
Choose Twilio (which includes SendGrid as Twilio SendGrid) if you also need SMS and voice alongside email in a single provider relationship.
Frequently asked questions
Do I need SendGrid if Lovable Cloud already sends emails for Supabase Auth?
Lovable Cloud handles authentication emails — email confirmations, magic links, and password resets — automatically without requiring a SendGrid account. You only need to integrate SendGrid yourself when you want to send custom transactional emails that go beyond auth: welcome messages, order receipts, shipping notifications, or any email triggered by your app's business logic rather than Supabase Auth events.
Can I use SendGrid's free tier for a production Lovable app?
SendGrid's free tier allows 100 emails per day permanently, which is sufficient for early-stage apps and testing. Once you exceed 100 emails/day or need dedicated IP addresses, unsubscribe groups, or advanced analytics, the Essentials plan starts at $19.95/month for 50,000 emails. Most small Lovable apps start on the free tier and upgrade when traffic grows.
Why can't I call the SendGrid API directly from my React frontend instead of using an Edge Function?
Two reasons: security and CORS. The SendGrid API key must never appear in client-side code because it is visible to anyone who opens browser developer tools. Additionally, SendGrid's API does not allow cross-origin requests from browser JavaScript, so even if you tried, you would get a CORS error. The Edge Function pattern solves both problems: the API key stays on the server, and the browser only communicates with your own Edge Function endpoint which has CORS properly configured.
How do I use SendGrid dynamic templates with my Lovable Edge Function?
Create your template in SendGrid Dashboard → Email API → Dynamic Templates, noting the template ID (starts with 'd-'). In your Edge Function, replace the content array in the payload with template_id and dynamic_template_data fields. For example: { personalizations: [{ to: [{email}], dynamic_template_data: { firstName, orderAmount } }], from: { email: fromEmail }, template_id: 'd-yourtemplateid' }. Dynamic templates let your design team update email layouts without touching Edge Function code.
Can RapidDev help me set up a more complex SendGrid email workflow?
Yes — for complex cases like setting up SendGrid event webhooks to track opens and clicks, implementing email queuing patterns, or integrating SendGrid with multiple Lovable Edge Functions across a multi-tenant app, RapidDev's team can help design and implement the architecture. The basics covered in this guide are sufficient for most standard transactional email needs.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation