Skip to main content
RapidDev - Software Development Agency
lovable-integrationsEdge Function Integration

How to Integrate Lovable with Dext (formerly Receipt Bank)

Integrating Dext (formerly Receipt Bank) with Lovable uses Edge Functions to call the Dext API for document upload, OCR extraction, and accounting sync. Store your Dext API credentials in Cloud Secrets, create an Edge Function to handle receipt uploads and fetch extracted data, and build an expense capture workflow where users photograph receipts and see structured data appear automatically. Setup takes 45 minutes.

What you'll learn

  • How to store Dext API credentials securely in Lovable Cloud Secrets
  • How to upload receipt images to Dext via a Deno Edge Function
  • How to retrieve OCR-extracted data (vendor, date, amount, tax) from Dext
  • How to build a receipt capture workflow with photo upload and automatic data extraction
  • How to sync extracted expense data to an accounting system via Dext's export features
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate14 min read45 minutesFinanceMarch 2026RapidDev Engineering Team
TL;DR

Integrating Dext (formerly Receipt Bank) with Lovable uses Edge Functions to call the Dext API for document upload, OCR extraction, and accounting sync. Store your Dext API credentials in Cloud Secrets, create an Edge Function to handle receipt uploads and fetch extracted data, and build an expense capture workflow where users photograph receipts and see structured data appear automatically. Setup takes 45 minutes.

Why integrate Dext with Lovable?

Manual receipt entry is one of the most hated tasks in business operations — employees lose receipts, miskey amounts, and spend hours on expense reports that take accountants more hours to verify. Dext (formerly Receipt Bank) solves this by using OCR and machine learning to extract vendor name, date, total amount, tax amount, currency, and category from a photo of a receipt or invoice. What used to take 5 minutes per receipt takes under 10 seconds with Dext, and the extracted data flows directly into QuickBooks, Xero, or Sage.

For Lovable developers, Dext's API opens up several valuable use cases. You can build a custom receipt capture interface embedded in your existing app rather than requiring employees to use a separate Dext app. You can create automated expense approval workflows where receipts are captured, categorized, and routed for manager approval all within your internal tool. You can build client portals where accountants receive structured expense data from clients who simply photograph receipts through your app.

Dext's API works through a straightforward pattern: upload a document (image or PDF), Dext processes it asynchronously (typically 30-120 seconds), and you poll for or receive a webhook notification when the extraction is complete. The extracted data includes all the key fields an accountant needs to record the expense. This tutorial walks through building a receipt capture workflow in Lovable with real-time status updates as Dext processes each upload.

Integration method

Edge Function Integration

Dext has no native Lovable connector. Integration requires Supabase Edge Functions to handle document upload via the Dext API, poll for OCR extraction results, and fetch processed receipt data. API credentials are stored in Cloud Secrets. The Edge Function proxies multipart file uploads from the Lovable frontend to Dext's API, then retrieves the extracted fields (vendor, date, total, tax) when processing completes.

Prerequisites

  • A Lovable project with Cloud enabled
  • A Dext account — sign up at dext.com (Partner API access requires contacting Dext's sales team for API credentials)
  • Dext API credentials (API key or OAuth client credentials from the Dext Partner Program)
  • Understanding that Dext processes documents asynchronously — plan for a polling or webhook pattern in your UI

Step-by-step guide

1

Obtain Dext API credentials

Dext's API access works differently from most SaaS tools — it's part of their Partner Program rather than a self-serve developer portal. To get API credentials, go to dext.com and contact their sales or partner team requesting API access. Describe your use case (building an internal tool, a client portal, etc.) and they will provision an API key for your account. For testing purposes, Dext provides a sandbox environment where you can upload test receipts and see the OCR extraction in action without processing real documents. The sandbox API base URL is typically https://api.dext.com/v1 with a test API key they provide during onboarding. Dext's API authenticates using an API key in the Authorization header as a Bearer token. Some partner integrations use OAuth2 — ask your Dext contact which authentication method your account is provisioned with. This tutorial assumes Bearer token authentication with a single API key. While waiting for API credentials, explore Dext's documentation at developers.dext.com to understand the document lifecycle: upload → processing → processed (with extracted fields) or error. Familiarize yourself with the document object structure, which includes supplier name, date, total amount, tax amount, currency, category, and the raw OCR confidence scores for each field.

Pro tip: Dext's OCR is optimized for structured documents like receipts, invoices, and bills. It works best on clear, well-lit photos where the receipt text is readable. Blurry, crumpled, or partially cut-off receipts will have lower confidence scores and may require manual correction.

Expected result: You have a Dext API key and sandbox access. You can review the Dext API documentation and understand the document upload and retrieval flow.

2

Store Dext API credentials in Cloud Secrets

Open your Lovable project and click the '+' icon in the top-right editor area to access the panels. Select 'Cloud' and expand the 'Secrets' section. Add your Dext credentials as encrypted environment variables accessible only from Edge Functions. Add DEXT_API_KEY with your API key value. Add DEXT_API_BASE with the base URL: use 'https://api.dext.com/v1' for production or the sandbox URL provided by Dext for testing. If your integration uses a specific Dext organization ID or company ID, add DEXT_COMPANY_ID as well — some API endpoints are scoped to a company. Dext's API key grants access to your entire Dext account including all documents, clients, and accounting integrations. Protecting it with Cloud Secrets is critical. The key must never appear in frontend code — a user with access to your app's network requests would be able to view and download all documents ever processed in your Dext account. Lovable's security infrastructure blocks approximately 1,200 hardcoded API keys per day; use it as designed by always storing credentials in Cloud Secrets.

Pro tip: If Dext provides both a sandbox API key and a production API key, store both in Cloud Secrets as DEXT_API_KEY_SANDBOX and DEXT_API_KEY_PROD, then add DEXT_ENVIRONMENT='sandbox' to control which key your Edge Function uses. Switch to production by updating DEXT_ENVIRONMENT without any code changes.

Expected result: DEXT_API_KEY and DEXT_API_BASE appear in your Cloud Secrets list. The API key value is hidden. No credential appears in source code.

3

Create the receipt upload Edge Function

Create a Supabase Edge Function to handle receipt uploads. The challenge with document upload APIs is that the frontend needs to send a file (binary image data) through your Edge Function to Dext. The flow is: the user selects a file in the browser → the browser sends the file to your Edge Function as a multipart form POST → the Edge Function forwards the file to Dext's upload endpoint with your API key. Dext's document upload endpoint is a POST to /api/v1/documents (or similar — check your partner documentation for the exact path). The request body should be multipart/form-data with the file attached, plus any metadata fields like document type and source. Dext returns a document object with an ID and status 'processing'. Store the document ID in a Supabase uploads table immediately after upload. Include: document_id, user_id, original_filename, upload_status ('processing'), uploaded_at. This lets you track all uploads and poll for their completion status. For the polling logic, create a second action in the Edge Function (or a separate function) that accepts a document_id and calls GET /api/v1/documents/{id} to check the current status and retrieve extracted fields when processing is complete. Return the full document object including confidence scores so your UI can flag low-confidence extractions for user review.

Lovable Prompt

Create a Supabase Edge Function at supabase/functions/dext-receipts/index.ts. Support two actions: action='upload' receives a base64-encoded file string and filename from the request body, decodes it, and POSTs it as multipart/form-data to DEXT_API_BASE + /api/v1/documents with Authorization: Bearer DEXT_API_KEY header; store the returned document_id to a Supabase uploads table (user_id, document_id, filename, status='processing', created_at); return {document_id}. action='status' takes a document_id, calls GET DEXT_API_BASE + /api/v1/documents/{document_id}, and returns {status, supplier_name, date, total_amount, tax_amount, currency, category} when status is 'processed'.

Paste this in Lovable chat

supabase/functions/dext-receipts/index.ts
1// supabase/functions/dext-receipts/index.ts
2import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
3import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
4
5const DEXT_KEY = Deno.env.get("DEXT_API_KEY") ?? "";
6const DEXT_BASE = Deno.env.get("DEXT_API_BASE") ?? "https://api.dext.com/v1";
7const corsHeaders = {
8 "Access-Control-Allow-Origin": "*",
9 "Access-Control-Allow-Methods": "POST, OPTIONS",
10 "Access-Control-Allow-Headers": "Content-Type, Authorization",
11};
12
13serve(async (req) => {
14 if (req.method === "OPTIONS") return new Response(null, { headers: corsHeaders });
15 const supabase = createClient(Deno.env.get("SUPABASE_URL") ?? "", Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? "");
16
17 try {
18 const body = await req.json();
19 const { action } = body;
20
21 if (action === "upload") {
22 const { file_base64, filename, user_id } = body;
23 const fileBytes = Uint8Array.from(atob(file_base64), c => c.charCodeAt(0));
24 const formData = new FormData();
25 formData.append("document", new Blob([fileBytes]), filename);
26
27 const res = await fetch(`${DEXT_BASE}/documents`, {
28 method: "POST",
29 headers: { "Authorization": `Bearer ${DEXT_KEY}` },
30 body: formData,
31 });
32 const data = await res.json();
33 if (!res.ok) throw new Error(data.message ?? "Upload failed");
34
35 await supabase.from("uploads").insert({
36 document_id: data.id,
37 user_id,
38 filename,
39 status: "processing",
40 created_at: new Date().toISOString(),
41 });
42
43 return new Response(JSON.stringify({ document_id: data.id }), {
44 headers: { ...corsHeaders, "Content-Type": "application/json" },
45 });
46 }
47
48 if (action === "status") {
49 const { document_id } = body;
50 const res = await fetch(`${DEXT_BASE}/documents/${document_id}`, {
51 headers: { "Authorization": `Bearer ${DEXT_KEY}` },
52 });
53 const data = await res.json();
54 if (!res.ok) throw new Error(data.message ?? "Status fetch failed");
55
56 const result = {
57 status: data.status,
58 supplier_name: data.supplier?.name ?? "",
59 date: data.date ?? "",
60 total_amount: data.total ?? null,
61 tax_amount: data.tax ?? null,
62 currency: data.currency ?? "GBP",
63 category: data.category?.name ?? "",
64 };
65
66 if (data.status === "processed") {
67 await supabase.from("uploads").update({ status: "processed" }).eq("document_id", document_id);
68 }
69
70 return new Response(JSON.stringify(result), { headers: { ...corsHeaders, "Content-Type": "application/json" } });
71 }
72
73 return new Response(JSON.stringify({ error: "Unknown action" }), { status: 400, headers: corsHeaders });
74 } catch (err) {
75 return new Response(JSON.stringify({ error: err.message }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } });
76 }
77});

Pro tip: Convert the file to base64 on the frontend before sending to the Edge Function: const reader = new FileReader(); reader.readAsDataURL(file); — then strip the data URL prefix (data:image/jpeg;base64,) and send only the base64 content. This avoids multipart form data complexity in the Edge Function request parsing.

Expected result: The dext-receipts Edge Function is deployed. Uploading a receipt image calls the Edge Function, which uploads to Dext and stores the document_id in Supabase. Calling action='status' with the document_id returns the processing status.

4

Build the receipt capture UI with polling

Build the frontend receipt capture workflow. The key UX pattern is: the user uploads a file → sees 'Processing receipt...' with a spinner → the app polls the Edge Function every 5 seconds → when processing is complete, the form auto-fills with extracted data → the user reviews, edits if needed, and saves. Create a file input component that accepts image files (JPEG, PNG, HEIC for mobile photos) and PDF files. When a file is selected, display a preview of the image or a PDF icon. Show a file size limit warning if the file exceeds Dext's limit (typically 25MB). For the polling mechanism, use React's useEffect with a setInterval that calls the Edge Function's status action every 5 seconds. When the response shows status='processed', clear the interval and populate the form fields. Use a ref to track the interval ID to prevent memory leaks. For the expense form, display fields for: vendor name (from supplier_name), date (from date), total amount (from total_amount), tax amount (from tax_amount), currency (from currency), and category (from category). Mark fields with low confidence scores with a yellow warning indicator so users know which values to double-check. When the user clicks Submit, save the expense to the Supabase expenses table with all fields plus the document_id reference for audit trail purposes.

Lovable Prompt

Build a receipt upload page. Add a drag-and-drop file upload area accepting images and PDFs. When a file is selected: 1) convert it to base64, 2) call the dext-receipts Edge Function with action='upload' to get a document_id, 3) show a 'Extracting receipt data...' spinner, 4) poll every 5 seconds with action='status' and the document_id until status='processed', 5) auto-fill a form with supplier_name, date, total_amount, tax_amount, currency, and category from the response. Add a Submit button that saves the expense to a Supabase expenses table. Show a success message after saving.

Paste this in Lovable chat

Pro tip: Set a maximum polling timeout of 3 minutes (36 polls at 5-second intervals). If Dext hasn't finished processing after 3 minutes, show an error message and let the user enter the data manually. Dext typically processes in under 2 minutes, but timeouts happen occasionally.

Expected result: Users can photograph or upload a receipt. The app shows a processing indicator and then auto-fills expense fields when Dext's OCR completes. Users review and submit the pre-filled form to save to Supabase.

Common use cases

Employee receipt capture mobile workflow

An internal expense tool lets employees photograph receipts using their phone's camera directly in the Lovable app. The image is uploaded to Dext via an Edge Function, which returns a document ID. The app polls for processing completion and then displays the extracted data — vendor, date, amount, category — pre-filled in an expense form. The employee reviews and submits, and the record is saved to Supabase for accounting review.

Lovable Prompt

Build a receipt capture page. Add a camera/file upload input. When a receipt image is selected, call an Edge Function to upload it to Dext's API and return a document_id. Poll the Edge Function every 5 seconds with that document_id until the status is 'processed'. When processed, fill in form fields for vendor, date, total_amount, tax_amount, and currency with the extracted values. Let the user review and edit before clicking Submit, which saves the expense to a Supabase expenses table.

Copy this prompt to try it in Lovable

Accountant client portal for receipt collection

An accountant builds a client portal where business clients submit receipts throughout the month. Each receipt is automatically processed by Dext and the structured data is stored in a Supabase table. At month end, the accountant reviews a clean list of categorized expenses with extracted data instead of a shoebox of crumpled receipts, and exports them to the client's accounting software.

Lovable Prompt

Create a client expense portal. Clients can upload receipts or invoice PDFs through a drag-and-drop upload area. Each file is sent to Dext via an Edge Function and stored in an uploads table with client_id, document_id, status='pending'. A background poll updates the status to 'processed' and fills in vendor, date, amount, tax, category fields when Dext finishes. Build an accountant review page showing all processed receipts for a client with totals by category.

Copy this prompt to try it in Lovable

Automated expense report generation

A project management tool automatically generates expense reports from receipts submitted against specific projects. Employees tag receipts to project codes, Dext extracts the financial data, and the app compiles a formatted expense report per project showing all receipts, totals, and category breakdowns that can be exported to PDF for client billing.

Lovable Prompt

Add project expense tracking. When uploading a receipt, let users select a project from a dropdown. After Dext extraction, store the expense in Supabase with project_id, vendor, date, amount, category. Build an expense report page filtered by project showing: all receipts in a table, subtotals by category, a grand total, and an Export PDF button that generates a formatted expense report using the data from the Supabase expenses table.

Copy this prompt to try it in Lovable

Troubleshooting

Upload returns 401 Unauthorized from Dext API

Cause: The DEXT_API_KEY Cloud Secret is missing, has a typo, or the key has been revoked. Dext also requires the Authorization header to use the exact format 'Bearer YOUR_KEY' with no extra spaces.

Solution: Open Cloud → Secrets and verify DEXT_API_KEY is present and has the correct value. Check Cloud Logs for the full request that was sent — confirm the Authorization header is formatted as 'Bearer ' followed by the key with no newlines or spaces. If the key was recently rotated, update the Cloud Secret with the new value.

typescript
1headers: { "Authorization": `Bearer ${DEXT_KEY}`.trim() }

Document status stays 'processing' indefinitely and never becomes 'processed'

Cause: The document may be in an error state that your polling code doesn't handle. Dext returns statuses like 'failed', 'duplicate', or 'low_quality' in addition to 'processed'. If your polling code only checks for 'processed', it will loop forever on error states.

Solution: Update your polling logic to stop and show an error when status is not 'processing' and not 'processed'. Handle 'failed' and 'low_quality' statuses by allowing manual entry. Also check that the uploaded file is a supported format — Dext supports JPEG, PNG, TIFF, PDF, and HEIC but not WebP or other formats.

typescript
1const terminalStatuses = ['processed', 'failed', 'duplicate', 'low_quality'];
2if (terminalStatuses.includes(data.status)) {
3 clearInterval(pollInterval);
4 if (data.status !== 'processed') {
5 setError(`Document processing ended with status: ${data.status}. Please enter details manually.`);
6 }
7}

Extracted amounts show wrong currency or incorrect decimal formatting

Cause: Dext returns amounts in the currency detected from the receipt, which may use different decimal conventions (e.g., European receipts use commas as decimal separators). The currency field indicates the detected currency but doesn't automatically convert amounts.

Solution: Display amounts using the returned currency code alongside the amount value. Use JavaScript's Intl.NumberFormat with the detected currency for display formatting. For multi-currency expense reports, store the original currency and amount alongside a converted base currency amount if you need to aggregate across currencies.

typescript
1const formatted = new Intl.NumberFormat('en-US', {
2 style: 'currency',
3 currency: data.currency ?? 'USD',
4}).format(data.total_amount);

Best practices

  • Store the Dext document_id alongside every saved expense record so you can retrieve the original document and extraction data for audit purposes.
  • Display OCR confidence indicators for extracted fields — Dext returns confidence scores that tell you which fields were extracted reliably vs. which may need user review.
  • Implement a manual fallback form that pre-fills from partial extraction data when Dext returns a low-quality or failed status, rather than requiring the user to start over.
  • Validate extracted amounts against receipt images client-side before final submission — show the receipt image alongside the pre-filled form so users can verify the extraction.
  • Use Dext's duplicate detection feature (documents with the same supplier, date, and amount) to prevent employees from submitting the same receipt twice.
  • For accountant-facing views, show the original receipt image alongside the extracted data so accountants can quickly verify unusual amounts without downloading separate files.
  • Implement file size validation on the frontend before upload — Dext rejects files over 25MB, and showing a clear error before upload saves a failed API call.

Alternatives

Frequently asked questions

How accurate is Dext's OCR for receipt data extraction?

Dext claims over 99% data accuracy for clearly legible receipts, backed by their combination of machine learning OCR and human review for low-confidence extractions. In practice, accuracy is very high for typed receipts from modern POS systems and lower for handwritten receipts or crumpled/damaged documents. Dext's confidence scores help identify which extractions need user review.

What's the difference between Dext and Expensify for a Lovable app?

Dext is primarily an accountant-focused tool that converts receipts and invoices into structured bookkeeping data — it's designed for the accounting workflow. Expensify is employee-focused with expense report submission, approval workflows, and reimbursement processing. Choose Dext when you're building accountant tools or client portals; choose Expensify when you're building employee expense management with approval chains.

Can Dext process invoices and bills as well as receipts?

Yes — Dext handles receipts, supplier invoices, bills, statements, and bank statements. The extraction fields vary by document type: invoices include PO numbers, payment terms, and line items; receipts focus on transaction totals. Specify the document_type in your upload request to get optimized extraction for each document category.

How do I sync Dext-extracted data to QuickBooks or Xero?

Dext has native integrations with QuickBooks, Xero, Sage, and other accounting platforms built into the Dext platform itself. Once data is extracted by Dext, you can trigger the sync to accounting software from within Dext's dashboard or via API. In your Lovable app, after saving an approved expense, you can call a Dext API endpoint to push the document to the connected accounting integration, or let the accountant manage that step within Dext's own interface.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.