Connect Bubble to Firebase Cloud Messaging to send push notifications to your users' browsers and mobile devices. Because FCM's HTTP v1 API requires a short-lived OAuth 2.0 token derived from a Google service account, all notification sends must go through a Bubble Backend Workflow — which is a Starter-plan feature. The legacy static server key was deprecated in 2024 and no longer works.
| Fact | Value |
|---|---|
| Tool | Firebase Cloud Messaging |
| Category | Database & Backend |
| Method | Backend Workflow (Webhooks) |
| Difficulty | Advanced |
| Time required | 3–5 hours |
| Last updated | July 2026 |
Push notifications from Bubble: why FCM requires a Backend Workflow
Firebase Cloud Messaging is free, reliable, and supported on every major browser and mobile OS — but its modern HTTP v1 API is intentionally more complex than the deprecated legacy API. Google moved away from static server keys specifically to improve security: the service account private key (an RSA private key embedded in a JSON file) never leaves your server, and only short-lived OAuth 2.0 Bearer tokens (valid for 1 hour) are used in actual API calls. Bubble as a visual full-stack builder cannot perform RSA JWT signing inside a workflow step, so the integration pattern requires a small external helper — typically a Google Cloud Function, AWS Lambda, or Supabase Edge Function — that accepts a request from your Bubble Backend Workflow, generates the OAuth token using the stored service account JSON, and returns it (or forwards the full FCM send request). Device token collection, on the other hand, is purely client-side: the Firebase JS SDK interacts with the browser's Push API, asks for notification permission, and returns a unique FCM registration token for that device/browser pair. You store that token in Bubble's database linked to the user record. When a notification should be sent — triggered by a new order, a message, a payment confirmation — your Backend Workflow looks up the target user's stored FCM token, calls the helper endpoint (or chains the OAuth token fetch with the FCM send), and dispatches the notification. Because all service account credentials stay inside the helper endpoint's secure environment, no sensitive key ever touches Bubble's client-side code or appears in a user's browser DevTools.
Integration method
Bubble Backend Workflow calls the FCM HTTP v1 API with an OAuth 2.0 Bearer token obtained from a Google service account; token generation is handled by a small helper endpoint (Cloud Function or Lambda) because Bubble cannot perform RSA signing internally.
Prerequisites
- A Bubble app on a Starter plan or above — Backend Workflows (required for secure FCM sends) are not available on the free plan
- A Firebase project at console.firebase.google.com with Cloud Messaging enabled
- A Google service account with the 'Firebase Cloud Messaging Admin' role (or 'Firebase Admin SDK Administrator Service Agent'), with a downloaded private key JSON file
- A deployed helper endpoint (Google Cloud Function, AWS Lambda, Supabase Edge Function, or equivalent) capable of generating a Google OAuth 2.0 Bearer token from the service account JSON
- The Zeroqode Firebase plugin installed in your Bubble app (for client-side FCM device token registration), or willingness to load the Firebase JS SDK via a page-header Script tag
- A Bubble data type to store FCM device tokens linked to User records (e.g. 'User_FCM_Token' with fields: user (User), token (text), created_at (date))
Step-by-step guide
Step 1 — Create a Firebase project and download the service account key
Go to console.firebase.google.com and create a new project (or open an existing one). In the left sidebar, click the gear icon next to 'Project Overview' and select 'Project settings.' Click the 'Service accounts' tab. You will see a section titled 'Firebase Admin SDK.' Click 'Generate new private key' and confirm by clicking 'Generate key.' A JSON file will download to your computer — this file contains your project ID, client email, and the RSA private key. Keep this file secure: it grants admin access to your Firebase project. Do not commit it to GitHub or paste it into a Bubble form field. Also, from the same 'Project settings' page, click the 'General' tab and scroll to 'Your apps.' If you do not have a web app registered, click the web icon (</>) to add one. Copy the `firebaseConfig` object (apiKey, authDomain, projectId, storageBucket, messagingSenderId, appId) — you will need this for client-side token registration in Step 5. Finally, click 'Cloud Messaging' in the left sidebar to confirm Cloud Messaging is active for your project.
1// Sample service account JSON structure (do NOT put this in Bubble)2// Store in your helper endpoint's environment variables3{4 "type": "service_account",5 "project_id": "your-project-id",6 "private_key_id": "abc123...",7 "private_key": "-----BEGIN RSA PRIVATE KEY-----\n...",8 "client_email": "firebase-adminsdk-xxxxx@your-project-id.iam.gserviceaccount.com",9 "client_id": "123456789",10 "auth_uri": "https://accounts.google.com/o/oauth2/auth",11 "token_uri": "https://oauth2.googleapis.com/token"12}Pro tip: If you already have a Firebase project that uses the Zeroqode Firebase plugin for Firestore, you can reuse the same project. Just ensure you generate a SERVICE ACCOUNT key from the 'Service accounts' tab — this is different from the client-safe SDK config keys used in the plugin settings.
Expected result: You have a downloaded service account JSON file with a private_key field, and you have noted your Firebase project_id (e.g. 'my-app-a1b2c'). Cloud Messaging is enabled in the Firebase console.
Step 2 — Store service account credentials in Bubble Environment Variables
Open your Bubble app editor and click 'Settings' in the left sidebar, then select 'Environment Variables.' Click 'New variable' and create two entries. First: name it FIREBASE_PROJECT_ID and paste your project ID value (e.g. 'my-app-a1b2c') — this is not sensitive but convenient to store here. Second: name it FIREBASE_SA_CLIENT_EMAIL and paste the client_email from the service account JSON (e.g. 'firebase-adminsdk-xxxxx@my-app-a1b2c.iam.gserviceaccount.com'). Do NOT paste the private_key directly into a Bubble Environment Variable — Bubble's Environment Variables are not encrypted and can be seen by collaborators with editor access. Instead, the private key should stay inside your helper endpoint's secure environment (Lambda environment variables, Google Cloud Function secrets, Supabase Vault, etc.). What you are storing in Bubble is only the non-sensitive identifiers that your helper endpoint needs to be called (or for logging/tracking purposes). The helper endpoint should have the private_key stored in its own secret management system.
1// Bubble Settings → Environment Variables2// Variable 1:3Name: FIREBASE_PROJECT_ID4Value: your-project-id56// Variable 2:7Name: FIREBASE_SA_CLIENT_EMAIL8Value: firebase-adminsdk-xxxxx@your-project-id.iam.gserviceaccount.com910// Do NOT store private_key here — store it in your11// helper endpoint's secret manager (AWS Secrets Manager,12// GCP Secret Manager, Supabase Vault, etc.)Pro tip: Backend Workflow (server-side) actions in Bubble cannot directly read Environment Variables at runtime in the same way code can — Environment Variables in Bubble are primarily used for Bubble's own configuration. The helper endpoint approach means the private key never needs to be accessible in Bubble at all. Your Backend Workflow simply calls the helper endpoint URL with a shared secret (API key) to authorize the call.
Expected result: You have created Environment Variables in Bubble for your Firebase project identifier. Your helper endpoint (next step) has the full service account JSON or at minimum the private_key stored securely in its own secret manager.
Step 3 — Deploy a helper endpoint to generate OAuth tokens and send FCM notifications
Because Bubble cannot perform RSA JWT signing internally, you need a small server-side helper. The simplest option for Firebase projects is a Google Cloud Function (Node.js), which has access to the same Firebase project environment and can use the official 'firebase-admin' npm package. Alternatively, use a Supabase Edge Function, an AWS Lambda, or any Node.js endpoint on Railway or Render. The helper endpoint should: (1) accept an HTTP POST request with a JSON body containing `{deviceToken, title, body, data}` and an `x-api-key` header for caller authentication; (2) use the service account credentials (loaded from secure environment) to obtain a Google OAuth 2.0 Bearer token using the 'google-auth-library' or 'firebase-admin' package; (3) call `https://fcm.googleapis.com/v1/projects/{projectId}/messages:send` with the Bearer token; (4) return `{success: true, messageId: '...'}` or an error JSON. Deploy this endpoint and note its public HTTPS URL — this is what your Bubble Backend Workflow will call. Set a strong, random x-api-key value as an environment variable in the helper and save it somewhere secure; you will add it as a Private header in Bubble's API Connector in the next step.
1// Google Cloud Function (Node.js 20) — send-fcm-notification/index.js2// Deploy with: gcloud functions deploy sendFcmNotification --trigger-http34const admin = require('firebase-admin');56// Initialize once (cold start)7if (!admin.apps.length) {8 admin.initializeApp(); // auto-picks up credentials in Cloud Functions environment9}1011exports.sendFcmNotification = async (req, res) => {12 // Verify caller API key13 const apiKey = req.headers['x-api-key'];14 if (apiKey !== process.env.BUBBLE_FCM_SECRET) {15 return res.status(401).json({ error: 'Unauthorized' });16 }1718 const { deviceToken, title, body, data } = req.body;1920 if (!deviceToken || !title) {21 return res.status(400).json({ error: 'Missing deviceToken or title' });22 }2324 try {25 const message = {26 token: deviceToken,27 notification: { title, body: body || '' },28 data: data || {}29 };3031 const messageId = await admin.messaging().send(message);32 return res.status(200).json({ success: true, messageId });33 } catch (error) {34 console.error('FCM send error:', error);35 return res.status(500).json({36 error: error.message,37 errorCode: error.errorInfo?.code38 });39 }40};Pro tip: If you prefer Supabase Edge Functions (Deno), use the 'firebase-admin' package from npm via esm.sh, or use the 'google-auth-library' for manual OAuth token generation. The key point is that the service account private key stays in the function's secure environment — never passed in from Bubble. Set BUBBLE_FCM_SECRET as an environment variable on the function to validate that only your Bubble Backend Workflow can call it.
Expected result: Your helper endpoint is deployed and publicly accessible via HTTPS. A test POST request with a valid x-api-key and a real device token returns `{success: true, messageId: '...'}`. Note the endpoint URL and the x-api-key value.
Step 4 — Configure the API Connector in Bubble to call your helper endpoint
In your Bubble app editor, click 'Plugins' in the left sidebar. If you do not already have the API Connector plugin installed, click 'Add plugins,' search for 'API Connector,' and install the free plugin by Bubble. Back in the Plugins tab, click 'API Connector.' Click 'Add another API' and name this group 'FCM Helper.' In the 'Shared headers across all calls' section, click 'Add a shared header.' Name it 'x-api-key' and paste your helper endpoint's secret API key as the value. Critically: check the 'Private' checkbox next to this header — this ensures the key is processed server-side and never sent to the browser, even when this API call is used in a client-side workflow. Under the 'Authentication' field for this API group, leave it as 'None' since you are handling authentication via the custom header. Click 'Add call.' Name this call 'Send Notification.' Set the method to POST and the endpoint URL to your helper's full HTTPS URL (e.g. `https://us-central1-your-project.cloudfunctions.net/sendFcmNotification`). In the Body section, set type to JSON and add these parameters: `deviceToken` (text, example: 'DEVICE_TOKEN_PLACEHOLDER'), `title` (text, example: 'Test Notification'), `body` (text, example: 'This is a test'), `data` (text, example: '{}'). Click 'Initialize call.' If the helper endpoint is running and returns a success response, Bubble will detect the response schema. Change 'Use as' to 'Action' (since this call triggers a side effect, not a data fetch).
1// API Connector configuration (represented as JSON for reference)2{3 "api_group": "FCM Helper",4 "shared_headers": [5 {6 "key": "x-api-key",7 "value": "your-secret-key-here",8 "private": true9 },10 {11 "key": "Content-Type",12 "value": "application/json"13 }14 ],15 "calls": [16 {17 "name": "Send Notification",18 "method": "POST",19 "url": "https://us-central1-your-project.cloudfunctions.net/sendFcmNotification",20 "use_as": "Action",21 "body": {22 "deviceToken": "<dynamic>",23 "title": "<dynamic>",24 "body": "<dynamic>",25 "data": "<dynamic>"26 }27 }28 ]29}Pro tip: The 'Initialize call' step requires your helper endpoint to return a real successful response. Have a test FCM token ready (from Step 5 below, or use a known valid token from the Firebase console). If the initialize call times out, check that your helper endpoint URL is correct and publicly accessible.
Expected result: The 'Send Notification' API call is initialized in Bubble and appears under FCM Helper. Its 'Use as' is set to 'Action.' The x-api-key shared header is marked Private.
Step 5 — Collect FCM device registration tokens on the client side
Push notifications require the user to grant browser notification permission, and the FCM SDK then returns a unique registration token for that browser/device. In Bubble, the easiest way to collect this token is via the Zeroqode Firebase plugin (a paid plugin — verify current pricing at zeroqode.com). Install it from the Bubble Plugin Marketplace by clicking Plugins → Add plugins and searching for 'Firebase' (look for the one by Zeroqode). In the plugin settings, paste your Firebase web app config: Project ID, API Key, Auth Domain, Storage Bucket, Messaging Sender ID, and App ID (all from the Firebase console under Project settings → General → Your apps). These are client-safe keys. Once installed, the plugin adds 'Firebase' actions to your Bubble workflow editor, including 'Get FCM Token' (or equivalent — check the plugin's action list for the exact name). Create a Bubble data type called 'FCM Token' with fields: User (type: User), Token (type: text), Created Date (type: date). Then, on your main page or dashboard page, add a workflow triggered by 'Page is loaded.' Add the action: Firebase plugin → 'Request notification permission & get token.' After this action succeeds, add another action: 'Create a new FCM Token' with User = Current User and Token = the result of the previous step. The next time the user visits, check if their FCM Token already exists before creating a duplicate. If you prefer not to use the paid Zeroqode plugin, you can load the Firebase JS SDK via a Script tag in the page header (Bubble Settings → SEO/metatags → Script/Meta tags in header) and use a 'Run JavaScript' action (requires the Toolbox plugin) to call `firebase.messaging().getToken()` and store the result in a Bubble custom state, then save it to the database.
1// Option B: Manual Firebase JS SDK approach2// Paste in Bubble page header (Settings → SEO/metatags → Script in header)3// REPLACE config values with your actual Firebase project config45// firebase-messaging-sw.js must be served at your app root6// (requires Bubble Business plan or a CDN workaround for service worker)78// In page header:9<script type="module">10import { initializeApp } from 'https://www.gstatic.com/firebasejs/10.12.0/firebase-app.js';11import { getMessaging, getToken } from 'https://www.gstatic.com/firebasejs/10.12.0/firebase-messaging.js';1213const firebaseConfig = {14 apiKey: "AIzaSy...",15 authDomain: "your-project.firebaseapp.com",16 projectId: "your-project-id",17 storageBucket: "your-project.appspot.com",18 messagingSenderId: "123456789",19 appId: "1:123456789:web:abc123"20};2122const app = initializeApp(firebaseConfig);23const messaging = getMessaging(app);2425// Called when user clicks 'Enable Notifications' button26window.requestFCMToken = async function() {27 try {28 const token = await getToken(messaging, {29 vapidKey: 'YOUR_VAPID_KEY_FROM_FIREBASE_CONSOLE'30 });31 // Store token in Bubble via custom state or element attribute32 bubble_fn_setFCMToken(token); // Toolbox plugin function33 } catch (err) {34 console.error('FCM token error:', err);35 }36};37</script>Pro tip: FCM web push requires the user to be on your live Bubble app domain (HTTPS). The token registration will not work on Bubble's test preview URL if you have domain-restricted API keys. Test on your published Bubble app URL or custom domain. Also, service workers (required for background push notifications when the browser tab is closed) need a firebase-messaging-sw.js file served at the root — the Zeroqode plugin handles this automatically, which is one of its key advantages over the manual approach.
Expected result: When a user visits your Bubble app and clicks an 'Enable Notifications' button (or it fires automatically on page load), they see a browser permission prompt. After granting permission, an FCM Token record is created in your Bubble database linked to their User record.
Step 6 — Build the Backend Workflow to trigger notifications and test end-to-end
Now wire everything together using a Bubble Backend Workflow. In the Bubble editor, click 'Backend workflows' in the left sidebar (if you do not see this, your plan may not include it — check Settings → Plan). Click 'New API workflow' and name it 'Send Push Notification.' Add parameter inputs: 'target_user' (type: User) and optionally 'message_title' (text) and 'message_body' (text). Inside this workflow, add a 'Step 1: Search for FCM Token' action — use 'Search for FCM Tokens' where User = input target_user, and get the first result (most recent token). Add a conditional: 'Only when step 1's FCM Token's Token is not empty.' Add 'Step 2: FCM Helper - Send Notification' (the API Connector action you configured in Step 4). Map the parameters: deviceToken = Step 1's FCM Token's Token, title = message_title input (or hardcode a default), body = message_body input. Save the Backend Workflow. To trigger this workflow from other Bubble workflows: in any regular workflow (button click, data change, scheduled recurring workflow), add the action 'Schedule API Workflow' (paid plan action). Select 'Send Push Notification' and pass the target user and message text. For the 'Run at' time, use 'Current date/time' to fire immediately. For bulk sends, use 'Schedule API Workflow on a List' with a list of users. Test by triggering the Backend Workflow manually from Bubble's workflow editor — click the three-dot menu on the Backend Workflow and select 'Run now' to fire it immediately. Check the result in Bubble's Logs tab → Workflow logs. If the FCM token is valid and the helper endpoint is running, you should receive the push notification in your browser within a few seconds. If RapidDev's team has built hundreds of Bubble apps with push notification pipelines like this and you are hitting issues at this stage, a free scoping call at rapidevelopers.com/contact can save hours of debugging.
1// Backend Workflow: 'Send Push Notification'2// Parameters: target_user (User), message_title (text), message_body (text)3//4// Step 1: Search for FCM Tokens5// Type: FCM Token6// Constraint: User = input's target_user7// Sort by: Created Date (descending)8// Count: 19//10// Step 2 (only when Step 1's first item's Token is not empty):11// FCM Helper - Send Notification12// deviceToken: Step 1's first item's Token13// title: input's message_title14// body: input's message_body15// data: {}16//17// Trigger from any workflow:18// Action: Schedule API workflow19// Workflow: Send Push Notification20// target_user: [relevant user]21// message_title: "Your order shipped!"22// message_body: "Track it here: [order link]"23// Run at: Current date/time2425// FCM HTTP v1 send payload (handled by your helper endpoint)26{27 "message": {28 "token": "fcm_device_token_here",29 "notification": {30 "title": "Your order shipped!",31 "body": "Track it here: https://yourapp.com/orders/123"32 },33 "data": {34 "order_id": "123",35 "type": "shipment_update"36 }37 }38}Pro tip: Use 'Schedule API Workflow on a List' to send to multiple users without hitting Bubble's WU ceiling per individual call. Each scheduled run consumes WUs, so for bulk notification campaigns, consider batching FCM tokens and using FCM's `batchSend` endpoint in your helper (up to 500 per batch) instead of one API call per user.
Expected result: The Backend Workflow fires successfully. In Bubble's Logs tab, you see the API call completed with a 200 response from your helper endpoint. The target device or browser displays the push notification with the correct title and body. Your end-to-end FCM integration is live.
Common use cases
Order and shipment status alerts
Trigger a Bubble Backend Workflow when an order status field changes to 'Shipped' or 'Delivered.' The workflow fetches the customer's stored FCM token and sends a push notification with the tracking number. Users see the alert in their browser or on their phone even when they are not actively on your site.
When Order's Status changes to 'Shipped', send FCM notification to Order's Customer's FCM Token with title 'Your order is on its way' and body 'Track your shipment: [tracking number]'
Copy this prompt to try it in Bubble
Real-time in-app messaging notifications
When a new Message record is created in Bubble's database, a Backend Workflow fires immediately and sends a push notification to the recipient user's FCM token. The user receives the notification in their browser even if they have a different tab open, improving engagement for chat or collaboration features.
When a new Message is created where Recipient is not the Current User, run Backend Workflow 'Send FCM Notification' with Recipient's FCM Token, title 'New message from [Sender Name]', and body truncated to 100 characters
Copy this prompt to try it in Bubble
Scheduled reminders and re-engagement campaigns
Use Bubble's recurring workflow scheduler to send daily or weekly push notifications to users who have not logged in recently. Pass a list of inactive user FCM tokens to the Backend Workflow and use FCM's batch send endpoint (up to 500 messages per request) to reduce Workload Unit consumption.
Every Sunday at 9am: find all Users where Last Login < 7 days ago AND FCM Token is not empty; for each, run Backend Workflow 'Send Weekly Digest Notification'
Copy this prompt to try it in Bubble
Troubleshooting
Helper endpoint returns 401 Unauthorized or Bubble's API Connector returns an error on the Send Notification call
Cause: The x-api-key header value in Bubble's API Connector does not match the BUBBLE_FCM_SECRET environment variable set on the helper endpoint, or the Private checkbox was not checked and the header is being processed incorrectly.
Solution: In Bubble's Plugins tab, open the API Connector and check the FCM Helper group. Verify the x-api-key shared header value exactly matches what you set on the Cloud Function or Lambda. Confirm the 'Private' checkbox is enabled. Re-initialize the call after making changes.
Helper endpoint returns 'messaging/invalid-registration-token' or 'messaging/registration-token-not-registered'
Cause: The FCM device token stored in Bubble's database is invalid, expired, or belongs to a device that has unregistered from push notifications (common after reinstalls, browser cache clears, or explicit permission revocations).
Solution: FCM tokens can expire or become invalid. Add logic to your token collection workflow: when a user visits your app, re-request the token and update the existing FCM Token record rather than creating a duplicate. When the helper endpoint returns a 'not-registered' error, delete the token from Bubble's database and prompt the user to re-enable notifications.
Bubble shows 'This action requires a paid plan' or the Backend Workflows section is not visible in the editor
Cause: Backend Workflows (API Workflows) are a Starter plan and above feature. The app is on the Bubble free plan.
Solution: Upgrade your Bubble app to at least the Starter plan to enable Backend Workflows. Until then, FCM notification sending cannot be done securely — sending from a client-side workflow would expose your helper endpoint's API key in browser DevTools. There is no free-plan workaround for this security requirement.
The API Connector 'Initialize call' times out or returns an error during setup (Step 4)
Cause: The helper endpoint is not deployed yet, the URL is incorrect, or the Cloud Function is cold-starting. Alternatively, the test device token used for initialization is a placeholder string, not a real FCM token.
Solution: Before running Initialize call, confirm your helper endpoint returns a success response from an external tool like Postman or curl. Use a real FCM device token (captured from Step 5) as the deviceToken value during initialization. If using Google Cloud Functions, trigger the function once from the Cloud Console to warm it up before testing from Bubble.
1// Test your helper endpoint before using Initialize call:2// curl -X POST https://your-helper-url/sendFcmNotification \3// -H 'x-api-key: your-secret' \4// -H 'Content-Type: application/json' \5// -d '{"deviceToken":"real_token_here","title":"Test","body":"Hello"}'Push notifications work in the browser tab but do not appear when the tab is closed or the browser is minimized
Cause: Background push (notifications delivered when the app is not in the foreground) requires a registered Service Worker (firebase-messaging-sw.js) served from your app's root URL. If using the manual JS SDK approach, the service worker is not registered; if using the Zeroqode plugin, the service worker setup depends on plugin version and Bubble's hosting configuration.
Solution: Background push is significantly easier with the Zeroqode Firebase plugin, which handles service worker registration. For the manual approach, a service worker file must be served from your Bubble custom domain root — this is only achievable on Bubble's Business plan or by using a CDN proxy (Cloudflare Workers) to serve the file at the root path. If background push is critical for your use case, the Zeroqode plugin is the recommended path.
Best practices
- Store the Google service account private key only in your helper endpoint's secret management system (GCP Secret Manager, AWS Secrets Manager, Supabase Vault) — never in Bubble's Environment Variables, API Connector fields, or database
- Cache the OAuth 2.0 Bearer token in your helper endpoint's memory or a fast cache (Redis, Supabase key-value) with a TTL of 55 minutes — the token is valid for 60 minutes, and re-generating it for every notification wastes compute and adds latency
- Store only one active FCM token per user per device/browser; when a user's token is refreshed, overwrite the existing record rather than accumulating stale tokens in your Bubble database
- Handle FCM error codes gracefully in your helper endpoint: 'messaging/registration-token-not-registered' means the token is dead — delete it from Bubble's database and remove the user from future sends
- Use FCM's batch send endpoint (`batchSend`) for campaigns with more than a few users; each batch can contain up to 500 tokens, drastically reducing the number of Backend Workflow API calls and your Bubble WU consumption
- Always check browser notification permission before requesting the FCM token — calling getToken() without prior permission grants will silently fail in some browsers; show users a clear value proposition before the browser permission prompt appears
- Test your full notification pipeline in Bubble's Logs tab (Logs → Workflow logs) before going live — you can see exactly which API calls succeeded or failed, the response payloads, and WU consumption per workflow run
- Add a 'notification opt-out' flow in your Bubble app: a user toggle that deletes their FCM Token record from the database, ensuring they are removed from future sends without requiring them to revoke browser permissions
Alternatives
Twilio handles SMS and WhatsApp notifications natively with a simple REST API and a Personal API key — far easier to set up in Bubble's API Connector than FCM's OAuth service account flow. Choose Twilio when your audience is more likely to be on mobile (SMS is universally supported without notification permissions) and push notification opt-in rates are too low. Twilio charges per message ($0.0079/SMS in the US) versus FCM's free pricing.
Slack Incoming Webhooks let you post real-time alerts to a Slack channel with a single API call and no OAuth complexity — ideal for internal team notifications (new orders, errors, sign-ups) rather than end-user push notifications. If your notification audience is your own team rather than your app's customers, Slack is dramatically simpler to set up in Bubble.
SendGrid delivers email notifications rather than push notifications. Email has near-universal reach (no permission required, works on all devices) but lower immediacy than push. For transactional alerts like order confirmations or password resets where message delivery is more important than instant delivery, SendGrid's simpler API key authentication is preferable to FCM's OAuth complexity.
Frequently asked questions
Can I use the old FCM server key (the static 'key=...' string) in Bubble's API Connector?
No. Google deprecated the legacy FCM server key in June 2023 and disabled it for new projects in 2024. Any Bubble tutorial or forum post showing `Authorization: key={server_key}` is outdated and will not work for new Firebase projects. You must use the FCM HTTP v1 API with a short-lived OAuth 2.0 Bearer token from a Google service account.
Do I need the Zeroqode Firebase plugin to use FCM in Bubble?
Not strictly. The Zeroqode plugin is the simplest path for client-side device token registration and service worker setup (required for background push notifications). The alternative — loading the Firebase JS SDK via a Script tag in the page header and using the Toolbox plugin's 'Run JavaScript' action — works but requires more manual setup and lacks automatic service worker registration, meaning background push won't work on the Bubble free hosting tier. For production apps, the paid Zeroqode plugin is worth the cost.
Is FCM available on the Bubble free plan?
Device token registration (client-side, using the Zeroqode plugin) works on any Bubble plan. However, sending FCM notifications requires a Backend Workflow with an API Connector Action call — both of which are Starter plan and above features. There is no secure way to send FCM notifications from a free Bubble plan, because putting your helper endpoint's API key in a client-side workflow would expose it in the browser.
How do I send the same push notification to all my users at once?
Use Bubble's 'Schedule API Workflow on a List' action. Search for all FCM Token records (filtered by users who have not opted out), pass the list to 'Schedule API Workflow on a List,' and have the workflow send one notification per user. For very large lists (1,000+ users), update your helper endpoint to accept an array of tokens and use FCM's `batchSend` endpoint (up to 500 tokens per batch) to reduce the number of Backend Workflow calls and WU consumption.
How long is the OAuth token valid, and do I need to refresh it for every notification?
Google OAuth 2.0 tokens generated from a service account are valid for exactly 1 hour. You do not need to generate a new token for every notification send. Cache the token in your helper endpoint's memory or an external cache (Redis, Upstash) with a TTL of 55 minutes. Check if a cached token exists and is not about to expire before generating a new one. Your helper endpoint should handle this caching internally — Bubble does not need to be involved in token lifecycle management.
Why did my user receive the notification once but now they are not receiving new ones?
FCM registration tokens can change when: the user clears browser storage or cache, the browser updates and re-registers service workers, or the user re-grants notification permission after revoking it. Add logic on your Bubble page load to re-request the FCM token on every visit and update the stored FCM Token record if the token has changed. Also check Bubble's Logs tab to confirm the Backend Workflow is firing correctly and the helper endpoint is returning success responses.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation