Connect FlutterFlow to Dext (formerly Receipt Bank) via a Firebase Cloud Function or Supabase Edge Function proxy that holds your API key in the Authorization header. FlutterFlow captures a receipt photo and sends it to the proxy; the proxy forwards the multipart upload to Dext, then the app polls for async OCR results.
| Fact | Value |
|---|---|
| Tool | Receipt Bank (now Dext) |
| Category | Finance & Accounting |
| Method | FlutterFlow API Call |
| Difficulty | Intermediate |
| Time required | 45 minutes |
| Last updated | July 2026 |
Dext in FlutterFlow: Mobile Receipt Capture with Async OCR
Dext — which many users still search for under its original name Receipt Bank — is the bookkeeper's choice for automated receipt and invoice capture. Its REST API at `https://api.dext.com/v1` accepts document uploads, runs OCR in the background, and returns extracted fields like vendor name, total amount, tax, date, and category. The FlutterFlow use-case is a mobile expense app where a user snaps a receipt photo, submits it, and sees the categorized data a few seconds later ready to confirm.
The first thing to sort out before writing any code: Dext API access is not available on all plans, and it must be explicitly enabled for your account. If you are on a lower-tier Dext plan, the API key may not exist yet — contact your Dext account manager to confirm access before building. Once enabled, the API uses a simple API key in the `Authorization` header, which sounds straightforward, but in a FlutterFlow app that key must never touch the client. FlutterFlow compiles to a Flutter app running on the user's device; any key in an API Call header or Dart code is embedded in the compiled binary and can be extracted. A Dext API key grants access to all captured financial documents — expense data, invoice amounts, supplier names — so it must live exclusively in a Firebase Cloud Function or Supabase Edge Function proxy.
The second important concept: Dext's OCR is asynchronous. When you upload a document you receive a document ID, not extracted data. The extracted fields become available only after Dext processes the image, which can take a few seconds to a minute depending on document complexity. Your FlutterFlow app needs a polling loop or a webhook-driven backend update to know when the data is ready. Both patterns are covered in the steps below.
Integration method
FlutterFlow communicates with Dext through a Firebase Cloud Function or Supabase Edge Function that holds the API key securely. The FlutterFlow app captures a receipt image using the built-in image picker, sends it to the proxy, and the proxy forwards the multipart upload to `api.dext.com/v1`. Because OCR processing is asynchronous, the app then polls a document-status endpoint (or waits for a webhook to update the backend) until extracted fields are ready. The API key never leaves the proxy.
Prerequisites
- A Dext account on a plan that includes API access (contact Dext support or your account manager to confirm and enable it)
- A Dext API key (generated from within your Dext account once API access is enabled)
- A FlutterFlow project with the image picker or camera widget already set up
- A Firebase project (Blaze plan) or Supabase project for the server-side proxy
Step-by-step guide
Confirm Dext API access is enabled and obtain your API key
Before doing anything in FlutterFlow, confirm that your Dext account tier includes API access. This is the step that trips most builders: Dext does not expose an API key to all paid accounts by default — it is gated at higher tiers and must be enabled per account. Log into your Dext account and look for an API or Developer section in account settings. If you do not see one, contact Dext support or your account manager directly and ask them to enable API access for your account. Provide your account email and the name of the integration you are building. Once API access is enabled, navigate to the API settings page (the exact path may vary — look under Account Settings → Integrations or Developer). Generate an API key and copy it immediately. Store it in your Firebase Functions environment config or Supabase Edge Function secrets under a name like `DEXT_API_KEY`. Do not paste it anywhere in FlutterFlow — not in App Values, not in a Custom Action, not in an API Call header. A Dext API key gives full access to all documents your account has captured, which includes sensitive financial data for every client whose receipts have been uploaded. Verify the base URL by checking the current Dext API documentation at `https://api.dext.com/v1` — confirm this endpoint is live for your account region before building the proxy.
Pro tip: Ask Dext support for a test or sandbox environment if available — it lets you upload test receipts without affecting real accounting data while you build and test the integration.
Expected result: You have a Dext API key stored securely in your Firebase/Supabase environment. You have confirmed the API base URL and your account can access it.
Deploy a Firebase/Supabase proxy for receipt upload and status polling
The proxy has two jobs: (1) receive a receipt image from FlutterFlow, forward the multipart upload to Dext with the API key in the Authorization header, and return the document ID; (2) accept a document ID from FlutterFlow and return the current processing status and extracted fields. For a Firebase Cloud Function, create a function named `dextProxy` with two routes: `POST /upload` (receives the image bytes from FlutterFlow, constructs a multipart/form-data body, and POSTs to `https://api.dext.com/v1/documents`) and `GET /status?documentId={{id}}` (fetches `https://api.dext.com/v1/documents/{id}` and returns the status and extracted fields). The Authorization header for Dext follows the pattern: `Authorization: ApiKey YOUR_KEY_HERE` (verify the exact format in current Dext API documentation — it may be `Bearer` or a custom prefix). Read the key from `process.env.DEXT_API_KEY` at runtime. For Supabase, deploy a Deno Edge Function named `dext-proxy` using `supabase/functions/dext-proxy/index.ts`. Read the key with `Deno.env.get('DEXT_API_KEY')`. Handle both upload and status-check routes by inspecting the URL pathname. Deploy the function and note the URL. This URL becomes the base URL of your FlutterFlow API Group.
1// Firebase Cloud Function — dextProxy (index.js)2const functions = require('firebase-functions');3const fetch = require('node-fetch');4const FormData = require('form-data');56const DEXT_API_KEY = process.env.DEXT_API_KEY;7const DEXT_BASE = 'https://api.dext.com/v1';89exports.dextProxy = functions.https.onRequest(async (req, res) => {10 res.set('Access-Control-Allow-Origin', '*');11 if (req.method === 'OPTIONS') { res.status(204).send(''); return; }1213 // GET /status?documentId=xxx — poll document status14 if (req.method === 'GET' && req.query.documentId) {15 const r = await fetch(`${DEXT_BASE}/documents/${req.query.documentId}`, {16 headers: { 'Authorization': `ApiKey ${DEXT_API_KEY}` }17 });18 return res.status(r.status).json(await r.json());19 }2021 // POST /upload — receive image bytes and forward to Dext22 if (req.method === 'POST') {23 const form = new FormData();24 form.append('file', Buffer.from(req.body.imageBase64, 'base64'), {25 filename: 'receipt.jpg',26 contentType: 'image/jpeg'27 });28 if (req.body.currency) form.append('currency', req.body.currency);2930 const r = await fetch(`${DEXT_BASE}/documents`, {31 method: 'POST',32 headers: { 'Authorization': `ApiKey ${DEXT_API_KEY}`, ...form.getHeaders() },33 body: form34 });35 return res.status(r.status).json(await r.json());36 }3738 res.status(405).json({ error: 'Method not allowed' });39});Pro tip: Verify the exact Authorization header format (ApiKey, Bearer, or a custom scheme) in the current Dext API documentation before deploying — using the wrong prefix returns 401 even with a valid key.
Expected result: The proxy function is deployed and accessible. A test POST with a base64-encoded JPEG returns a document ID from Dext. A test GET with that document ID returns a status field.
Capture a receipt photo in FlutterFlow and send it to the proxy
In FlutterFlow, build the capture screen. Add a Camera or Image Picker widget — click the widget on the canvas, open its Settings panel, and choose 'Camera' or 'Gallery' as the source. Set the maximum dimensions to 1920×1920 pixels to keep upload size reasonable without losing OCR accuracy. Add a 'Submit Receipt' button below the image widget. In the button's Action Flow Editor, add the following sequence: (1) an 'Update Page State' action to set a loading boolean to true, showing a progress indicator; (2) a 'Convert Image to Base64' action (available under Custom Actions if you need a Dart snippet, or use FlutterFlow's built-in media conversion in newer versions) to encode the selected image; (3) an API Call action targeting your proxy's upload endpoint — set the body to `{"imageBase64": "{{base64Image}}"}` using the encoded image; (4) capture the returned document ID into a Page State variable named `currentDocumentId`; (5) start a polling loop. Create a second API Group named 'Dext Proxy' with the base URL of your Firebase/Supabase function URL. Add two API Calls: 'Upload Receipt' (POST, body `{"imageBase64": "{{imageBase64}}", "currency": "{{currency}}"}`) and 'Get Document Status' (GET, endpoint `?documentId={{documentId}}`). In 'Response & Test' for 'Get Document Status', paste a sample Dext document response and generate JSON paths for `$.status`, `$.data.total_amount`, `$.data.date`, `$.data.supplier.name`, `$.data.category`.
1{2 "api_group": "Dext Proxy",3 "base_url": "https://us-central1-yourproject.cloudfunctions.net/dextProxy",4 "calls": [5 {6 "name": "Upload Receipt",7 "method": "POST",8 "endpoint": "/",9 "body": { "imageBase64": "{{imageBase64}}", "currency": "{{currency}}" },10 "variables": [11 { "name": "imageBase64", "type": "String" },12 { "name": "currency", "type": "String", "test_value": "GBP" }13 ],14 "json_paths": {15 "documentId": "$.id"16 }17 },18 {19 "name": "Get Document Status",20 "method": "GET",21 "endpoint": "/?documentId={{documentId}}",22 "variables": [23 { "name": "documentId", "type": "String" }24 ],25 "json_paths": {26 "status": "$.status",27 "amount": "$.data.total_amount",28 "date": "$.data.date",29 "supplier": "$.data.supplier.name",30 "category": "$.data.category"31 }32 }33 ]34}Pro tip: Compress the image before uploading — OCR accuracy does not significantly improve beyond 1200×1600 pixels, but file size increases upload time and proxy function execution costs.
Expected result: Tapping 'Submit Receipt' with a selected image sends the base64-encoded photo to the proxy. The proxy returns a document ID that is stored in Page State. A loading indicator is visible.
Poll for OCR results and display extracted fields
Because Dext's OCR is asynchronous, the app must keep checking the document status until it changes from 'processing' to 'ready' (verify the exact status values in Dext's API documentation — common values are 'pending', 'processing', 'processed', 'failed'). Implement a polling loop in FlutterFlow using a combination of a Timer and an Action Flow condition. In the Action Flow Editor on the capture screen, after capturing the document ID, add a 'Wait' action (set to 3000 ms), then call 'Get Document Status' with the stored document ID. Add a conditional branch: if `$.status == 'processed'` (or the equivalent 'ready' value), navigate to the Review Screen passing the extracted fields as page parameters. If the status is still 'processing', loop back to the Wait → poll cycle. Add a maximum retry counter (Page State integer) — after 20 attempts (60 seconds total), show an error Snackbar: 'Receipt is taking longer than expected — check Dext later'. On the Review Screen, create text fields pre-populated with the passed parameters: Vendor Name, Total Amount, Date, and Category. The user can edit any field before tapping 'Confirm' to save the expense record to your Firestore or Supabase database. If your Dext plan and the proxy support webhooks (Dext can POST to a URL when processing completes), use the webhook approach instead: Dext calls your Firebase/Supabase function, which updates a Firestore document, and the FlutterFlow app listens on a real-time stream from Firestore. This eliminates the polling loop entirely. If you'd prefer RapidDev to set up the webhook pattern or the full proxy architecture, the team builds FlutterFlow integrations like this every week — free scoping call at rapidevelopers.com/contact.
Pro tip: Store the polling result in Page State, not App State — the document ID and extracted fields are specific to the current capture session and should not persist across navigations.
Expected result: After upload, the app shows a 'Processing' indicator and periodically rechecks the document status. When processing completes, the Review Screen opens with Vendor, Amount, Date, and Category pre-filled from Dext's OCR output.
List all submitted documents and their statuses
Beyond single-document capture, a useful feature is a document list screen showing all submissions with their processing status. Add a third API Call to your Dext Proxy API Group named 'List Documents' — a GET to your proxy endpoint with an optional status filter (e.g. `?status=processed&limit=20`). The proxy calls `https://api.dext.com/v1/documents?status=processed&limit=20` with the API key. In FlutterFlow, add a new screen named 'Documents'. Set the 'List Documents' API Call as the Backend Query for the page, which automatically fetches on load. Add a ListView widget bound to the returned document array. Each list item shows the supplier name, amount, date, and a status badge (a Container with conditional background color: green for 'processed', yellow for 'processing', red for 'failed'). Tap on a list item to navigate to the Review Screen for that document. Pass the document ID as a parameter and call 'Get Document Status' on load to fetch the full extracted fields. Add a pull-to-refresh gesture to the ListView to let users manually refresh the list. Cache the list in Page State to avoid redundant API calls on every tab switch.
Pro tip: Filter the document list to 'processed' status by default so users see actionable items first. Offer a filter toggle for 'processing' documents so they can track in-progress uploads.
Expected result: The Documents screen shows a list of all submitted receipts with their vendor, amount, and processing status. Tapping any row opens the full extracted data in the Review Screen.
Common use cases
Mobile expense capture app for small business owners
A FlutterFlow app where employees snap receipt photos on their phones and submit them to Dext for automatic categorization. The app shows the extracted vendor, amount, date, and category for the user to confirm before submitting to the accounting system. The proxy handles the upload and polls for results, so the user sees a progress indicator and then the extracted data.
Build a mobile expense app where I can take a photo of a receipt, submit it, and see the extracted vendor name, amount, date, and expense category automatically filled in for me to review and confirm.
Copy this prompt to try it in FlutterFlow
Invoice processing dashboard for bookkeepers
A FlutterFlow tablet app for a bookkeeper's workflow where clients upload invoices and receipts throughout the day. The app shows a list of pending documents, their OCR status, and the extracted data once processing completes. A status indicator shows Pending, Processing, or Ready for each document.
Show a list of uploaded documents with their processing status. When a document is marked Ready, show the extracted fields — supplier, amount, date, and tax — in a detail view for review.
Copy this prompt to try it in FlutterFlow
Travel expense tracker with OCR auto-fill
A FlutterFlow app for employees on business trips that captures receipts in the field and pre-fills an expense report form using Dext's OCR output. The user takes a photo, waits for OCR, and the form fields for amount, date, and merchant are automatically populated — they just add a note and category.
After I take a receipt photo, automatically fill in the expense form with the extracted amount, date, and merchant name from OCR. Let me add a description and category, then submit.
Copy this prompt to try it in FlutterFlow
Troubleshooting
HTTP 403 Forbidden — API key appears valid but all requests are rejected
Cause: API access has not been enabled for the Dext account, or the API key was generated before API access was activated. Even a correctly formatted key returns 403 if the account tier does not include API access.
Solution: Contact Dext support or your account manager to confirm API access is enabled for the account. Ask them to verify the account tier and activate API access if needed. Once enabled, regenerate the API key and test again with the proxy.
Document status stays 'processing' indefinitely and never changes to 'processed'
Cause: The receipt image quality is too low (blurry, dark, or cut off), causing OCR to fail or stall. Alternatively, the Dext API may return a 'failed' status for unreadable documents.
Solution: Check the raw status response in your proxy logs — Dext may be returning a 'failed' status with an error reason that is being ignored by the polling loop. Add handling for the 'failed' status in the FlutterFlow conditional branch and show the user a message like 'Receipt could not be read — please retake the photo in better light.' Ensure the uploaded image is at least 800px on the shorter side and well-lit.
CORS error or XMLHttpRequest error on FlutterFlow web preview
Cause: The Dext API (or a misconfigured proxy) is blocking browser-origin requests. FlutterFlow web preview runs in a browser and enforces CORS.
Solution: Ensure the Firebase Cloud Function adds `res.set('Access-Control-Allow-Origin', '*')` before sending any response, including OPTIONS preflight responses. Confirm the proxy is handling the `OPTIONS` method with a 204 response. All calls from FlutterFlow web must go through the proxy — never directly to api.dext.com.
Upload fails with a multipart/form-data error or 400 Bad Request
Cause: The proxy is not constructing the multipart form correctly — missing file content-type, wrong field name ('file' vs another expected name), or the base64 image is not decoded back to binary before sending.
Solution: Check the current Dext API documentation for the exact field name and content-type expected in the document upload endpoint. Ensure `Buffer.from(req.body.imageBase64, 'base64')` is used in the proxy to decode the image before adding it to FormData. Log the form headers in the proxy to verify the content-type boundary is being generated correctly by the FormData library.
Best practices
- Confirm API access is enabled on the Dext account tier before building — a 403 error from a valid key is always an account-access issue, not a code issue.
- Store the Dext API key exclusively in Firebase Functions environment config or Supabase Edge Function secrets — never in FlutterFlow API Call headers or App Values.
- Compress receipt images to under 2MB before uploading to reduce proxy function execution time and Dext processing cost.
- Always handle the 'failed' OCR status in the polling loop with a user-friendly message and a retake option — blurry or dark receipts fail regularly.
- Use a maximum polling retry count (20 attempts over ~60 seconds) and show a timeout message rather than polling indefinitely, which drains the user's battery.
- For production apps with high document volumes, prefer a webhook-driven approach over polling — Dext posts to your Firebase/Supabase function when processing completes, updating Firestore/Supabase for the app to read via a real-time stream.
- Filter the document list to 'processed' status by default so users see actionable items; provide a toggle for in-progress documents.
- Verify current Dext API field names and status values in the official documentation before mapping JSON paths — field names occasionally change between API versions.
Alternatives
If you need a full accounting ledger rather than just receipt capture, QuickBooks provides invoicing, expenses, and financial reports through its OAuth-protected REST API.
Xero is a strong alternative for UK, Australian, and New Zealand businesses needing accounting and expense management rather than standalone receipt OCR.
Zoho Books offers expense tracking and receipt management as part of a broader accounting suite, with a REST API available on paid plans.
Frequently asked questions
Does Receipt Bank still exist, or is it Dext now?
Receipt Bank rebranded to Dext in 2021. The product is the same — OCR-powered receipt and invoice capture for bookkeepers — but all API endpoints are now under `api.dext.com`. If you have documentation or tutorials referencing Receipt Bank's old domain, update them to use the Dext base URL.
Can I put the Dext API key directly in a FlutterFlow API Call header?
No. FlutterFlow compiles to a Flutter app that runs on the user's device. Any API key placed in a FlutterFlow API Call header is embedded in the compiled binary and can be extracted by decompiling the APK or IPA. A Dext API key grants full access to all financial documents captured by the account. Always route Dext calls through a Firebase Cloud Function or Supabase Edge Function that reads the key from a server-side secret.
How long does Dext OCR processing take?
Processing time varies by document complexity, image quality, and account plan — typically a few seconds to about a minute for clear, well-lit receipts. Blurry, dark, or multi-page documents take longer and may fail. The Dext API returns a document status field that the app polls; check the current Dext API documentation for the exact status values and average processing times for your plan.
Can I use webhooks instead of polling for OCR completion?
Yes, and it is the preferred approach for production apps. Configure Dext to send a webhook to your Firebase or Supabase function URL when a document finishes processing. The function verifies the webhook signature, updates the document record in Firestore or Supabase, and the FlutterFlow app reads the updated record via a real-time stream — no polling loop needed. Check the current Dext API documentation for webhook configuration steps.
Does this integration work if users upload receipts from multiple client accounts?
The Dext API key is account-scoped — all documents uploaded via one key go into one Dext account. For multi-client use, each client needs their own Dext account and API key, which the proxy looks up by client ID at request time. Store each client's API key in Firestore or Supabase (encrypted) and retrieve it in the proxy based on the authenticated user's client association.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation