Connect FlutterFlow to Amplitude by creating a FlutterFlow API Call that POSTs events to Amplitude's HTTP V2 endpoint at /2/httpapi. Store your public ingestion API key in App Constants, build a JSON body with the events array, and fire it from Action Flows. EU-region orgs must change the base URL to api.eu.amplitude.com or events will silently vanish despite a 200 response.
| Fact | Value |
|---|---|
| Tool | Amplitude |
| Category | Analytics |
| Method | FlutterFlow API Call |
| Difficulty | Intermediate |
| Time required | 35 minutes |
| Last updated | July 2026 |
Why Add Amplitude to Your FlutterFlow App?
FlutterFlow ships with Firebase Analytics for basic event logging, but Amplitude brings a different analytical lens: deep retention analysis, behavioral cohorts you can action with targeted messaging, and a conversion funnel builder that surfaces exactly which steps lose the most users. Teams that need to make product decisions from user-level data — not just aggregate session counts — typically reach for Amplitude.
Amplitude's HTTP V2 ingestion endpoint (`/2/httpapi`) is one of the cleanest analytics APIs to consume from a FlutterFlow API Call. Unlike some other vendors, it accepts a plain JSON body with an `api_key` field and an `events` array — no base64 encoding required. Each event object needs at minimum a `user_id` or `device_id` (or both), an `event_type` string, and optionally a `time` epoch millisecond timestamp. This makes the body straightforward to construct using FlutterFlow's JSON body editor.
Amplitude splits its auth into two tiers: the **API key** (an ingestion-only public key, safe to store in FlutterFlow's App Constants) and the **API key + secret key** pair (required for the Dashboard REST API and Data Export API — server-side only). The most common integration mistake — and the one that causes the most silent failures — is building for a US-region Amplitude project and then deploying to an EU-region project without changing the base URL from `api2.amplitude.com` to `api.eu.amplitude.com`. The API returns 200 both ways, but EU-region events sent to the US host simply vanish.
Integration method
FlutterFlow sends events to Amplitude using a POST request to Amplitude's HTTP V2 endpoint (/2/httpapi) via a FlutterFlow API Group. The ingestion API key is public and safe in App Constants. For EU-region Amplitude projects, the base URL must be changed to the EU host or events will not be stored. A Custom Action wrapping the amplitude_flutter SDK adds offline queueing and automatic session tracking.
Prerequisites
- A FlutterFlow project on any paid plan with Test Mode or a published app available for testing
- An Amplitude account with a project created — the free Starter plan supports the HTTP V2 API
- Your Amplitude project's API key (from Settings → Projects → your project → General) — confirm whether your project is in US or EU data residency
- Basic understanding of FlutterFlow's API Calls panel and Action Flow Editor
- If using the amplitude_flutter Custom Action path: FlutterFlow Basic plan or above for Custom Code access
Step-by-step guide
Create an Amplitude project and confirm your data region
Open amplitude.com and sign in or create an account. Click 'Create Project' from the home screen, give it a name, and — critically — pay attention to the 'Data Residency' selector. Amplitude offers US data residency (the default) and EU data residency (for GDPR-focused organizations). This setting determines your base URL for all subsequent API calls and cannot be changed after project creation without migrating data. If you select EU, your ingestion URL is `https://api.eu.amplitude.com/2/httpapi`. If US, it is `https://api2.amplitude.com/2/httpapi`. After creating the project, go to Settings (gear icon) → Projects → click your project → General tab. Copy the 'API Key' field — this is the ingestion-only public key. Also note the 'Secret Key' below it — that credential is only for the Dashboard REST API and Export API, which must be called from a server, never from FlutterFlow. Keep the Amplitude project open in a separate tab so you can use User Look-Up for testing in Step 5.
Pro tip: If you are unsure of your project's region, go to Settings → Organization → Data Residency. EU projects show 'Europe' there. Getting this wrong means events appear to succeed (HTTP 200) but never show up in your dashboards.
Expected result: You have the Amplitude API key copied and you know your project's data residency region (US or EU).
Add the API key to FlutterFlow App Constants
In FlutterFlow, click the gear icon in the bottom-left navigation → 'App Constants'. Click '+ Add Constant', name it `amplitudeApiKey`, set the type to String, and paste your Amplitude API key. Click Save. This is an ingestion-only key — it can write events into your project but cannot read data, export cohorts, or query dashboards. It is safe to bundle in your compiled app just as a publishable Stripe key or a Mapbox API key would be. The Amplitude secret key is a completely different credential: it enables read access to your analytics data and must never leave your server environment. If you need EU data residency, also store the base URL string as a second constant named `amplitudeBaseUrl` with value `https://api.eu.amplitude.com` — this makes it easy to switch environments without hunting through API Call configurations.
Pro tip: Create a separate Amplitude project for development and production, then store the corresponding API keys in different FlutterFlow projects or switch them via App Constants when building release vs debug variants.
Expected result: The `amplitudeApiKey` constant (and optionally `amplitudeBaseUrl`) appears in the App Constants list.
Create the Amplitude API Group and HTTP V2 POST call
Click 'API Calls' in the FlutterFlow left nav → '+ Add' → 'Create API Group'. Name the group 'Amplitude'. For the base URL: if you are a US-region project, enter `https://api2.amplitude.com`; if EU, enter `https://api.eu.amplitude.com`. Click 'Add Call', name it 'Track Event'. Set the method to POST and the endpoint path to `/2/httpapi`. Under the Headers tab, add: `Content-Type` = `application/json`. Under the Body tab, select 'JSON' and enter the body structure below as raw JSON. Use FlutterFlow's variable syntax `{{variableName}}` for the fields that will vary per call. Add these variables in the Variables tab: `apiKey` (String), `userId` (String), `eventType` (String), and `eventTime` (integer — milliseconds since epoch). In the body, set `api_key` to `{{apiKey}}`, and inside the events array set `user_id` to `{{userId}}`, `event_type` to `{{eventType}}`, and `time` to `{{eventTime}}`. Under Response & Test, click 'Test API Call' using your actual API key and a test user ID to verify the call returns HTTP 200 with body `{"code":200}`. Save the API group.
1{2 "api_key": "{{apiKey}}",3 "events": [4 {5 "user_id": "{{userId}}",6 "event_type": "{{eventType}}",7 "time": "{{eventTime}}",8 "event_properties": {}9 }10 ]11}Pro tip: Amplitude's HTTP V2 endpoint accepts arrays of multiple events in a single call — you can batch events rather than firing one API Call per event if you are tracking high-frequency interactions.
Expected result: The Amplitude API Group appears in your API Calls panel with a Track Event POST call, and the Test Response shows HTTP 200 with body {"code":200}.
Trigger events from Action Flows with the user ID
Now connect the Amplitude API Call to actual user interactions. Select a widget — for example, the main action button on your most important screen — and open the Actions panel on the right. Click '+ Add Action' → 'Backend/API Call' → Amplitude → Track Event. You will be asked to provide values for each variable defined in Step 3. Map them as follows: `apiKey` → App Constants → amplitudeApiKey; `userId` → App State → currentUserUid (if using FlutterFlow auth) or your own user ID state variable; `eventType` → set an inline value like `'button_tapped'` or a variable if the event name varies; `eventTime` → use a Custom Function that returns `DateTime.now().millisecondsSinceEpoch` as an integer. For the `userId` vs `device_id` distinction: Amplitude requires at least one identifier per event. For logged-in users, `user_id` is your authenticated user UID. For anonymous/pre-login users, you must supply a `device_id` instead. If you later log the user in, Amplitude automatically merges their pre-login `device_id` events with their post-login `user_id` events. To get a stable `device_id`, add a Custom Action using the `device_info_plus` pub.dev package to read the device's persistent identifier. For page-view events, click on the page background (not any widget), go to Actions → On Page Load, and add the same Amplitude Track Event call with event_type set to `'screen_viewed'` and an additional custom property for the screen name. Add the action to every key page in your app.
1// FlutterFlow Custom Function: getCurrentTimeMs2// Return type: int3// No arguments needed45int getCurrentTimeMs() {6 return DateTime.now().millisecondsSinceEpoch;7}Pro tip: Add a `platform` event property (e.g., 'iOS', 'Android', 'Web') to every event by detecting the platform in a Custom Function using `dart:io`. This helps Amplitude's segmentation when you have users across multiple platforms.
Expected result: Tapping the button or loading the page fires the Amplitude API Call. The Response panel in the Action shows HTTP 200.
Verify in Amplitude's User Look-Up and handle the EU URL gotcha
Open your Amplitude project → click 'User Look-Up' in the left sidebar. Enter the `user_id` you used in your test (your authenticated UID or the test value you hardcoded). If the event was ingested successfully, you will see a timeline of events for that user within a few seconds to a minute. If User Look-Up shows 'No users found', go to Data → Ingestion Debugger (available in some plans) or check the Ingestion Monitor under Settings. Common reasons for zero events despite 200 responses: (1) you are sending to the US host but your project is in the EU region — check Settings → Data Residency and update the API Group base URL accordingly; (2) the event is missing both `user_id` and `device_id` — Amplitude returns a 400 `missing_field` error in the response body that you may have missed; (3) there is a property type mismatch (e.g., `time` sent as a string instead of a number). To protect the secret key for Dashboard/Export API calls: never add the Amplitude secret key to an API Call header or App Constant. If your app needs to pull cohort data or query the Dashboard REST API, build a Firebase Cloud Function that uses the secret key server-side and expose a simple endpoint to your FlutterFlow app. The FlutterFlow API Call targets your function URL, not Amplitude's API directly.
Pro tip: Amplitude's Ingestion Debugger (under Settings → Data → Ingestion Monitor) shows you rejected events and the reason for rejection in near real-time — much faster than waiting for events to appear in the Events chart.
Expected result: User Look-Up shows your test user with the events you fired from FlutterFlow, confirming end-to-end ingestion.
(Optional) Wrap amplitude_flutter SDK in a Custom Action for richer tracking
The API Call approach in Steps 1-5 gives you reliable event ingestion but lacks some features that the native Amplitude Flutter SDK provides out of the box: automatic session tracking (start/end of app sessions), offline event queuing (events captured when there is no network are sent when connectivity returns), and automatic device properties (OS version, device model, carrier). If your analytics requirements need these, you can wrap the `amplitude_flutter` pub.dev package in a FlutterFlow Custom Action. Click 'Custom Code' in the left nav → '+ Add' → 'Action'. Name it `initAmplitude`. In the Dependencies field, add `amplitude_flutter: ^3.16.0` (check pub.dev for the latest version). Add an argument `apiKey` (String). Paste the init code below. Add a second Custom Action named `trackAmplitudeEvent` with arguments `eventName` (String) and `userId` (String), containing the track code. Important: Custom Actions do not run in the FlutterFlow browser Run mode. You must test them by deploying to a real device via APK or using FlutterFlow's Local Run feature. Call `initAmplitude` once from your App's initialization action (on the first page's On Page Load, guarded by a 'first launch' check). Then call `trackAmplitudeEvent` wherever you previously called the API Call.
1// Custom Action: initAmplitude2// Argument: apiKey (String)3import 'package:amplitude_flutter/amplitude.dart';4import 'package:amplitude_flutter/identify.dart';56Future initAmplitude(String apiKey) async {7 final Amplitude amplitude = Amplitude.getInstance();8 await amplitude.init(apiKey);9 await amplitude.trackingSessionEvents(true);10 amplitude.setUserId(currentUserUid); // set from App State11}1213// Custom Action: trackAmplitudeEvent14// Arguments: eventName (String)15Future trackAmplitudeEvent(String eventName) async {16 final Amplitude amplitude = Amplitude.getInstance();17 await amplitude.logEvent(eventName);18}Pro tip: The `amplitude_flutter` SDK stores events in a local queue and retries sending them when the network is available — this is the main advantage over the bare API Call approach for mobile-first apps with unreliable connectivity.
Expected result: The Custom Action compiles, and after running on a real device, events appear in Amplitude's User Look-Up with automatic session start/end events alongside your custom events.
Common use cases
Mobile subscription app tracking trial-to-paid conversion
A FlutterFlow subscription app fires Amplitude events at each conversion touchpoint: 'Trial Started', 'Feature Activated', 'Paywall Viewed', and 'Subscription Started'. The event properties include the user's plan tier and feature usage count. Amplitude's funnel chart reveals which feature activations correlate most strongly with paid conversion.
Track when a user views the paywall screen — send an Amplitude event called 'paywall_viewed' with properties: user_id, trial_day_number, features_used, and the specific plan shown.
Copy this prompt to try it in FlutterFlow
EdTech app measuring lesson completion rates by cohort
A FlutterFlow learning app sends 'Lesson Started' and 'Lesson Completed' events with the lesson ID, subject, and difficulty level. Amplitude's retention analysis compares completion rates by the user's signup cohort, revealing whether a recent UX change improved lesson follow-through.
When a user finishes a lesson, send an Amplitude event 'lesson_completed' with user_id, lesson_id, subject, difficulty, and time_spent_seconds as event properties.
Copy this prompt to try it in FlutterFlow
B2B SaaS tool tracking team-level feature adoption
A team productivity app built in FlutterFlow fires Amplitude events with both a `user_id` (the individual) and a `groups` property (the team/organization). Amplitude's group analytics then surfaces which team types adopt which features, enabling account-level engagement analysis beyond individual-user behavior.
Fire an Amplitude event 'report_exported' with user_id, team_id as a group property, report_type, and file_format whenever a team member exports a report.
Copy this prompt to try it in FlutterFlow
Troubleshooting
Events return HTTP 200 but never appear in Amplitude dashboards or User Look-Up
Cause: The most common cause is a region mismatch: your Amplitude project is in the EU data residency region but your API Group base URL is still set to the US host (api2.amplitude.com). Amplitude returns 200 for both hosts even when the event is dropped.
Solution: Go to your Amplitude project Settings → Organization → Data Residency. If it says 'Europe', update the API Group base URL in FlutterFlow from `https://api2.amplitude.com` to `https://api.eu.amplitude.com`. Save and re-test. If the region is US, check the response body for the `missing_field` error — this means both `user_id` and `device_id` are absent or empty.
400 Bad Request with body {"code":400,"error":"missing_field","missing_with_error_keys":["user_id","device_id"]}
Cause: Amplitude requires at least one of `user_id` or `device_id` in every event object. If both variables are empty strings or null when the API Call fires, Amplitude rejects the event.
Solution: Add a condition to your Action Flow: before calling the Amplitude API, check that `currentUserUid` is not empty. For unauthenticated users, generate and persist a UUID using a Custom Action with the `uuid` package, and pass it as `device_id` instead of `user_id`. Never leave both fields blank.
XMLHttpRequest error on FlutterFlow web build — events work in Test Mode but fail after publishing
Cause: Flutter Web runs in a browser that enforces CORS. Amplitude's /2/httpapi endpoint may block preflight requests from your app's origin.
Solution: Enable FlutterFlow's built-in API proxy for the Amplitude group (API Calls → Amplitude group → toggle 'Use FlutterFlow Proxy'). This routes requests through FlutterFlow's servers, bypassing the browser's CORS restriction. Alternatively, use a Firebase Cloud Function as the intermediary. Native iOS/Android builds are not affected by CORS.
401 Unauthorized when calling Amplitude's Dashboard REST API or Export API
Cause: The Dashboard REST API and Export API require both the API key and the secret key, sent via HTTP Basic Auth. The /2/httpapi ingestion endpoint only needs the API key in the JSON body. If you attempt to call the Dashboard API from FlutterFlow using only the API key, you will receive 401.
Solution: Build a Firebase Cloud Function that authenticates to the Amplitude Dashboard REST API using both the API key and secret key (stored as Firebase environment variables). Your FlutterFlow API Call targets the Cloud Function URL, not Amplitude directly. Never put the secret key in FlutterFlow's API Call headers or App Constants.
Best practices
- Confirm your project's data residency region before writing a single line of configuration — EU projects silently drop events sent to the US endpoint even though the API returns 200.
- Store only the Amplitude ingestion API key in App Constants. The secret key belongs exclusively in a Firebase Cloud Function for server-side Dashboard/Export API calls.
- Always supply either `user_id` (authenticated users) or `device_id` (anonymous users) on every event. Missing both causes silent 400 rejections that look like successes from the HTTP status code alone.
- Use consistent, lowercase event names with underscores (e.g., `screen_viewed`, `button_tapped`, `purchase_completed`). Amplitude's taxonomy is case-sensitive and inconsistent naming creates duplicate event types that clutter your event list.
- Batch multiple events in a single /2/httpapi call when tracking high-frequency interactions — this reduces API Call count, improves battery life on mobile, and is more resilient under intermittent connectivity.
- For web builds, enable FlutterFlow's built-in API proxy on the Amplitude group to prevent CORS failures in published web apps.
- Use Amplitude's User Look-Up tool for testing individual event flows, and the Ingestion Monitor for bulk ingestion health — these are faster for debugging than waiting for events to appear in charts.
- If you need offline event queuing or automatic session tracking, use the `amplitude_flutter` SDK via a Custom Action rather than the bare API Call approach.
Alternatives
Mixpanel uses a base64-encoded form body for /track ingestion (more complex than Amplitude's plain JSON) but offers stronger funnel analysis and cohort-based messaging tooling.
Segment is a customer data platform that routes events to Amplitude and dozens of other tools simultaneously — ideal when you want one integration to populate multiple analytics products.
Google Analytics is natively built into FlutterFlow via Firebase, requiring no API Call setup — choose it when zero-configuration event logging is sufficient and you do not need user-level retention or cohort analysis.
Frequently asked questions
Can I send multiple events in one Amplitude API Call from FlutterFlow?
Yes. Amplitude's HTTP V2 endpoint accepts an `events` array that can contain multiple event objects in a single POST. In your FlutterFlow API Call body, you can add multiple items to the events array. This is useful for batching events that occur in rapid succession (e.g., tracking a multi-step onboarding flow) without firing multiple separate API Calls.
What is the difference between the Amplitude API key and the secret key?
The API key is an ingestion-only identifier — it can write events into your Amplitude project but cannot read, export, or query your data. It is safe to store in FlutterFlow's App Constants and bundle in your compiled app. The secret key is the complement to the API key for server-side access: it is required for the Dashboard REST API, the Export API, and cohort downloads. The secret key must never appear in FlutterFlow — keep it in a Firebase Cloud Function environment variable.
Why are my events showing HTTP 200 but not appearing in Amplitude?
The two most common causes are: (1) a data residency mismatch — you are posting to the US endpoint but your project is in EU data residency (or vice versa). Check Settings → Data Residency and update the base URL accordingly. (2) Missing user_id and device_id — every event requires at least one identifier. Check the actual response body (not just the status code); a 400 with `missing_field` indicates the identifier problem.
Does Amplitude work on both iOS/Android and FlutterFlow web builds?
The HTTP V2 API Call approach works on iOS, Android, and web builds. However, web builds published from FlutterFlow may encounter CORS restrictions because browsers enforce same-origin policies that native apps do not. Enable FlutterFlow's built-in proxy on the Amplitude API Group (toggle 'Use FlutterFlow Proxy') to route web requests through FlutterFlow's servers and avoid the CORS block. The `amplitude_flutter` SDK Custom Action approach also works on mobile but may have web renderer caveats.
Is there a free Amplitude plan that works with this integration?
Yes. Amplitude's Starter plan is free and includes access to the HTTP V2 ingestion endpoint, User Look-Up, and core analytics features including funnels and retention charts. The free plan has monthly tracked user limits — check amplitude.com/pricing for current limits. The integration described in this guide works fully on the free Starter plan.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation