Skip to main content
RapidDev - Software Development Agency
flutterflow-integrationsFlutterFlow API Call

SendGrid API

Connect FlutterFlow to SendGrid by deploying a Firebase Cloud Function or Supabase Edge Function that holds your SendGrid API key and calls the v3 Mail Send endpoint. FlutterFlow triggers your proxy with recipient details and template variables — the proxy builds the JSON payload and sends it. Never place a SendGrid API key in a FlutterFlow API Call header, as it can send email as your verified domain from anywhere.

What you'll learn

  • Why a backend proxy is mandatory for SendGrid and how to deploy one in Firebase or Supabase
  • How to verify your sender identity in SendGrid before any emails can be sent
  • How to create a FlutterFlow API Call that triggers your proxy with recipient and template data
  • How to use SendGrid Dynamic Templates so your marketing team can edit email copy without app redeployments
  • How to handle SendGrid's 202 Accepted response correctly in FlutterFlow's action flow
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate18 min read30 minutesMarketingLast updated July 2026RapidDev Engineering Team
TL;DR

Connect FlutterFlow to SendGrid by deploying a Firebase Cloud Function or Supabase Edge Function that holds your SendGrid API key and calls the v3 Mail Send endpoint. FlutterFlow triggers your proxy with recipient details and template variables — the proxy builds the JSON payload and sends it. Never place a SendGrid API key in a FlutterFlow API Call header, as it can send email as your verified domain from anywhere.

Quick facts about this guide
FactValue
ToolSendGrid API
CategoryMarketing
MethodFlutterFlow API Call
DifficultyIntermediate
Time required30 minutes
Last updatedJuly 2026

Trigger Transactional Emails from Your FlutterFlow App

Transactional emails — welcome messages when users sign up, password reset codes, order confirmations, shipping notifications — are a core part of any serious app experience. SendGrid's v3 API makes sending these emails straightforward: one POST request with a JSON payload containing the recipient, sender, subject, and content. The SendGrid free plan supports approximately 100 emails per day, and paid Essentials and Pro plans scale by volume — check SendGrid's current pricing page for exact numbers as plans change.

The critical security point for FlutterFlow developers: a SendGrid API key scoped to Mail Send can send email from any of your verified senders to anyone in the world. If that key ends up in your compiled Flutter app bundle — even inside an API Call header that looks private — it can be extracted by anyone who installs your app and inspects the binary. The consequence is not just a leaked key; an attacker can use your domain to send phishing or spam emails, destroying your sender reputation and potentially getting your domain blacklisted. This is why the proxy pattern is non-negotiable for SendGrid.

The good news: the architecture is clean and the actual proxy code is minimal. A Cloud Function or Edge Function of about 30 lines receives a simple trigger from FlutterFlow, builds the SendGrid payload, and returns a success indicator. Once it is deployed, adding new email triggers in your FlutterFlow app takes only minutes. Using Dynamic Templates adds another layer of flexibility — your marketing team can update email copy, subject lines, and layouts directly in SendGrid without requiring any changes to the FlutterFlow app or proxy code.

Integration method

FlutterFlow API Call

SendGrid's v3 Mail Send endpoint is a single REST call with a nested JSON payload — conceptually simple, but the API key grants the ability to send email as your verified domain and must never be placed in a Flutter client bundle. The correct pattern is a Firebase Cloud Function or Supabase Edge Function that receives a trigger from FlutterFlow (recipient email, template ID, dynamic data), builds the full SendGrid payload, and calls `https://api.sendgrid.com/v3/mail/send` server-side with the key in the Authorization header. FlutterFlow's API Calls point to your proxy URL.

Prerequisites

  • A SendGrid account with a verified sender identity — either single-sender verification or domain authentication (required before any email can be sent)
  • A SendGrid API key with at least the 'Mail Send' scope, created from Settings → API Keys in the SendGrid dashboard
  • A Firebase project with Cloud Functions enabled (Blaze plan required) or a Supabase project with Edge Functions
  • A FlutterFlow project with a screen where email sending should be triggered (sign-up screen, checkout completion, etc.)
  • Optional: at least one Dynamic Template created in SendGrid's Email API → Dynamic Templates section if you want editable email copy

Step-by-step guide

1

Create a scoped SendGrid API key and verify your sender identity

Log in to your SendGrid account and complete two critical setup tasks before writing any code. First, verify your sender identity — SendGrid will refuse all email sends with a 403 Forbidden error if your sender is not verified. There are two options: single-sender verification (Settings → Sender Authentication → Single Sender Verification → Create New Sender) works for testing and small scale; domain authentication (Settings → Sender Authentication → Authenticate Your Domain) is required for production and better deliverability. Follow the DNS record instructions SendGrid provides — this typically takes 5-30 minutes to propagate. Once your sender is verified, create a scoped API key. Navigate to Settings → API Keys → Create API Key. Give it a descriptive name like 'FlutterFlow App - Mail Send'. Under permissions, choose 'Restricted Access' and enable only 'Mail Send' → Full Access. Leave all other permissions at No Access. This limits the blast radius if the key is ever compromised — it can only send email, not read contacts, manage templates, or access analytics. Click Create & View. Copy the API key immediately — SendGrid only shows it once. Store it temporarily in a secure notes app. You will paste it into your Cloud Function environment variables in the next step. Never paste it into FlutterFlow.

Pro tip: If you are testing during development, use a personal email address as both the 'From' sender and the 'To' recipient so you can verify delivery in your own inbox before pointing at real users.

Expected result: You have a verified sender address or domain in SendGrid, and a scoped API key (Mail Send only) copied to a secure location.

2

Deploy a proxy Cloud Function or Edge Function that holds the API key

The proxy is the core of this integration. It receives a simple JSON payload from FlutterFlow — typically just the recipient email, a template ID, and dynamic data — and assembles the full nested SendGrid payload server-side before calling `https://api.sendgrid.com/v3/mail/send` with your API key in the Authorization header. For Firebase Cloud Functions: in your Firebase project, create a new HTTPS function. Store your SendGrid API key using Firebase environment config (`firebase functions:config:set sendgrid.key=SG.yourkey`) or as a Secret in the Firebase Console (Functions → Secrets). The function accepts a POST body with `to_email`, `to_name`, `template_id`, and `template_data`, builds the SendGrid payload, and calls the API. Return a simple `{ success: true }` for a 202 response or an error object for failures. For Supabase Edge Functions: navigate to Edge Functions in your Supabase Dashboard, click New Function, name it `send-email`, and write the Deno function. Store the API key in Supabase Secrets (Settings → Edge Functions → Secrets) as `SENDGRID_API_KEY`. Both approaches result in a deployed URL that FlutterFlow will call. CRITICAL: SendGrid returns HTTP 202 Accepted (not 200 OK) for a successful mail send. Your proxy should pass this status through, and you need to teach FlutterFlow to treat 202 as success. If your proxy normalizes to 200, make that explicit in your code comments.

index.js
1// Firebase Cloud Function: SendGrid email proxy
2// Deploy with: firebase deploy --only functions
3const functions = require('firebase-functions');
4const fetch = require('node-fetch');
5
6exports.sendEmail = functions.https.onRequest(async (req, res) => {
7 res.set('Access-Control-Allow-Origin', '*');
8 res.set('Access-Control-Allow-Methods', 'POST');
9 res.set('Access-Control-Allow-Headers', 'Content-Type');
10 if (req.method === 'OPTIONS') { res.status(204).send(''); return; }
11 if (req.method !== 'POST') { res.status(405).json({ error: 'Method not allowed' }); return; }
12
13 const SENDGRID_API_KEY = process.env.SENDGRID_API_KEY;
14 const FROM_EMAIL = 'hello@yourdomain.com'; // must be verified in SendGrid
15 const FROM_NAME = 'Your App Name';
16
17 const { to_email, to_name = '', template_id, template_data = {} } = req.body;
18 if (!to_email || !template_id) {
19 res.status(400).json({ error: 'to_email and template_id are required' });
20 return;
21 }
22
23 const payload = {
24 personalizations: [{ to: [{ email: to_email, name: to_name }], dynamic_template_data: template_data }],
25 from: { email: FROM_EMAIL, name: FROM_NAME },
26 template_id: template_id,
27 };
28
29 try {
30 const sgRes = await fetch('https://api.sendgrid.com/v3/mail/send', {
31 method: 'POST',
32 headers: {
33 'Authorization': `Bearer ${SENDGRID_API_KEY}`,
34 'Content-Type': 'application/json',
35 },
36 body: JSON.stringify(payload),
37 });
38 // SendGrid returns 202 for success (no body)
39 if (sgRes.status === 202) {
40 res.status(200).json({ success: true, message: 'Email queued for delivery' });
41 } else {
42 const errorBody = await sgRes.json();
43 res.status(sgRes.status).json({ success: false, error: errorBody });
44 }
45 } catch (err) {
46 res.status(500).json({ success: false, error: err.message });
47 }
48});

Pro tip: Set the FROM_EMAIL constant in your function code (not as an environment variable) since it is your verified sender address, not a secret. This makes it easy to see at a glance which sender your function uses.

Expected result: Your Cloud Function or Edge Function is deployed and returns `{ success: true }` when called with a valid `to_email` and `template_id`. You can verify by calling the function URL directly from Postman with test data and checking your inbox.

3

Create a SendGrid Dynamic Template for editable email copy

Dynamic Templates let your marketing or content team update email subject lines, body copy, and design directly in SendGrid without requiring any changes to your FlutterFlow app or proxy code. This is especially valuable for welcome emails, newsletters, and promotional messages where copy needs frequent iteration. In SendGrid, navigate to Email API → Dynamic Templates → Create a Dynamic Template. Give it a name like 'Welcome Email'. Click Add Version and choose the Design Editor or Code Editor. In the Design Editor, you can use SendGrid's drag-and-drop builder to create the email layout. Use Handlebars syntax to insert dynamic variables: `{{first_name}}` for the user's name, `{{app_name}}` for your app name, and so on. For simple transactional emails, the Code Editor (HTML + text) gives you more control. Create a minimal welcome email that uses `{{first_name}}`, `{{cta_url}}`, and any other variables your FlutterFlow app will pass in `dynamic_template_data`. After saving, note the Template ID — it looks like `d-32characters`. This is the value you will pass as `template_id` in your FlutterFlow API Call payload. The template ID is not a secret — it is a public identifier for the email design. Create separate templates for each email type your app sends: welcome, order confirmation, password reset, appointment reminder. Having separate template IDs for each keeps your analytics clean and lets you A/B test individual email types without affecting others.

Pro tip: Always create both an HTML and a plain-text version of each Dynamic Template. Many email clients prefer plain text, and having both improves deliverability scores and prevents spam filter triggers.

Expected result: You have at least one Dynamic Template in SendGrid with a Template ID (e.g., `d-abc123def456...`). The template uses Handlebars variables like `{{first_name}}` for personalization. You can test it using SendGrid's preview function before wiring it to FlutterFlow.

4

Create a FlutterFlow API Call to trigger the email proxy

With the proxy deployed and templates ready, create the FlutterFlow API Call that connects your app to the email-sending backend. This call goes to your proxy URL, not to SendGrid directly. In FlutterFlow, click API Calls in the left navigation. Click + Add → Create API Group. Name it 'Email Service' and set the Base URL to your Cloud Function or Edge Function URL (e.g., `https://us-central1-your-project.cloudfunctions.net`). Add a header: `Content-Type: application/json`. Click Save. Click + Add → Create API Call. Set the Name to 'Send Email', the Method to POST, and the Endpoint to `/sendEmail` (or your function path). In the Variables tab, click + Add Variable and create: `to_email` (String), `to_name` (String), `template_id` (String), and `first_name` (String) — adjust the last one based on what your specific template uses. In the Request Body tab, set the body format to JSON and enter: `{ "to_email": "{{ to_email }}", "to_name": "{{ to_name }}", "template_id": "{{ template_id }}", "template_data": { "first_name": "{{ first_name }}" } }`. Add any additional fields your Dynamic Template uses. In the Response & Test tab, click Test, fill in your own email as `to_email`, the welcome template ID as `template_id`, and your name as `first_name`. Click Run Test. If your proxy is set up correctly, you should receive a `{ success: true }` response and find a real email in your inbox within a few minutes. If you see a 403 error, your sender identity is not verified. If you see a 401, your API key is wrong or not being injected into the proxy correctly.

typescript
1{
2 "method": "POST",
3 "endpoint": "/sendEmail",
4 "headers": {
5 "Content-Type": "application/json"
6 },
7 "body": {
8 "to_email": "{{ to_email }}",
9 "to_name": "{{ to_name }}",
10 "template_id": "{{ template_id }}",
11 "template_data": {
12 "first_name": "{{ first_name }}",
13 "app_name": "Your App Name"
14 }
15 }
16}

Pro tip: For emails that always use the same template (like your welcome email), hardcode the `template_id` as a default value on the `template_id` variable rather than passing it dynamically. This reduces the number of variables you need to set in each Action.

Expected result: The Send Email API Call in FlutterFlow returns `{ success: true }` in the Test tab and delivers a real email to the test address. The template renders with the correct personalized values substituted.

5

Wire the email action to app events and handle the response

Connect the Send Email API Call to the appropriate moments in your FlutterFlow app's user journey, and handle both success and failure states gracefully so users are not left confused if something goes wrong. Navigate to the screen where you want to trigger an email — for example, the registration success screen or a form submission confirmation page. Click on the primary button or use the On Page Load trigger if the email should fire automatically. In the right panel, click the Actions tab and + Add Action. Select 'Backend/API Call', choose the Email Service group, and select Send Email. In the Set Variables section, bind `to_email` to the current user's email (available as a FlutterFlow variable if you use Supabase Auth or Firebase Auth — look for Auth User Email or the user's email field from your database). Bind `to_name` to the user's display name. Set `template_id` to the template ID string for this specific email type (e.g., `d-welcome-template-id`). After the API Call action, add a Conditional action that checks the API response. Use the JSON Path `$.success` to check the value — if `true`, show a success Snackbar like 'Check your inbox!'. If the response indicates failure or the call errors out, show a gentle error Snackbar like 'We could not send the confirmation email. Please check your inbox settings or contact support.' For critical emails like password resets, avoid silent failures. For non-critical emails like promotional confirmations, a fire-and-forget pattern (no response check, no UI feedback) is acceptable and simpler to implement. Match the UX treatment to the email's importance in the user journey.

Pro tip: Do not show a loading indicator while the email sends — users should never feel like they are waiting for an email to be dispatched. Show the success state immediately after tapping the button, with the Snackbar appearing asynchronously when the API Call returns.

Expected result: When a user completes registration (or another configured trigger), the Send Email action fires automatically, the proxy sends the email, and the user sees either a success Snackbar or an error Snackbar based on the response. The email arrives in the recipient's inbox within minutes.

6

Handle 202 responses and set up error monitoring

SendGrid returns HTTP 202 Accepted for a successful mail send — this is different from the 200 OK that most APIs return. It means the email has been accepted into SendGrid's delivery queue, not that it has been delivered. Delivery happens asynchronously, and most emails arrive within seconds to a few minutes, though busy queues or blocklisted domains can cause delays. If your proxy normalizes SendGrid's 202 to a 200 response (as shown in the code example in Step 2), FlutterFlow will see a 200 OK and you can treat it as success straightforwardly. If your proxy passes through the raw 202, you need to configure FlutterFlow's response handling to accept 202 as a success status — in the API Call's Response & Test tab, the response code checking is primarily for documentation; the actual handling is in your Action's conditional logic based on the body content. For production apps, configure SendGrid's Event Webhook to receive delivery status updates: in the SendGrid dashboard go to Settings → Mail Settings → Event Webhook, enter your Cloud Function URL (you will need a separate function for this), and enable the Delivered, Bounce, and Spam Report events. Log bounces and spam reports to your Firestore or Supabase database so you can stop emailing invalid addresses and protect your sender reputation. If you need help setting up the webhook-based delivery monitoring or the full proxy architecture, RapidDev's team builds FlutterFlow + SendGrid integrations regularly and offers a free scoping call at rapidevelopers.com/contact. Sender reputation issues can be difficult to recover from once they start, so getting the delivery monitoring right early is worth the effort.

index.js
1// Firebase Cloud Function: handle SendGrid delivery webhooks
2// Add this as a separate function alongside sendEmail
3exports.sendgridWebhook = functions.https.onRequest(async (req, res) => {
4 if (req.method !== 'POST') { res.status(405).send(''); return; }
5
6 const events = req.body; // array of SendGrid event objects
7 const db = admin.firestore();
8
9 for (const event of events) {
10 const { email, event: eventType, timestamp, reason } = event;
11 if (eventType === 'bounce' || eventType === 'spamreport') {
12 await db.collection('email_issues').add({
13 email,
14 event_type: eventType,
15 reason: reason || '',
16 occurred_at: new Date(timestamp * 1000),
17 logged_at: admin.firestore.FieldValue.serverTimestamp(),
18 });
19 console.warn(`Email issue: ${eventType} for ${email}`);
20 }
21 }
22 res.status(200).send('OK');
23});

Pro tip: Check your SendGrid Activity Feed (Activity → Activity Feed) the first few times you test the integration. It shows every email attempt with delivery status, bounce codes, and spam filter results — far more detail than you get from the API response alone.

Expected result: Your app correctly interprets success responses from the proxy, shows appropriate UI feedback to users, and (for production) logs bounce and spam events to your database so you can monitor sender reputation over time.

Common use cases

SaaS app that sends welcome emails on user registration

When a new user signs up in a FlutterFlow app, a registration success action triggers the SendGrid proxy with the user's email and first name. The proxy sends a welcome email using a Dynamic Template that the marketing team keeps updated. The user sees the app's success screen while the email is sent asynchronously in the background.

FlutterFlow Prompt

After a user successfully registers, send them a welcome email using the SendGrid Dynamic Template with ID 'd-abc123'. Pass their first name as the dynamic_template_data so the email personalizes to 'Hi Sarah,' and include a link to the app's getting started guide.

Copy this prompt to try it in FlutterFlow

E-commerce app that emails order confirmations

After a purchase is completed in a FlutterFlow shopping app, the order confirmation screen triggers the SendGrid proxy with the order details, item list, and total. The proxy sends a structured order confirmation email with the Dynamic Template that shows the line items and shipping estimate. This replaces the need for a separate backend service just for email.

FlutterFlow Prompt

When the payment success screen loads, call the SendGrid proxy with the order ID, item names, quantities, prices, and total. Use the order confirmation Dynamic Template and pass all items as an array in dynamic_template_data so the template can loop through them.

Copy this prompt to try it in FlutterFlow

Appointment app that sends booking confirmations and reminders

A FlutterFlow booking app sends confirmation emails immediately after a slot is booked and reminder emails 24 hours before the appointment via a scheduled Cloud Function. Both use SendGrid Dynamic Templates — the booking confirmation has the appointment details, and the reminder uses a different template with a shorter, more urgent tone.

FlutterFlow Prompt

When a user confirms a booking, trigger a SendGrid confirmation email with the appointment date, time, provider name, and a cancellation link. Schedule a reminder email for 24 hours before the appointment time using a Firestore scheduled trigger.

Copy this prompt to try it in FlutterFlow

Troubleshooting

API Call returns 403 Forbidden even though the API key is correct

Cause: The sender identity used in your proxy's FROM_EMAIL has not been verified in SendGrid. SendGrid requires all senders to be either single-sender verified or domain-authenticated before accepting mail send requests, regardless of API key validity.

Solution: Log in to SendGrid and navigate to Settings → Sender Authentication. Complete either single-sender verification (for testing) or domain authentication (for production). Domain authentication requires adding CNAME records to your DNS — check your DNS provider's dashboard and allow up to 48 hours for propagation. Once verified, retry the API Call.

FlutterFlow API Call shows an error but emails are still being delivered

Cause: Your proxy is passing SendGrid's raw 202 Accepted status to FlutterFlow, which may be interpreting it as an error depending on how your response handling is configured. 202 is a valid success status that means 'accepted for delivery' — it is not an error.

Solution: In your proxy code, convert SendGrid's 202 response to a 200 response with a `{ success: true }` body (see the code in Step 2). Alternatively, update your FlutterFlow Action's conditional check to treat both 200 and 202 as success states. The simplest fix is the proxy normalization approach.

Dynamic template variables like {{first_name}} appear literally in the email instead of being replaced

Cause: The `template_data` object in your proxy payload does not include the variable key, or the key name does not exactly match the Handlebars variable name used in the Dynamic Template (case-sensitive).

Solution: In the SendGrid Dynamic Template editor, note the exact variable names used (e.g., `{{first_name}}` requires the key `first_name`). In your proxy code, confirm the `dynamic_template_data` object contains all required keys with the exact same names. Test by sending a raw JSON payload to your proxy via Postman with the full template_data object and verifying the rendered email in SendGrid's Activity Feed.

XMLHttpRequest error when calling the proxy from FlutterFlow web preview

Cause: CORS (Cross-Origin Resource Sharing) headers are missing from your Cloud Function or Edge Function response. Web browsers block cross-origin requests that do not include the correct CORS headers, while native mobile builds are not affected.

Solution: In your Cloud Function, add CORS headers to all responses and handle OPTIONS preflight requests. The code in Step 2 already includes this — ensure `res.set('Access-Control-Allow-Origin', '*')` and the OPTIONS handler are present in your function. If you are using Firebase, you can also use the `cors` npm package for cleaner CORS handling.

Best practices

  • Always proxy SendGrid API calls through a backend function — a leaked Mail Send key lets anyone send spam from your verified domain, destroying your sender reputation and potentially blacklisting your domain.
  • Create separate, narrowly-scoped API keys for each integration (Mail Send only) rather than using a full-access key — limits the damage if a key is ever compromised.
  • Use SendGrid Dynamic Templates for all email content so marketing can iterate on copy without requiring app updates or redeployments.
  • Verify your sending domain (domain authentication) rather than using single-sender verification for production apps — it significantly improves deliverability and prevents your emails from landing in spam folders.
  • Normalize SendGrid's 202 Accepted response to 200 OK in your proxy so FlutterFlow's response handling treats it consistently as success.
  • Set up SendGrid's Event Webhook to log bounces and spam reports to your database — unmonitored bounce rates can get your account suspended without warning.
  • Do not show a loading spinner while emails are being queued — show the success state immediately after the user's action and let the email delivery happen asynchronously in the background.
  • Use one Dynamic Template per email type (welcome, reset, confirmation) rather than re-using templates with different data — keeps analytics clean and makes A/B testing easier.

Alternatives

Frequently asked questions

Can I call the SendGrid API directly from FlutterFlow without a proxy?

Technically yes, but you absolutely should not. A SendGrid API key with Mail Send permissions placed in a FlutterFlow API Call header is baked into the compiled app binary where it can be extracted by anyone who installs the app. With that key, an attacker can send unlimited spam from your verified domain, tank your sender reputation, and get your domain blacklisted within hours. The proxy is not optional for production apps.

Why does SendGrid return 202 instead of 200 when an email is sent successfully?

SendGrid uses HTTP 202 Accepted to indicate that your email has been queued for delivery — it does not guarantee that the email has reached the recipient's inbox yet. Delivery happens asynchronously after the 202 response. This is standard REST API behavior for asynchronous operations. In your proxy, you can normalize this to 200 OK with a success body to simplify FlutterFlow's response handling.

Does FlutterFlow have a native SendGrid integration?

No — FlutterFlow does not have a built-in SendGrid connector. The integration is built using FlutterFlow's API Calls feature pointing to a backend proxy that you deploy in Firebase or Supabase. This proxy pattern is more flexible than a native integration would be, since you can customize the payload, add logging, and handle errors server-side.

How long does it take for a SendGrid email to arrive?

Most SendGrid emails arrive within seconds to a few minutes. The 202 Accepted response means the email entered SendGrid's delivery queue successfully. Actual delivery time depends on the recipient's mail server responsiveness, your sender reputation, and current SendGrid queue volume. Use SendGrid's Activity Feed to track delivery status if an email appears delayed.

What is the SendGrid free plan limit?

The SendGrid free plan supports approximately 100 emails per day. For development and low-volume testing this is sufficient, but any production app with regular user registrations or transactions will need a paid plan. Check SendGrid's current pricing page for the latest plan details, as tiers and limits change periodically.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Integrations are where projects stall

We wire FlutterFlow integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.