When no external API exists for your feature — custom pricing calculators, PDF generators, complex business rules — build the API yourself as a Firebase Cloud Function with an HTTP endpoint. Deploy the Cloud Function, register its URL in FlutterFlow's API Manager as an API Group, create an API Call for each endpoint, and call it from Action Flows. Always verify the Firebase ID token in your Cloud Function to authenticate the caller. Return structured JSON that FlutterFlow can parse and bind to widgets.
Build your own backend API endpoints for business logic that FlutterFlow cannot handle natively
FlutterFlow's backend query and action flow system handles most common data operations — reading and writing Firestore documents, calling external APIs. But some features require custom business logic that no external service provides: a pricing calculator applying your company's unique discount rules, a PDF report generator using your brand template, a document processing pipeline that combines data from multiple sources, or a custom scoring algorithm. For these, you build your own API endpoint as a Firebase Cloud Function, deploy it, and call it from FlutterFlow exactly like any other external API.
Prerequisites
- A Firebase project with Cloud Functions enabled on the Blaze billing plan (required for outbound network calls)
- Node.js installed locally for writing and testing Cloud Functions before deployment
- A FlutterFlow project connected to the same Firebase project
Step-by-step guide
Design and deploy your Cloud Function HTTP endpoint
Design and deploy your Cloud Function HTTP endpoint
Create a Cloud Function using functions.https.onRequest (for unauthenticated or custom-auth endpoints) or functions.https.onCall (for Firebase Auth-integrated callable functions). For most FlutterFlow custom APIs, use onCall because it automatically handles CORS, parses the request body, and provides context.auth with the caller's Firebase UID. The function signature is: exports.functionName = functions.https.onCall(async (data, context) => { ... }). Verify authentication: if (!context.auth) throw new functions.https.HttpsError('unauthenticated', 'Login required'). Process the request using data (the JSON body from FlutterFlow). Return a plain JavaScript object — FlutterFlow receives it as JSON. Deploy with: firebase deploy --only functions. The URL format is: https://us-central1-{projectId}.cloudfunctions.net/{functionName}. For callable functions, FlutterFlow must send the body wrapped in {"data": {...}} format.
1// Custom API: calculateCustomPrice2// Called by FlutterFlow to apply business pricing rules3const functions = require('firebase-functions');4const admin = require('firebase-admin');56exports.calculateCustomPrice = functions.https.onCall(async (data, context) => {7 // Require authentication8 if (!context.auth) {9 throw new functions.https.HttpsError('unauthenticated', 'Login required');10 }1112 const { productId, quantity, customerType, promoCode } = data;1314 // Validate input15 if (!productId || !quantity || quantity < 1) {16 throw new functions.https.HttpsError('invalid-argument', 'productId and quantity required');17 }1819 // Fetch product from Firestore20 const productDoc = await admin.firestore().collection('products').doc(productId).get();21 if (!productDoc.exists) {22 throw new functions.https.HttpsError('not-found', 'Product not found');23 }24 const product = productDoc.data();2526 // Apply business pricing rules27 let unitPrice = product.basePrice;28 let discountPercent = 0;2930 if (customerType === 'wholesale') discountPercent += 15;31 if (quantity >= 100) discountPercent += 10;32 if (quantity >= 500) discountPercent += 5;3334 if (promoCode) {35 const promoDoc = await admin.firestore().collection('promo_codes').doc(promoCode).get();36 if (promoDoc.exists && promoDoc.data().active) {37 discountPercent += promoDoc.data().discountPercent;38 }39 }4041 const discountedPrice = unitPrice * (1 - discountPercent / 100);42 const totalPrice = discountedPrice * quantity;4344 return {45 unitPrice: parseFloat(discountedPrice.toFixed(2)),46 totalPrice: parseFloat(totalPrice.toFixed(2)),47 discountPercent,48 breakdown: {49 basePrice: unitPrice,50 quantity,51 discountApplied: discountPercent > 0,52 promoCodeUsed: !!promoCode && discountPercent > 053 }54 };55});Expected result: The Cloud Function deploys and returns pricing calculations with correct discounts applied based on customer type, quantity, and promo codes.
Register the custom API in FlutterFlow's API Manager
Register the custom API in FlutterFlow's API Manager
In FlutterFlow, open the API Manager (plug icon in left sidebar). Add a new API Group named CustomBackend. Set the Base URL to your Cloud Functions base URL: https://us-central1-{your-project-id}.cloudfunctions.net. Add a Content-Type: application/json header. Add an API Call named calculatePrice. Set method to POST, path to /calculateCustomPrice. In the Body tab, add the request body JSON. For onCall callable functions, the body MUST be wrapped in a data key: {"data": {"productId": "[productId]", "quantity": [quantity], "customerType": "[customerType]", "promoCode": "[promoCode]"}}. Mark productId, quantity, customerType, promoCode as Variables. In the Response and Test tab, test with sample values. The response will be wrapped in a result key: {"result": {"unitPrice": 8.50, "totalPrice": 850.00, ...}}. Extract the fields: result.unitPrice, result.totalPrice, result.discountPercent, result.breakdown.discountApplied.
Expected result: A passing test in the API Manager's Response and Test tab shows the pricing calculation result from your custom Cloud Function.
Call the custom API from a FlutterFlow Action Flow
Call the custom API from a FlutterFlow Action Flow
On your product detail page, add a TextField for quantity and a DropDown for customerType (values: retail, wholesale). Add a promo code TextField. Add a Calculate Price Button. In the Button's Action Flow: (1) Validate: IF quantity TextField is empty, show Snackbar 'Enter quantity' and stop. (2) API Call → CustomBackend → calculatePrice, passing: productId from Page Parameter, quantity from TextField, customerType from DropDown, promoCode from TextField. (3) Set Page State variable priceResult to the API response. (4) Show a Container (conditional visibility based on priceResult is not null) with: Text 'Unit Price: $[priceResult.unitPrice]', Text 'Total: $[priceResult.totalPrice]', Text 'Discount: [priceResult.discountPercent]%' (conditionally visible if discountPercent > 0). Add an error handler: if the API returns a non-200 status, show a Snackbar with the error message.
Expected result: Entering quantity and customer type then tapping Calculate shows the correct price breakdown from the Cloud Function, including any applicable discounts.
Add a PDF generation endpoint example
Add a PDF generation endpoint example
A second common custom API pattern is document generation. Deploy a Cloud Function named generateReport that accepts a reportType and dateRange, queries Firestore for the relevant data, uses the pdfkit npm package to generate a PDF with your branding, uploads the PDF to Firebase Storage, and returns the download URL. In FlutterFlow: add an API Call named generateReport in the CustomBackend API Group. In the Action Flow on a Reports page: call generateReport with the user's selections → receive the download URL → use a Launch URL action to open the PDF. For reports that take more than a few seconds to generate: return a jobId instead of waiting, write status updates to a Firestore document, and have FlutterFlow poll the document with a real-time Backend Query. This is the same pattern for any long-running custom API operation.
Expected result: A Generate Report button triggers the Cloud Function and opens the generated PDF in the device's browser once processing completes.
Complete working example
1CUSTOM CLOUD FUNCTION API PATTERN FOR FLUTTERFLOW23CLOUD FUNCTION STRUCTURE:4├── exports.functionName = functions.https.onCall(async (data, context) => {5│ ├── 1. Verify auth: if (!context.auth) throw HttpsError('unauthenticated')6│ ├── 2. Validate input: if (!data.required) throw HttpsError('invalid-argument')7│ ├── 3. Business logic: fetch Firestore, apply rules, compute result8│ ├── 4. Return plain object → becomes JSON response9│ └── })10└── URL: https://us-central1-{project}.cloudfunctions.net/{name}1112FLUTTERFLOW API MANAGER:13├── API Group: CustomBackend14│ └── Base URL: https://us-central1-{project}.cloudfunctions.net15├── API Call: functionName (POST)16│ └── Body: { "data": { var1: [v1], var2: [v2] } }17│ (onCall requires body wrapped in 'data' key)18└── Response extraction: result.field1, result.field21920ACTION FLOW PATTERN:211. Validate inputs (IF empty → Snackbar → stop)222. API Call → CustomBackend → functionName233. Check response status (IF != 200 → show error)244. Set Page State variable = response fields255. Show result Container (conditional visibility)2627COMMON CUSTOM API USE CASES:28├── Pricing calculator (volume discounts, promo codes)29├── PDF / report generator (returns Storage URL)30├── Data aggregation (combines multiple Firestore queries)31├── Email / notification trigger (uses server-side keys)32├── External service integration (OAuth, complex auth)33└── Scheduled data processing with status feedback3435AUTHENTICATION:36├── context.auth.uid → Firebase UID of the caller37├── context.auth.token.email → caller's email38└── Always verify before executing business logicCommon mistakes when integrating a Custom API for Unique App Functionalities in FlutterFlow
Why it's a problem: Not verifying the Firebase ID token in the Cloud Function, leaving the endpoint open to unauthenticated calls
How to avoid: Use functions.https.onCall — it automatically provides context.auth with the caller's Firebase Auth session. Always check: if (!context.auth) throw new functions.https.HttpsError('unauthenticated', 'Login required') at the start of every function that handles user data or protected operations.
Why it's a problem: Forgetting to wrap the FlutterFlow request body in a data key when calling an onCall Cloud Function
How to avoid: In the API Manager Body tab, always wrap onCall payload in: {"data": {"field1": [var1], "field2": [var2]}}. The response also comes wrapped: the actual result is in response.result.field, not response.field. Extract response paths as result.yourField.
Why it's a problem: Not handling Cloud Function errors with try-catch, returning unhelpful 500 errors to FlutterFlow
How to avoid: Wrap business logic in try-catch. For known error conditions, throw new functions.https.HttpsError('error-code', 'User-friendly message'). For unexpected errors, log and rethrow: console.error(err); throw new functions.https.HttpsError('internal', 'Something went wrong'). In FlutterFlow, check the API response status and show appropriate error messages rather than blank screens.
Best practices
- Always verify Firebase authentication at the start of every custom Cloud Function that handles user data or protected operations
- Return structured JSON with consistent field names that FlutterFlow can reliably parse — define a response schema and stick to it
- Validate all input parameters at the start of the function and throw descriptive HttpsError messages that FlutterFlow can display to users
- Use Cloud Function environment variables for all secrets (external API keys, database passwords) — never hardcode them in function source code
- Add a timeout parameter to your function (functions.https.onCall({timeoutSeconds: 30})) for long-running operations to prevent runaway executions
- Test Cloud Functions locally using the Firebase emulator (firebase emulators:start) before deploying — fast iteration without deployment delays
- Log important events and errors with console.log/console.error — view logs in Firebase Console → Functions → Logs for debugging
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I need to build a custom Cloud Function API for my FlutterFlow app that implements [describe your business logic — e.g., a custom pricing calculator with volume discounts and promo codes]. Write the Firebase Cloud Function using functions.https.onCall that accepts the required parameters from FlutterFlow, validates them, applies the business rules, and returns a structured JSON response. Include authentication verification and error handling.
Set up a custom API in my FlutterFlow project to call a Cloud Function I have deployed at https://us-central1-my-project.cloudfunctions.net/calculateCustomPrice. Create an API Group named CustomBackend with this base URL. Add an API Call named calculatePrice that sends a POST request with body {"data": {"productId": [productId], "quantity": [quantity], "customerType": [customerType]}}. Extract the response fields result.unitPrice, result.totalPrice, and result.discountPercent. Add this API call to a Calculate Price button on my product detail page.
Frequently asked questions
How do I create a custom API endpoint for my FlutterFlow app?
Deploy a Firebase Cloud Function using functions.https.onCall. Write your business logic in the function — query Firestore, apply rules, call other services. Return a plain JavaScript object as the response. Register the Cloud Function URL in FlutterFlow's API Manager as an API Group with a POST API Call. Wrap the request body in {"data": {...}} format (required by onCall). Test with sample values in the API Manager's Response and Test tab.
What is the difference between onCall and onRequest for Firebase Cloud Functions?
onCall is a Firebase callable function — it automatically handles CORS, parses the JSON body wrapped in {data: {}}, provides context.auth with the caller's Firebase Auth session, and handles authentication errors. onRequest is a raw HTTP handler — you get the full Express req/res objects and must handle CORS, body parsing, and auth yourself. Use onCall for FlutterFlow custom APIs because authentication integration is automatic. Use onRequest for webhooks from external services (Stripe, Shopify) that send their own request format.
How do I pass the user's login info to my custom Cloud Function?
When using functions.https.onCall, Firebase automatically includes the caller's authentication information in context.auth. This is populated when the user is logged in with Firebase Auth. context.auth.uid is the user's unique Firebase ID, context.auth.token.email is their email, and context.auth.token contains their ID token claims. You do not need to pass authentication manually from FlutterFlow — it is handled automatically.
How do I call my Cloud Function from FlutterFlow and get the response?
In FlutterFlow's API Manager: create an API Group with base URL https://us-central1-{projectId}.cloudfunctions.net. Add an API Call with method POST and path /yourFunctionName. In the Body tab, set the JSON body as {"data": {"param1": [variable1]}}. Test to see the response structure. Extract response fields in the Response and Test tab. In an Action Flow, add an API Call action, pass variable values, and use Set from Variable to bind the response fields to UI elements.
My Cloud Function works in the Firebase emulator but fails in production. Why?
Common causes: (1) Missing Blaze billing plan — outbound network calls from Cloud Functions require the Blaze pay-as-you-go plan. (2) Wrong Firebase project — the deployed function is in a different Firebase project than FlutterFlow is connected to. (3) CORS in onRequest functions — the emulator is more permissive. (4) Environment variables not set in production — set them with firebase functions:config:set or in the Firebase Console → Functions → your function → Edit. (5) Cold start timeout — the first call after a period of inactivity takes 2-10 seconds. Test the deployed URL directly in Postman to isolate the issue.
Can RapidDev help build complex custom API endpoints for my FlutterFlow app?
Yes. Complex business logic — multi-step pricing calculators, PDF report generators, external service integrations, data processing pipelines — requires careful Cloud Function architecture to handle edge cases, authentication, error recovery, and performance. RapidDev builds custom Cloud Function APIs for FlutterFlow apps regularly and can implement business logic that would be impractical to build purely within FlutterFlow's visual workflow system.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation