Connect FlutterFlow to Campaign Monitor using FlutterFlow API Calls routed through a Firebase Cloud Function or Supabase Edge Function that applies Basic Auth (API key as username, any dummy string as password) to every request. Your app adds subscribers from in-app forms to Campaign Monitor lists and optionally reads campaign stats for a dashboard — the client/list ID workflow and the dummy-password Basic Auth quirk are the two details you must get right.
| Fact | Value |
|---|---|
| Tool | Campaign Monitor |
| Category | Marketing |
| Method | FlutterFlow API Call |
| Difficulty | Intermediate |
| Time required | 35 minutes |
| Last updated | July 2026 |
Add Subscribers and Read Campaign Stats from Your FlutterFlow App
Campaign Monitor (api.createsend.com) is designed for agencies managing email marketing for multiple clients — one Campaign Monitor account can hold several client sub-accounts, each with their own subscriber lists and campaigns. Its REST API (version 3.3, base URL `https://api.createsend.com/api/v3.3`) lets you add and update subscribers, retrieve list details, and pull campaign performance stats like open rates and click rates. For a FlutterFlow app, the typical use case is a newsletter sign-up form that automatically adds the user to a specific subscriber list without any manual export.
Campaign Monitor's auth model has a distinctive quirk: it uses HTTP Basic Auth where the username is your API key and the password is any non-empty dummy string — Campaign Monitor ignores the password but rejects the request if you send an empty one. The other structural detail is the multi-tenant model: every write call requires a client ID (identifying the client sub-account) and most list calls also require a list ID. Fetch these IDs first, before writing a single FlutterFlow binding.
Because the API key is account-wide, it must never appear in a FlutterFlow API Call header (which compiles into the app binary). Route it through a backend proxy that computes the Basic Auth header server-side. Campaign Monitor plans start from approximately $11/month — check current pricing at campaignmonitor.com/pricing. Rate limits are not publicly documented at a fixed number; verify them in Campaign Monitor's API docs.
Integration method
FlutterFlow API Calls point at a Firebase Cloud Function or Supabase Edge Function that holds the Campaign Monitor API key and applies it as Basic Auth — with the key as the username and any non-empty dummy string as the password. The proxy forwards authenticated requests to `https://api.createsend.com/api/v3.3`, handles the multi-client ID lookup, and returns clean data to FlutterFlow. This keeps the account-wide API key out of the compiled app binary.
Prerequisites
- A Campaign Monitor account with at least one client sub-account and one subscriber list
- Your Campaign Monitor API key (from Account Settings → API Keys) and your client ID and list ID
- A Firebase project (for Cloud Functions) or a Supabase project (for Edge Functions) connected to FlutterFlow
- A FlutterFlow project with a form screen ready to connect
- Basic familiarity with FlutterFlow's API Calls panel and Action Flow Editor
Step-by-step guide
Get your API key, client ID, and list ID from Campaign Monitor
Log into Campaign Monitor and navigate to your account name (top-right) → 'Account Settings' → 'API Keys'. Click 'Generate API Key' if you do not have one, then copy the key. This key is account-wide and must be stored securely in your backend — never in FlutterFlow. To find your client ID, you have two options. First, check the URL when you are viewing a specific client in the Campaign Monitor interface — the client ID appears in the URL path. Alternatively, make a direct API call: `GET https://api.createsend.com/api/v3.3/clients.json` with Basic Auth (your API key as username, the word `x` as the password) — this returns all your client sub-accounts with their IDs. To find your list ID, navigate to the client in Campaign Monitor → Lists & Subscribers → click the list you want → look at Settings or the URL for the list ID. You can also call `GET /api/v3.3/clients/{clientID}/lists.json` with Basic Auth to get all lists for a client. Note both the client ID and list ID you want to write subscribers to. These will be stored as environment variables in your backend so you can reference them without hardcoding them into your Cloud Function source code.
Pro tip: Campaign Monitor IDs are long alphanumeric strings (e.g., `a1b2c3d4e5f6...`). Copy them carefully, including all characters. A single wrong character will cause a 404 or an 'object not found' error from the API.
Expected result: You have your Campaign Monitor API key, the client ID for the target client sub-account, and the list ID for the subscriber list you want to write to.
Deploy a backend proxy that applies Basic Auth server-side
Create a Firebase Cloud Function (or Supabase Edge Function) that holds your Campaign Monitor API key and computes the Basic Auth header for every request. Basic Auth requires a header of the form `Authorization: Basic <base64(username:password)>`. For Campaign Monitor, this is `Authorization: Basic <base64(API_KEY:x)>` — where `x` (or any non-empty string) is the dummy password. In Node.js, you can compute this with `Buffer.from(API_KEY + ':x').toString('base64')`. Never hard-code the key in source code — store it using `firebase functions:config:set campaignmonitor.key=YOUR_KEY` and read it with `functions.config().campaignmonitor.key` at runtime. Expose at least two endpoints from your function: one for adding a subscriber (`POST /addSubscriber`) and one for reading campaign stats (`GET /getCampaigns`). For the subscriber endpoint, accept `{ email, name }` from FlutterFlow, then POST to `https://api.createsend.com/api/v3.3/subscribers/{listID}.json` with the body Campaign Monitor expects. For campaign stats, call `GET /api/v3.3/clients/{clientID}/campaigns.json`, then for each campaign ID call the summary endpoint to get open/click rates. Return clean JSON to FlutterFlow. Make sure every Cloud Function response includes the CORS header `Access-Control-Allow-Origin: *` and handles OPTIONS preflight requests, so FlutterFlow's web Test Mode can reach it.
1// Firebase Cloud Function — Campaign Monitor proxy (index.js)2const functions = require('firebase-functions');3const fetch = require('node-fetch');45const CM_KEY = functions.config().campaignmonitor.key;6const CM_LIST_ID = functions.config().campaignmonitor.list_id;7const CM_CLIENT_ID = functions.config().campaignmonitor.client_id;8const CM_BASE = 'https://api.createsend.com/api/v3.3';910// Campaign Monitor uses Basic Auth: API key as username, dummy string as password11const auth = 'Basic ' + Buffer.from(CM_KEY + ':x').toString('base64');12const headers = { 'Authorization': auth, 'Content-Type': 'application/json' };1314// Endpoint: add subscriber15exports.addSubscriber = functions.https.onRequest(async (req, res) => {16 res.set('Access-Control-Allow-Origin', '*');17 if (req.method === 'OPTIONS') return res.status(204).send('');1819 const { email, name } = req.body;20 if (!email) return res.status(400).json({ error: 'email required' });2122 const body = JSON.stringify({23 EmailAddress: email,24 Name: name || '',25 Resubscribe: true,26 ConsentToTrack: 'Yes'27 });2829 const r = await fetch(`${CM_BASE}/subscribers/${CM_LIST_ID}.json`, {30 method: 'POST',31 headers,32 body33 });3435 // 200/201 = success, 400 = already subscribed (treat as success)36 if (r.status === 200 || r.status === 201 || r.status === 400) {37 return res.json({ success: true });38 }39 const data = await r.json();40 res.status(r.status).json(data);41});4243// Endpoint: get campaigns list44exports.getCampaigns = functions.https.onRequest(async (req, res) => {45 res.set('Access-Control-Allow-Origin', '*');46 if (req.method === 'OPTIONS') return res.status(204).send('');4748 const r = await fetch(`${CM_BASE}/clients/${CM_CLIENT_ID}/campaigns.json`, { headers });49 const data = await r.json();50 res.json(data || []);51});52Pro tip: The `Resubscribe: true` field in the POST body means that if someone is already on the list but previously unsubscribed, Campaign Monitor will re-subscribe them. Remove this flag if you want unsubscribe decisions to be respected permanently.
Expected result: Two Cloud Function URLs are deployed and tested: POST to `/addSubscriber` adds a real subscriber to your Campaign Monitor list, and GET to `/getCampaigns` returns a list of campaigns.
Create a FlutterFlow API Call group for Campaign Monitor
In your FlutterFlow project, click 'API Calls' in the left navigation bar. Click '+ Add' and choose 'Create API Group'. Name it 'Campaign Monitor' and set the base URL to your Firebase Cloud Functions deployment root URL (e.g., `https://us-central1-your-project.cloudfunctions.net`). No Authorization header is needed — your backend proxy handles it. Click 'Save' to create the group. Add the first API Call: click '+ Add API Call', name it 'Add Subscriber', set method to POST, and endpoint to `/addSubscriber`. Go to the 'Variables' tab and add two variables: `subscriber_email` (String) and `subscriber_name` (String). In the 'Body' tab, set the body type to JSON and write: `{ "email": "{{subscriber_email}}", "name": "{{subscriber_name}}" }`. Switch to 'Response & Test', enter a real email address in the variables, click 'Test', and verify the subscriber appears in your Campaign Monitor list. Generate JSON Paths from the response. Add a second API Call named 'Get Campaigns', set method to GET, endpoint to `/getCampaigns`. Test it and generate JSON Paths — you should see paths like `$[0].Name`, `$[0].SentDate`, `$[0].UniqueOpens` appear after FlutterFlow parses the response. These paths are what you will bind to a List or Chart widget.
1// FlutterFlow API Call config reference2{3 "group_name": "Campaign Monitor",4 "base_url": "https://us-central1-YOUR_PROJECT.cloudfunctions.net",5 "api_calls": [6 {7 "name": "Add Subscriber",8 "method": "POST",9 "endpoint": "/addSubscriber",10 "body": { "email": "{{subscriber_email}}", "name": "{{subscriber_name}}" },11 "variables": [12 { "name": "subscriber_email", "type": "String" },13 { "name": "subscriber_name", "type": "String" }14 ]15 },16 {17 "name": "Get Campaigns",18 "method": "GET",19 "endpoint": "/getCampaigns"20 }21 ]22}23Pro tip: If the API Call Test returns a 401 even with the correct key, check that your Cloud Function is not stripping the Authorization header before forwarding. Log `req.headers` in the function briefly to confirm the header is arriving from FlutterFlow if you are calling a middleware-based function setup.
Expected result: Both API Calls test successfully. Add Subscriber creates a real Campaign Monitor subscriber and Get Campaigns returns the list of campaigns with JSON Paths generated.
Wire the newsletter sign-up form to the Add Subscriber call
Navigate to the screen in your FlutterFlow project that has your newsletter sign-up form. If it does not exist yet, add a Column containing a TextField for email (set the keyboard type to 'Email Address'), an optional TextField for the subscriber's name, and a Button labeled 'Subscribe'. Select the Subscribe button and open its Actions panel (the lightning bolt icon on the right side). Click '+ Add Action' → 'Backend/Database' → 'API Call'. Choose the 'Campaign Monitor' group and the 'Add Subscriber' call. For the `subscriber_email` variable, click the binding option and select 'Widget State → [your email TextField's controller]'. For `subscriber_name`, bind it to the name TextField, or leave it as an empty string literal if you only want email. After the API Call action, add a 'Show Snack Bar' action with a success message like 'You are subscribed!'. To handle errors gracefully, add a conditional action after the API Call that checks the API response status — if the status indicates an error, show a different snack bar message like 'Something went wrong, please try again'. For the case where a subscriber is already on the list, your Cloud Function treats a 400 response as success (because Campaign Monitor returns 400 for 'Email address is already a subscriber') and returns `{ success: true }`. This means FlutterFlow will show the same success message for new and existing subscribers — a good UX choice that avoids awkward 'already subscribed' error messages.
Pro tip: If you want to track which app screen a subscriber came from, add a custom field to your Campaign Monitor list (e.g., `Source`) and pass it in the API Call body. Update the Cloud Function to forward it in the subscriber POST as a `CustomFields` array: `[{ "Key": "Source", "Value": "FlutterFlow App" }]`.
Expected result: Tapping the Subscribe button adds the user's email to the Campaign Monitor list. Existing subscribers see the same success message rather than an error.
Add a campaigns stats screen and test end-to-end (optional)
If you want to display campaign performance analytics in your app (open rates, click rates, send counts), create a new screen in FlutterFlow. Add a ListView and configure its backend query: 'API Call → Campaign Monitor → Get Campaigns'. Inside each list item, add Text widgets bound to the campaign name (`$[0].Name`), send date (`$[0].SentDate`), and a numeric stat like unique open rate. For a visual chart, add a FlutterFlow Bar Chart or Pie Chart widget and configure its data series manually by creating a Custom Function or App State variable that extracts open/click rate values from the API response for each campaign. This requires a bit more setup but creates a compelling analytics view that your team can check on mobile without logging into Campaign Monitor. Before publishing, test the full flow on a real device or in FlutterFlow's Local Run: open the sign-up screen, enter an email, tap Subscribe, and verify the subscriber appears in Campaign Monitor. Then open the campaigns screen and confirm real campaign data loads. If you are using FlutterFlow's web Test Mode, confirm your Cloud Function has the CORS header set correctly — the common symptom of a missing CORS header in web preview is that the ListView shows 'No data' even though the API Call tests correctly in the Test tab. If you want help setting up the backend proxy or the multi-client ID workflow, RapidDev's team builds FlutterFlow integrations like this every week — free scoping call at rapidevelopers.com/contact.
Pro tip: Campaign Monitor sends confirmation emails if the list has double opt-in enabled. If your test subscribers are not appearing, check whether double opt-in is on for the list in Campaign Monitor's list settings — the subscriber will be in 'Unconfirmed' state until they click the confirmation link.
Expected result: The campaigns screen shows real Campaign Monitor campaign names and stats. The sign-up flow is tested end-to-end and subscribers appear in Campaign Monitor immediately after form submission.
Common use cases
Newsletter sign-up form inside a consumer mobile app
A lifestyle brand built their app in FlutterFlow and wants a 'Join our newsletter' screen. The user enters their email and optionally their name, taps Subscribe, and the app calls the backend proxy to add them to the Campaign Monitor list for that brand — no Zapier, no CSV exports.
Build a newsletter sign-up screen with an email field and a Subscribe button. When tapped, add the user to our Campaign Monitor subscriber list and show a confirmation message.
Copy this prompt to try it in FlutterFlow
Agency dashboard showing campaign performance for multiple clients
A digital marketing agency uses a FlutterFlow internal app to review email campaign performance across their client portfolio. The app lists all campaigns for a selected client, shows open rates and click rates in gauge or bar charts, and lets the account manager quickly compare the last three sends.
Create a dashboard screen where I can select a client, see their recent campaigns, and view open rate and click rate charts for each campaign.
Copy this prompt to try it in FlutterFlow
Event registration app that adds attendees to post-event email sequences
An events company uses FlutterFlow for event check-in and registration. When an attendee registers, the app adds them to a Campaign Monitor list tagged to that event so they automatically receive post-event follow-up emails from the existing Campaign Monitor automation.
After an attendee submits the registration form, add them to the Campaign Monitor list for this event with a custom field for the event name.
Copy this prompt to try it in FlutterFlow
Troubleshooting
401 Unauthorized from Campaign Monitor API
Cause: The Basic Auth header is malformed — either the API key is wrong, the base64 encoding is incorrect, or the dummy password was left empty.
Solution: Log the computed `auth` string in your Cloud Function temporarily (not in production). Verify the format is `Basic <base64(API_KEY:x)>` with a colon between the key and `x`. A common mistake is encoding just the API key without the colon and dummy password. Also confirm the API key is copied completely from Campaign Monitor Account Settings → API Keys.
1// Correct Basic Auth construction for Campaign Monitor2const auth = 'Basic ' + Buffer.from(CM_KEY + ':x').toString('base64');3// Wrong: Buffer.from(CM_KEY).toString('base64') — missing :x4Subscriber added to the wrong list or 'list not found' 404 error
Cause: The list ID in the Cloud Function environment variable is incorrect or belongs to a different client sub-account.
Solution: Call `GET /api/v3.3/clients/{clientID}/lists.json` from your Cloud Function (or a REST client like Postman) with the correct client ID and Basic Auth to retrieve all lists for that client. Find the correct list ID in the response and update your `CM_LIST_ID` function config variable. Redeploy after updating.
FlutterFlow ListView shows 'No data' in web Test Mode even though the API Call Test tab succeeds
Cause: The Cloud Function response is missing the `Access-Control-Allow-Origin` CORS header, causing the browser to block the response in FlutterFlow's web preview.
Solution: Add `res.set('Access-Control-Allow-Origin', '*')` at the top of each Cloud Function handler (before any early returns) and add an OPTIONS preflight handler that returns `res.status(204).send('')`. Redeploy the function and reload FlutterFlow. Native iOS and Android builds are not affected by CORS.
New subscribers appear in Campaign Monitor as 'Unconfirmed' status
Cause: The Campaign Monitor list has double opt-in enabled, requiring subscribers to confirm their email before being marked Active.
Solution: This is expected behavior for double opt-in lists. If you want subscribers to be active immediately (single opt-in), go to Campaign Monitor → Lists → your list → Settings and disable double opt-in. Note that enabling `Resubscribe: true` in the POST body does not bypass double opt-in — confirmation is still required.
Best practices
- Never put the Campaign Monitor API key in a FlutterFlow API Call header — it is account-wide, grants access to all client sub-accounts, and compiles into the app binary if placed there.
- Always include a non-empty dummy password string in the Basic Auth encoding — Campaign Monitor's API rejects requests with an empty password even though it ignores the password value itself.
- Look up client IDs and list IDs via the API first (not from memory or the UI URL) and store them in backend environment variables so you can change targets without redeploying code.
- Set `Resubscribe: true` in subscriber POST bodies only if you intentionally want to re-subscribe people who previously unsubscribed — remove it if you want unsubscribe preferences to be respected.
- Handle the 400 'already a subscriber' response gracefully in your Cloud Function by treating it as a success rather than an error, so your app's UX does not alarm users who are already subscribed.
- Add CORS headers to all Cloud Function responses (including error paths) so FlutterFlow's web Test Mode and web-target builds work correctly.
- Test double opt-in behavior in your Campaign Monitor list settings — if subscribers appear as 'Unconfirmed', either notify users in-app to check their email or disable double opt-in for the target list.
Alternatives
Choose SendGrid if your goal is transactional email (welcome messages, receipts, notifications triggered by user actions) rather than subscriber list management and marketing campaigns — SendGrid uses a Bearer token via Dynamic Templates and has a generous free tier of 100 emails/day.
Choose Constant Contact if you are building for a single-brand consumer audience rather than multi-client agency use — it uses OAuth-2 with token refresh for subscriber management and is more consumer-app oriented.
Choose Mailchimp if you need a larger free tier (up to 500 contacts and 1,000 sends/month for free), a more widely used brand that your end users will recognise, and extensive automation features.
Frequently asked questions
Why does Campaign Monitor's Basic Auth use a dummy password?
Campaign Monitor authenticates entirely on the API key (the username field in Basic Auth) and ignores the password entirely. However, the HTTP Basic Auth specification requires both a username and a password, and Campaign Monitor's servers reject requests where the password is empty. The convention is to pass any non-empty string — `x` is the most common choice. This is a Campaign Monitor-specific quirk and does not apply to most other APIs.
Can I put the Campaign Monitor API key directly in a FlutterFlow API Call header?
You should not. The Campaign Monitor API key is account-wide, meaning it grants access to all your client sub-accounts and subscriber lists. Placing it in a FlutterFlow API Call header compiles it into the iOS/Android app binary where it can be extracted. Always compute the Basic Auth header in a backend proxy (Firebase Cloud Function or Supabase Edge Function) that runs server-side.
What if I have multiple Campaign Monitor clients and want to show different lists in the app?
Store the client IDs you care about in your backend configuration and expose an API endpoint from your Cloud Function that accepts a `clientId` query parameter. The Cloud Function then calls Campaign Monitor's `GET /clients/{clientId}/lists.json` endpoint with the provided ID, returning that client's lists. In FlutterFlow, pass the selected client ID as a variable in the API Call.
Does FlutterFlow support Campaign Monitor natively?
No — unlike Firebase and Supabase, Campaign Monitor does not have a native FlutterFlow integration panel. The correct approach is FlutterFlow API Calls routed through a backend proxy. This is more flexible than a native panel because it lets you control exactly which endpoints you expose to the app and add server-side logic like the ID-to-label lookup or rate limiting.
What Campaign Monitor plan do I need for API access?
Campaign Monitor API access is available on all paid plans. The Basic plan starts from approximately $11/month. Check current pricing and plan limits at campaignmonitor.com/pricing, as pricing and included sends vary. Rate limits are not publicly documented at a fixed number — verify the current limits in Campaign Monitor's API documentation for your plan tier.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation