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

Zoho Mail

Connect FlutterFlow to Zoho Mail using the Zoho Mail REST API via a Firebase Cloud Function proxy. Create a Zoho OAuth self-client, deploy a Cloud Function to hold the refresh token, then build a FlutterFlow API Group on mail.zoho.com to fetch your accountId and send transactional emails from your mobile app after form submissions.

What you'll learn

  • How to create a Zoho OAuth self-client with the correct mail scopes
  • How to deploy a Firebase Cloud Function that safely refreshes Zoho access tokens
  • How to build a FlutterFlow API Group on the mail.zoho.com domain (not zohoapis.com)
  • How to fetch and store your Zoho accountId in App State before sending
  • How to wire a FlutterFlow form to send transactional confirmation emails via Zoho Mail
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate19 min read45 minutesCRM & SalesLast updated July 2026RapidDev Engineering Team
TL;DR

Connect FlutterFlow to Zoho Mail using the Zoho Mail REST API via a Firebase Cloud Function proxy. Create a Zoho OAuth self-client, deploy a Cloud Function to hold the refresh token, then build a FlutterFlow API Group on mail.zoho.com to fetch your accountId and send transactional emails from your mobile app after form submissions.

Quick facts about this guide
FactValue
ToolZoho Mail
CategoryCRM & Sales
MethodFlutterFlow API Call
DifficultyIntermediate
Time required45 minutes
Last updatedJuly 2026

Send transactional emails from your FlutterFlow app using Zoho Mail

Many FlutterFlow apps need to send confirmation emails — after a booking, a form submission, or a support request — without wiring up a full email marketing platform. Zoho Mail's REST API gives you a straightforward send-message endpoint that fits this use case well. The free tier covers up to 5 users with 5GB of storage each, and Mail Lite plans are available for higher volumes (verify current pricing on Zoho's site). The API is straightforward enough for a single purpose: look up your numeric accountId once, then POST messages with recipient, subject, and body.

The key detail that separates Zoho Mail from the other Zoho services is the domain: while Zoho CRM and Zoho Books live on zohoapis.com, Zoho Mail's API is at mail.zoho.com (or mail.zoho.eu for EU-hosted accounts). Mixing these domains is the single most common mistake and causes immediate 404 errors. The auth header is also Zoho's non-standard format — `Authorization: Zoho-oauthtoken [token]` — rather than the plain Bearer prefix used by most REST APIs.

Because FlutterFlow compiles to a Flutter client app running on the user's device, any API key or refresh token placed directly in an API Call header or in Dart code ships inside the compiled app bundle and can be extracted by someone who decompiles it. The Zoho OAuth refresh token is a long-lived credential — it should never leave your server. The pattern used throughout this tutorial routes all token management through a Firebase Cloud Function, so FlutterFlow only ever receives a short-lived one-hour access token. This is the recommended approach for any shipped app.

Integration method

FlutterFlow API Call

FlutterFlow makes REST API calls to the Zoho Mail API at mail.zoho.com. Because the Zoho OAuth refresh token is a long-lived server secret that must never ship inside a compiled mobile app, a Firebase Cloud Function holds the refresh token and mints short-lived access tokens on demand. FlutterFlow then calls this Cloud Function to get a token, fetches the Zoho accountId once and stores it in App State, and sends email messages by calling POST accounts/{accountId}/messages with the short-lived token.

Prerequisites

  • A Zoho account with Zoho Mail enabled (free tier or paid plan)
  • A FlutterFlow project open in the browser
  • A Firebase project with Cloud Functions enabled (Blaze pay-as-you-go plan required for outbound HTTP calls)
  • Basic familiarity with the FlutterFlow API Calls panel
  • Node.js 18+ installed locally to deploy the Firebase Cloud Function (one-time setup)

Step-by-step guide

1

Create a Zoho OAuth self-client with mail scopes

Open the Zoho API Console at api-console.zoho.com and sign in with your Zoho account. Click 'Add Client' and choose 'Self Client' — this is the correct type for server-to-server access where no user redirect is needed. Give it a name (e.g. 'FlutterFlow Mail Proxy') and click Create. Once created, go to the 'Generate Code' tab. In the Scope field, enter the two required scopes separated by a comma: `ZohoMail.accounts.READ,ZohoMail.messages.CREATE`. The READ scope allows fetching your accountId; CREATE scope allows sending. Set the time duration to the maximum available and click Create. Copy the grant code immediately — it expires in about a minute. Now switch to the 'Client Secret' tab and copy your Client ID and Client Secret. Then open a tool like Postman or curl to exchange the grant code for tokens. Make a POST to `https://accounts.zoho.com/oauth/v2/token` with these form fields: `code` (your grant code), `client_id`, `client_secret`, `redirect_uri` (use `https://www.zoho.com/crm/oauth` as a placeholder for self-clients), and `grant_type=authorization_code`. The response will include an `access_token` and a `refresh_token`. Copy the refresh token — this is your long-lived server secret. Store it in a secure place (Firebase project environment variables or Secret Manager), never in FlutterFlow or source code. Note your data center region: if your Zoho account is in the EU, your token URL is `accounts.zoho.eu` and your Mail API URL will be `mail.zoho.eu`.

Pro tip: If the grant code expires before you can use it, go back to the Generate Code tab and generate a fresh one. The Client ID and Secret remain the same.

Expected result: You have a Client ID, Client Secret, and a refresh token stored securely. You know which Zoho data center region your account uses (US = zoho.com, EU = zoho.eu).

2

Deploy a Firebase Cloud Function to manage token refresh

Because the Zoho refresh token is a permanent server credential that must never ship inside a compiled Flutter app, you need a backend proxy to handle token minting. Firebase Cloud Functions is the recommended choice since it integrates well with Firebase Auth (which many FlutterFlow apps already use) and has a generous free tier for infrequent calls. In your Firebase project, enable Cloud Functions (Blaze plan required for outbound HTTP). Create or open your `functions/index.js` file. The function should do two things: first, store the refresh token as a Firebase environment variable or Secret Manager secret (never hardcode it in the function source); second, expose an HTTPS endpoint that FlutterFlow can call to get a fresh access token. Deploy this function using the Firebase CLI (`firebase deploy --only functions`). Once deployed, Firebase gives you an HTTPS URL like `https://us-central1-yourproject.cloudfunctions.net/getZohoMailToken`. This is the only URL you'll put into FlutterFlow — not the Zoho token URL, and not the Zoho Mail API with the code in it. Test the function in your browser or Postman to confirm it returns a valid `access_token` string before moving to FlutterFlow.

index.js
1// functions/index.js
2const functions = require('firebase-functions');
3const axios = require('axios');
4
5// Store these in Firebase environment config or Secret Manager
6// firebase functions:config:set zoho.refresh_token=... zoho.client_id=... zoho.client_secret=... zoho.region=com
7exports.getZohoMailToken = functions.https.onRequest(async (req, res) => {
8 // Allow FlutterFlow web builds (CORS)
9 res.set('Access-Control-Allow-Origin', '*');
10 if (req.method === 'OPTIONS') {
11 res.set('Access-Control-Allow-Methods', 'GET, POST');
12 res.set('Access-Control-Allow-Headers', 'Content-Type');
13 return res.status(204).send('');
14 }
15
16 const config = functions.config().zoho;
17 const region = config.region || 'com'; // 'com' for US, 'eu' for EU
18
19 try {
20 const params = new URLSearchParams({
21 grant_type: 'refresh_token',
22 refresh_token: config.refresh_token,
23 client_id: config.client_id,
24 client_secret: config.client_secret,
25 });
26
27 const response = await axios.post(
28 `https://accounts.zoho.${region}/oauth/v2/token`,
29 params.toString(),
30 { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
31 );
32
33 const accessToken = response.data.access_token;
34 if (!accessToken) {
35 return res.status(500).json({ error: 'No access token returned', details: response.data });
36 }
37
38 return res.status(200).json({ access_token: accessToken });
39 } catch (err) {
40 console.error('Token refresh failed:', err.response?.data || err.message);
41 return res.status(500).json({ error: 'Token refresh failed' });
42 }
43});
44

Pro tip: Use `firebase functions:config:set zoho.refresh_token=YOUR_TOKEN` to set environment variables without hardcoding them in source. Run `firebase functions:config:get` to verify before deploying.

Expected result: Your Cloud Function is deployed and returns a JSON response like `{"access_token": "1000.abc123..."}` when called from a browser or Postman. You have the HTTPS function URL ready to paste into FlutterFlow.

3

Create the Zoho Mail API Group in FlutterFlow

In FlutterFlow, click 'API Calls' in the left navigation sidebar. Click the '+' button (or '+ Add') and select 'Create API Group'. Name it something clear like 'Zoho Mail' and set the base URL to your mail region — for US accounts this is `https://mail.zoho.com/api` and for EU accounts `https://mail.zoho.eu/api`. Important: do NOT use zohoapis.com here — that domain is for Zoho CRM and Books, not Zoho Mail. Using the wrong domain will produce 404 errors on every call. With the API Group created, you now need two API Calls inside it. Click '+ Add' inside the Zoho Mail group to create the first call. Name it 'Get Accounts' and set the method to GET with the path `/accounts`. This endpoint returns the list of mail accounts associated with your Zoho login. You'll call this once on app start to get the numeric accountId needed for sending. Create a second API Call named 'Send Message', set method to POST, and path to `/accounts/[accountId]/messages` — you'll replace `[accountId]` with a Variable in a moment. Go to the Variables tab for this call and add a variable named `accountId` (String). Then update the path to use this variable: `/accounts/{{accountId}}/messages`. Add two more variables: `toAddress` (String), `subject` (String), and `content` (String). For the body of the Send Message call, switch to the Body tab, select JSON, and enter a template with your variables referenced using double curly brace syntax. For the Authorization header, do NOT add it to the API Group shared headers yet — you'll handle it dynamically per call using an App State variable. Go back to the API Group settings and add a shared header: `Content-Type` with value `application/json`. Do not add the Authorization header with a hardcoded token here.

api_call_config.json
1{
2 "Get Accounts": {
3 "method": "GET",
4 "path": "/accounts",
5 "headers": {
6 "Authorization": "Zoho-oauthtoken {{accessToken}}"
7 }
8 },
9 "Send Message": {
10 "method": "POST",
11 "path": "/accounts/{{accountId}}/messages",
12 "headers": {
13 "Authorization": "Zoho-oauthtoken {{accessToken}}"
14 },
15 "body": {
16 "fromAddress": "yourname@yourdomain.com",
17 "toAddress": "{{toAddress}}",
18 "subject": "{{subject}}",
19 "content": "{{content}}"
20 }
21 }
22}
23

Pro tip: The Authorization header prefix MUST be `Zoho-oauthtoken` with a capital Z and lowercase o — not `Bearer`. Using `Bearer` silently fails with a 401 even if your token is valid.

Expected result: The Zoho Mail API Group appears in your FlutterFlow API Calls list with two calls inside: 'Get Accounts' (GET) and 'Send Message' (POST with accountId, toAddress, subject, and content variables).

4

Fetch the access token and accountId on app launch

With the API Group set up, you need to wire the token-fetching flow so your app always has a valid access token and accountId before it tries to send email. The right place to do this is in the app's initial action — either on the Main page's On Page Load action, or in your app's first authenticated screen. First, create two App State variables. Go to App Values in the left sidebar, then App State, and add: `zohoAccessToken` (String, default empty) and `zohoAccountId` (String, default empty). These will hold the values once fetched. Next, create a separate FlutterFlow API Call (not in the Zoho Mail group) that calls your Firebase Cloud Function URL. This is a GET call to your deployed function URL, e.g., `https://us-central1-yourproject.cloudfunctions.net/getZohoMailToken`. It returns `{"access_token": "..."}`. In the Response & Test tab, paste a sample response and generate JSON paths. The path `$.access_token` should auto-generate. This is not Zoho's API — it's your own proxy, so it can use any auth scheme you want (or none, since Firebase can validate the caller via App Check). On your app's entry screen, open the Action Flow Editor (click the screen → Actions panel → + Add Action → Backend/API Calls → API Request). Chain two actions: first, call 'Get Token' (your Cloud Function call) and update App State `zohoAccessToken` with `$.access_token`. Second, immediately after, call 'Get Accounts' (the Zoho Mail API Call), passing `{{zohoAccessToken}}` as the `accessToken` variable. Parse the response JSON Path `$.data[0].accountId` and update App State `zohoAccountId` with the result. This two-step chain runs once when the app loads, giving you both the token and the accountId stored in App State, ready to use in any send action.

Pro tip: App State variables in FlutterFlow are in-memory for the session. If your app is backgrounded for more than an hour, the access token will expire. Add a check: before calling Send Message, compare the token fetch timestamp (store it in App State) against the current time. If more than 55 minutes have passed, re-fetch the token.

Expected result: On app launch, the Action Flow calls the Cloud Function, sets zohoAccessToken in App State, then calls Get Accounts and sets zohoAccountId. You can verify this by adding a Text widget temporarily that displays both App State values.

5

Build the email send form and wire the Send Message action

Now build the UI for sending an email. For a confirmation email after a form submission, you typically already have the recipient's email address from a TextFormField and the subject/content can be partially templated. Create a form page or add a form to an existing screen. You need at minimum a TextFormField for the recipient address and whatever input fields drive the email content. Add a Button widget at the bottom of the form (e.g., 'Send Confirmation' or 'Submit'). Open the Action Flow Editor for this button. Add an action: Backend/API Calls → API Request → select your 'Zoho Mail' group → 'Send Message'. You'll now map each variable: - `accessToken` → App State → `zohoAccessToken` - `accountId` → App State → `zohoAccountId` - `toAddress` → the TextFormField value for recipient email - `subject` → a string literal like 'Your booking is confirmed' or a template using other form values - `content` → a multi-line string built from your form fields (name, date, time, etc.) After the Send Message action, add a Show Snack Bar action with a success message like 'Email sent successfully!' For error handling, use an If/Else conditional checking the API response status code: if it is not 200/201, show an error snack bar. If your app sends emails frequently (more than roughly every 2 seconds sustained), be aware that Zoho Mail's API has a rate limit of approximately 30 requests per minute before triggering a temporary lock. For single-send use cases like form confirmations, this limit is very unlikely to be hit. If you build a bulk-send feature, add a delay between sends. For the `fromAddress` field in the API body, use the email address associated with your Zoho Mail account. You can also make this a FlutterFlow App Constant (App Values → App Constants) so it's set once rather than repeated in every action.

Pro tip: Test the Send Message call first in the FlutterFlow API Call's 'Response & Test' tab with hardcoded values. If you get a 403, your OAuth scope is missing ZohoMail.messages.CREATE. If you get a 404, your domain is wrong (check that you're using mail.zoho.com, not zohoapis.com).

Expected result: Tapping the button in Run mode triggers the Send Message API Call. The recipient's email address receives the message within seconds, and a success snack bar appears in the app.

6

Handle token expiry and the 30 req/min rate limit

Zoho access tokens expire after 3,600 seconds (one hour). In a typical mobile app where users open the app, perform a task, and close it, this is rarely an issue — the token fetched on launch is still valid for the entire session. However, if your app has long-lived sessions or background processes, you need to handle 401 responses gracefully. In the Action Flow Editor, after each Zoho Mail API Call, add a conditional action that checks the HTTP response code. Use the API response's `statusCode` output variable. If `statusCode == 401`, fire a second chain: call your Cloud Function again to get a fresh token, update App State `zohoAccessToken`, then retry the original action. This creates a clean refresh-then-retry pattern without bothering the user. For the rate limit, Zoho Mail allows approximately 30 API requests per minute. The API returns a 4xx or a temporary lock response when this is exceeded. For normal usage — one email per user action — you'll never approach this limit. If you're building a feature that could trigger multiple sends quickly (e.g., a 'resend' button a user might tap repeatedly), disable the button for a few seconds after each tap using a local page state variable toggled by a Timer action. Also note that Zoho Mail has per-day sending limits that vary by plan. The free tier (up to 5 users) has lower daily limits than Mail Lite and higher plans — check your current plan's limits in your Zoho Mail admin dashboard. These per-day limits apply to actual email messages sent, not API calls generally. For EU accounts specifically, double-check that both your token URL and your API base URL use the `.eu` suffix. Mixing account domain (`accounts.zoho.com`) with the EU API domain (`mail.zoho.eu`) causes 401 errors that are hard to diagnose. The Cloud Function should be configured with the correct region from the start.

Pro tip: If you'd rather skip building and maintaining the Cloud Function, RapidDev's team builds FlutterFlow integrations like this every week — free scoping call at rapidevelopers.com/contact.

Expected result: Your app gracefully refreshes the access token if it expires during a session, and the send button is protected against rapid repeated taps. Your integration handles errors with user-friendly messages rather than silent failures.

Common use cases

Service booking app that sends confirmation emails

A FlutterFlow app for a small services business (cleaning, consulting, repairs) allows customers to book appointments through a form. After the customer taps 'Confirm Booking', the app calls a Zoho Mail API endpoint to send a formatted confirmation email with date, time, and service details. No third-party email platform subscription is needed — Zoho Mail's free tier handles the volume for most small businesses.

FlutterFlow Prompt

Build a booking confirmation screen that, on form submit, sends an email via Zoho Mail to the customer's address with the booking details in the body.

Copy this prompt to try it in FlutterFlow

Lead capture form with instant email notification to the sales team

A mobile marketing or event app captures leads through a multi-step form. When a lead submits, the FlutterFlow app uses the Zoho Mail API to send a real-time notification email to the internal sales team with the lead's name, company, and contact details. The sales rep gets an instant mobile-friendly email and can follow up immediately, without waiting for a daily CRM digest.

FlutterFlow Prompt

Add a lead capture form that POSTs the submitted details to a Cloud Function, which sends a Zoho Mail notification to sales@yourcompany.com.

Copy this prompt to try it in FlutterFlow

Support ticket acknowledgement in a customer service app

A customer-facing support app lets users submit tickets through a FlutterFlow form. After submission, the app sends an automated acknowledgement email via Zoho Mail — including a ticket reference number stored in Supabase — so customers know their request was received. This replaces a dedicated helpdesk platform's email triggers for apps that need a lightweight touch.

FlutterFlow Prompt

When a support form is submitted, call the Zoho Mail API to send an acknowledgement email to the user with their ticket ID and expected response time.

Copy this prompt to try it in FlutterFlow

Troubleshooting

404 Not Found when calling Zoho Mail API

Cause: Using zohoapis.com as the base URL instead of mail.zoho.com. The Zoho Mail API lives on a separate domain from Zoho CRM and Zoho Books.

Solution: In your FlutterFlow API Group, change the base URL from `https://www.zohoapis.com/...` to `https://mail.zoho.com/api` (or `https://mail.zoho.eu/api` for EU accounts). This is the most common setup mistake when developers have previously worked with Zoho CRM or Books.

401 Unauthorized — token appears valid but calls still fail

Cause: The Authorization header prefix is wrong. Using `Bearer` instead of `Zoho-oauthtoken` silently fails authentication with Zoho's API.

Solution: In your FlutterFlow API Call headers, change `Authorization: Bearer {{accessToken}}` to `Authorization: Zoho-oauthtoken {{accessToken}}`. The prefix is `Zoho-oauthtoken` — capital Z, lowercase o, no space before the token. Verify this in the API Call → Variables tab and in the shared group headers.

403 Forbidden when calling POST accounts/{accountId}/messages

Cause: The Zoho OAuth self-client was created without the `ZohoMail.messages.CREATE` scope, or the grant token was generated without it.

Solution: Go back to the Zoho API Console, navigate to your self-client, and generate a new grant code with both `ZohoMail.accounts.READ` and `ZohoMail.messages.CREATE` in the scope field. Exchange the new grant code for a new refresh token, update your Firebase Cloud Function environment config with the new refresh token, and redeploy. Scopes cannot be added to an existing refresh token — you must generate a new one.

accountId is empty or null when Send Message fires

Cause: The On Page Load action sequence ran before the Cloud Function returned the token, so Get Accounts fired with an empty token and the accountId was never set in App State.

Solution: Ensure the actions are chained with 'Wait for Response' — each action should only start after the previous one completes. In the Action Flow Editor, verify the chain order: (1) Call Cloud Function → update zohoAccessToken → (2) Call Get Accounts with the new token → parse `$.data[0].accountId` → update zohoAccountId. Both steps must use 'Wait for Response: Yes'. If using conditional actions, add a null-check before Send Message: if `zohoAccountId == ''`, re-run the initialization chain first.

XMLHttpRequest error on web builds when calling the Zoho Mail API

Cause: Browser-enforced CORS blocks direct calls from a FlutterFlow web app to mail.zoho.com. Native iOS/Android builds are not affected, but FlutterFlow's web preview (Run mode) and any published web app will hit this.

Solution: Route all Zoho Mail API calls through your Firebase Cloud Function proxy rather than calling mail.zoho.com directly from FlutterFlow. The Cloud Function runs server-side and is not subject to browser CORS restrictions. Set CORS response headers in the Cloud Function (`Access-Control-Allow-Origin: *`) to allow the FlutterFlow web build to reach your function URL.

Best practices

  • Always use mail.zoho.com (not zohoapis.com) as the API domain — these are separate Zoho services with separate endpoints.
  • Keep the Zoho refresh token exclusively in your Firebase Cloud Function environment config or Secret Manager — never paste it into FlutterFlow, App Constants, or source code.
  • Fetch the accountId once on app start and cache it in App State instead of calling GET /accounts before every send — the accountId never changes for a given login.
  • Use the exact header prefix `Zoho-oauthtoken` (not `Bearer`) — this is non-standard and easy to get wrong after working with other REST APIs.
  • Match your region domains consistently: if your Zoho account is EU, use accounts.zoho.eu for token refresh AND mail.zoho.eu for the Mail API — mixing US and EU domains causes 401s.
  • Add a 55-minute token expiry check in App State (store the fetch timestamp) and proactively refresh the token instead of relying solely on 401 recovery.
  • Disable send buttons after tap for at least 2-3 seconds to prevent accidental double-sends and protect against approaching the ~30 requests/minute rate limit.
  • Scope the OAuth grant narrowly — request only `ZohoMail.accounts.READ` and `ZohoMail.messages.CREATE`, not broad ZohoMail scopes, to minimize the impact of any credential leak.

Alternatives

Frequently asked questions

Can I use Zoho Mail to send bulk or marketing emails from my FlutterFlow app?

Zoho Mail is designed for business email and transactional messages, not bulk marketing campaigns. The API rate limits (~30 requests/minute) and daily sending limits on free and lower-tier plans make it a poor fit for mass emailing. For marketing email, use Zoho Campaigns (a separate Zoho product) or a dedicated platform like SendGrid or Mailgun. Zoho Mail's FlutterFlow integration works best for one-at-a-time transactional sends like booking confirmations, support acknowledgements, and notifications.

Why do I need a Firebase Cloud Function — can't I just call the Zoho Mail API directly?

You can call it directly in a prototype, but you should not do so in a published app. FlutterFlow compiles to a Flutter app that runs on the user's device. Any API key, OAuth token, or credential placed in an API Call header or Dart code is compiled into the app binary and can be extracted by anyone who decompiles it. The refresh token for Zoho Mail is a permanent credential — if it's extracted, an attacker can send emails from your account indefinitely. The Firebase Cloud Function acts as a secure server-side intermediary that holds the secret and only exposes a limited interface.

Does this integration work with FlutterFlow's web builds, or only mobile?

Both mobile and web builds work, but web builds have an additional requirement: browsers enforce CORS (Cross-Origin Resource Sharing) and will block direct API calls to mail.zoho.com from your web app's domain. When all Zoho Mail calls are routed through your Firebase Cloud Function (which sets the appropriate CORS headers), web builds work correctly. Native iOS and Android builds do not enforce CORS and would work with direct calls in a prototype, but the security model still requires the Cloud Function for any app you ship to users.

My Zoho account is in Europe — do I need to change anything?

Yes. EU-hosted Zoho accounts use different API domains. For token refresh, the URL is `accounts.zoho.eu` instead of `accounts.zoho.com`. For the Mail API, the base URL is `https://mail.zoho.eu/api` instead of `https://mail.zoho.com/api`. You must use matching regional domains — using accounts.zoho.com to refresh a token for an EU account will fail. Update your Cloud Function's region variable and your FlutterFlow API Group base URL accordingly. If you're unsure which region your account is in, log in to mail.zoho.eu — if it shows your inbox, you're on the EU data center.

How do I find my Zoho Mail accountId?

The accountId is returned by the GET /accounts endpoint — it's a numeric value in the `$.data[0].accountId` field of the response. You should call this endpoint once when the app launches (after fetching the access token), store the result in an App State variable, and reuse it throughout the session. The accountId is stable and won't change between sessions, so you could even hardcode it after finding it once — but storing it dynamically from the API is more robust if you ever switch Zoho Mail accounts.

What happens if I exceed the Zoho Mail API rate limit?

Zoho Mail enforces approximately 30 API requests per minute. If you exceed this, the API returns an error response and temporarily locks further requests. For normal single-send use cases in a FlutterFlow app, you're extremely unlikely to hit this limit. If you have a feature where many users might send emails simultaneously (for example, a 'notify team' button in a collaboration app), consider adding server-side rate limiting in your Cloud Function using a simple in-memory counter or a Firestore-based throttle. Disabling the send button in FlutterFlow for a few seconds after each tap also helps prevent individual users from triggering multiple rapid sends.

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.