Skip to main content
RapidDev - Software Development Agency
v0-integrationsNext.js API Route

How to Integrate Receipt Bank (Dext) with V0

To use Dext (formerly Receipt Bank) with V0, generate an expense upload UI in V0, then create a Next.js API route at app/api/dext/route.ts that calls the Dext API to submit receipts and invoices for automated OCR extraction. Store your Dext API key in Vercel Dashboard → Settings → Environment Variables as DEXT_API_KEY. Dext processes documents asynchronously, so poll the items endpoint for extracted data.

What you'll learn

  • How to generate a receipt upload interface and expense dashboard with V0
  • How to create a Next.js API route that submits documents to the Dext API
  • How to store Dext credentials securely in Vercel environment variables
  • How to retrieve and display extracted expense data from the Dext items endpoint
  • How to handle asynchronous document processing and polling in Next.js
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate18 min read30 minutesOtherApril 2026RapidDev Engineering Team
TL;DR

To use Dext (formerly Receipt Bank) with V0, generate an expense upload UI in V0, then create a Next.js API route at app/api/dext/route.ts that calls the Dext API to submit receipts and invoices for automated OCR extraction. Store your Dext API key in Vercel Dashboard → Settings → Environment Variables as DEXT_API_KEY. Dext processes documents asynchronously, so poll the items endpoint for extracted data.

Automating Expense Processing in Your V0 App with Dext

Dext (previously known as Receipt Bank) sits at the heart of modern bookkeeping workflows for small businesses and accountants. It ingests photos of receipts, scanned invoices, and PDF bills and returns structured data — supplier name, total, VAT, date, category — ready to sync into accounting software. For founders building internal finance tools, client portals, or expense management dashboards with V0, connecting to Dext via API means you can automate the tedious receipt-data-entry step entirely.

The integration pattern follows V0's standard API route model: V0 generates the front-end file upload component and expense data display, while a Next.js API route at app/api/dext handles the Dext API calls server-side. The Dext REST API accepts document uploads as multipart form data, stores them in a processing queue, and returns item IDs. Because OCR extraction is asynchronous — Dext processes documents in the background — your API route needs to handle both document submission (POST) and data retrieval (GET) as separate operations.

One V0-specific consideration: V0 generates React components that run in the browser, so file handling uses standard browser File APIs and FormData. The Next.js API route receives the uploaded file, reconstructs it as a server-side Blob or Buffer, and forwards it to Dext in the correct multipart format. This server-side proxying pattern is essential because Dext's API requires authentication headers that must not be exposed to the browser.

Integration method

Next.js API Route

V0 generates the file upload and expense dashboard UI components, while a Next.js API route handles multipart document submission to the Dext API. The API route keeps your Dext API credentials server-side and acts as the secure proxy between the browser and Dext's document processing pipeline. Extracted expense data is then fetched from the Dext items endpoint and displayed back in your V0-generated dashboard.

Prerequisites

  • A V0 account with a Next.js project at v0.dev
  • A Dext account at dext.com with API access enabled (requires a paid Dext plan)
  • Your Dext API key from Dext Dashboard → Settings → Integrations → API
  • A Vercel account with your V0 project deployed via GitHub
  • Basic familiarity with file upload handling in web apps

Step-by-step guide

1

Generate the Receipt Upload UI in V0

Start by prompting V0 to build the front-end interface for document submission. You need two main UI elements: a file upload component and an expense list or dashboard to display the results. V0 is well suited for building polished file upload interfaces with drag-and-drop support and a fallback file picker button — both are standard patterns in Tailwind CSS and React. When prompting V0, specify that the upload button should POST to /api/dext/upload with the file as multipart form data. Critically, the frontend should use FormData to send the file rather than JSON encoding — binary file content cannot be sent as JSON. The V0-generated component should build a FormData object, append the file to it, and fetch the API route without setting a Content-Type header (the browser automatically sets the correct multipart/form-data boundary when you omit it). For the expense list section, tell V0 to fetch from /api/dext/items and render the returned data in a table or card grid. Include a refresh button or auto-polling every 10-15 seconds so users see when Dext finishes processing their document. V0 can generate the polling logic using a useEffect hook with setInterval — ask it to fetch the items list every 15 seconds and stop once the item status changes from 'processing' to 'completed'. Review V0's generated fetch call to confirm it sends FormData correctly. The fetch should look like: const formData = new FormData(); formData.append('file', file); fetch('/api/dext/upload', { method: 'POST', body: formData }). If V0 adds a Content-Type header manually, remove it — the browser must set this automatically to include the multipart boundary.

V0 Prompt

Create an expense upload page with a drag-and-drop file zone that accepts JPG, PNG, and PDF files up to 10MB. When a file is dropped or selected, show a preview thumbnail and a Submit button. The Submit button should POST the file as FormData to /api/dext/upload and show a loading spinner during upload. Below the upload zone, render a table with columns: Supplier, Date, Amount, Currency, Category, Status. Fetch data from /api/dext/items every 15 seconds to refresh the table.

Paste this in V0 chat

Pro tip: Ask V0 to add file type validation in the component — check that file.type is one of 'image/jpeg', 'image/png', 'image/gif', or 'application/pdf' before submitting. Dext supports these formats and will reject others.

Expected result: V0 generates a file upload interface with drag-and-drop and a data table. The component uses FormData for uploads and polls /api/dext/items for processed results.

2

Create the Document Upload API Route

Create the server-side API route that receives the file from the browser and forwards it to Dext's document ingestion endpoint. In your project's file structure, create app/api/dext/upload/route.ts. This route runs as a Vercel serverless function, meaning it has access to your Dext API key via process.env and its code is never exposed to the browser. The Dext API accepts document uploads at POST https://api.dext.com/v1/items with the file as multipart form data. The request must include an Authorization header with your API key in the format Bearer {DEXT_API_KEY}. You also need to set the correct supplier context — Dext organizes documents by client or user, so if you have a multi-tenant app, you will need to include the relevant inbox or client identifier in the request. In the Next.js App Router, handling incoming multipart form data uses request.formData(). This gives you a FormData object from which you can extract the file using formData.get('file'). The returned value is a File or Blob object. To forward this to Dext, construct a new FormData with the file appended, then pass that FormData as the body to your fetch call to the Dext API. Handle errors carefully in this route. Dext returns specific HTTP status codes: 201 for successful document submission, 400 for invalid file types, 401 for authentication failures, and 422 for malformed requests. Return meaningful error responses to the client so the V0-generated UI can display helpful messages instead of a generic 'Something went wrong' alert. For larger files or slow connections, be aware that Vercel's default serverless function timeout is 10 seconds on the Hobby plan. Dext document uploads typically complete quickly, but if you experience timeouts on large PDF files, consider upgrading to a Pro plan for the longer 60-second limit or compress images before upload on the client side.

V0 Prompt

Create a Next.js API route at app/api/dext/upload/route.ts that accepts POST requests with multipart form data. Extract the file from the request using request.formData(), forward it to the Dext API at https://api.dext.com/v1/items using the DEXT_API_KEY environment variable in an Authorization Bearer header, and return the Dext item ID in the response JSON. Handle 400 and 401 errors from Dext and return appropriate error messages.

Paste this in V0 chat

app/api/dext/upload/route.ts
1import { NextRequest, NextResponse } from 'next/server';
2
3export async function POST(request: NextRequest) {
4 try {
5 const formData = await request.formData();
6 const file = formData.get('file') as File | null;
7
8 if (!file) {
9 return NextResponse.json(
10 { error: 'No file provided' },
11 { status: 400 }
12 );
13 }
14
15 // Validate file type
16 const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
17 if (!allowedTypes.includes(file.type)) {
18 return NextResponse.json(
19 { error: 'Invalid file type. Accepted: JPEG, PNG, GIF, PDF' },
20 { status: 400 }
21 );
22 }
23
24 // Forward to Dext API
25 const dextFormData = new FormData();
26 dextFormData.append('file', file);
27
28 const response = await fetch('https://api.dext.com/v1/items', {
29 method: 'POST',
30 headers: {
31 Authorization: `Bearer ${process.env.DEXT_API_KEY}`,
32 },
33 body: dextFormData,
34 });
35
36 if (!response.ok) {
37 const errorText = await response.text();
38 console.error('Dext API error:', response.status, errorText);
39 return NextResponse.json(
40 { error: `Dext API error: ${response.status}` },
41 { status: response.status }
42 );
43 }
44
45 const data = await response.json();
46 return NextResponse.json({ itemId: data.id, status: data.status });
47 } catch (error) {
48 console.error('Upload error:', error);
49 return NextResponse.json(
50 { error: 'Failed to upload document' },
51 { status: 500 }
52 );
53 }
54}

Pro tip: Dext processes documents asynchronously. The upload endpoint returns an item ID immediately; the extracted data becomes available via the items endpoint after a short delay (typically 30 seconds to 2 minutes depending on document complexity).

Expected result: Uploading a receipt JPEG or PDF from the browser calls this API route, which forwards the document to Dext and returns an item ID. The browser can then poll the items endpoint using this ID.

3

Create the Items Retrieval API Route

Create a second API route to fetch processed expense data from Dext. Create app/api/dext/items/route.ts for fetching the full list of items, and optionally app/api/dext/items/[id]/route.ts for fetching a single item by ID. The items endpoint is what the V0-generated dashboard polls to show extracted receipt data. The Dext GET /v1/items endpoint returns an array of item objects. Each item includes fields like id, status (processing or completed), supplier_name, date, total, currency, tax, category, and a thumbnail_url. The status field is crucial for polling — only when status is completed will the extracted fields like supplier_name and total be populated. Items with status processing will have these fields as null or empty strings. For the single-item route (app/api/dext/items/[id]/route.ts), extract the id from the route params using the params argument passed to the GET function. Fetch from https://api.dext.com/v1/items/{id} and return the full item data. This is useful for the three-step invoice review flow where the user uploads a document, waits for processing, then reviews the extracted data in a pre-filled form. Consider adding pagination support to the items list endpoint. Dext's API supports page and per_page query parameters. For an expense dashboard that might have hundreds of items, you do not want to fetch everything at once. Accept page and limit as query parameters in your Next.js route, forward them to Dext, and include pagination metadata in your response so the V0-generated UI can show 'Load more' or page number controls. For production deployments, consider caching the items response in Vercel's data cache using Next.js fetch caching with revalidate: 30 to reduce the number of Dext API calls from your polling frontend, while still keeping data reasonably fresh.

V0 Prompt

Create a Next.js API route at app/api/dext/items/route.ts that fetches all items from the Dext API at https://api.dext.com/v1/items using DEXT_API_KEY in an Authorization Bearer header. Return the array of items as JSON. Also create app/api/dext/items/[id]/route.ts that fetches a single item by ID from https://api.dext.com/v1/items/{id} and returns the item data including supplier_name, date, total, currency, and status fields.

Paste this in V0 chat

app/api/dext/items/route.ts
1import { NextRequest, NextResponse } from 'next/server';
2
3export async function GET(request: NextRequest) {
4 try {
5 const { searchParams } = new URL(request.url);
6 const page = searchParams.get('page') || '1';
7 const perPage = searchParams.get('per_page') || '25';
8
9 const response = await fetch(
10 `https://api.dext.com/v1/items?page=${page}&per_page=${perPage}`,
11 {
12 headers: {
13 Authorization: `Bearer ${process.env.DEXT_API_KEY}`,
14 'Content-Type': 'application/json',
15 },
16 // Revalidate every 30 seconds to reduce API calls
17 next: { revalidate: 30 },
18 }
19 );
20
21 if (!response.ok) {
22 return NextResponse.json(
23 { error: `Dext API error: ${response.status}` },
24 { status: response.status }
25 );
26 }
27
28 const data = await response.json();
29 return NextResponse.json({
30 items: data.items || data,
31 total: data.total,
32 page: parseInt(page),
33 });
34 } catch (error) {
35 console.error('Items fetch error:', error);
36 return NextResponse.json(
37 { error: 'Failed to fetch items' },
38 { status: 500 }
39 );
40 }
41}

Pro tip: Add a status query parameter filter so the frontend can request only completed items: ?status=completed. Forward this to Dext's API as a query param to avoid returning items that are still being processed.

Expected result: The dashboard frontend can call GET /api/dext/items and receive a list of expense items with extracted data. Items with status 'processing' appear in the list but have empty supplier/amount fields until Dext completes extraction.

4

Add Environment Variables in Vercel

Your API routes read DEXT_API_KEY from environment variables. You need to add this to your Vercel project so it is available when your serverless functions run in production, and optionally create a .env.local file for local development. Go to your Vercel Dashboard and open your project. Click the Settings tab, then Environment Variables in the left sidebar. Add the following variable: DEXT_API_KEY with the value of your Dext API key, which you can find in Dext Dashboard → Settings → Integrations → API. Set the environment to Production, Preview, and Development so it is available across all deployments. Important: do NOT add the NEXT_PUBLIC_ prefix to DEXT_API_KEY. This is a server-only secret. Adding the NEXT_PUBLIC_ prefix would cause Next.js to embed this value directly in the compiled JavaScript bundle that is sent to every browser visitor — completely exposing your API key. The key should only exist in server-side code within your API routes, which run as Vercel serverless functions invisible to the browser. For local development, create a .env.local file in your project root with the same variable. This file is included in .gitignore by default in V0-generated projects, so it will not be committed to GitHub. When you run npm run dev, Next.js automatically loads variables from .env.local, making your local API routes work the same way as production. After adding the environment variable in Vercel, you must trigger a redeployment for the change to take effect. The easiest way is to push any small commit to your GitHub repository — Vercel auto-deploys on every push. Alternatively, in your Vercel project dashboard, go to Deployments, find your latest deployment, click the three-dot menu, and select Redeploy.

.env.local
1# .env.local (for local development only do NOT commit to git)
2DEXT_API_KEY=your_dext_api_key_here

Pro tip: To verify the environment variable is loading correctly, add a temporary console.log at the top of your API route: console.log('Dext key loaded:', !!process.env.DEXT_API_KEY). Check Vercel Function logs to see 'true'. Remove the log after confirming.

Expected result: Vercel Dashboard shows DEXT_API_KEY saved under Environment Variables. After redeployment, your API routes can access process.env.DEXT_API_KEY without the value being undefined.

5

Test the Full Receipt Processing Flow

Test the complete flow from receipt upload to extracted data display. Start with a clear, well-lit receipt photograph or a simple PDF invoice — Dext's OCR accuracy depends on image quality, so using a sharp, properly oriented image gives the most predictable results for initial testing. Navigate to your deployed V0 app and use the file upload UI to submit a test receipt. After clicking upload, your app should show a loading state while the file travels through your Next.js API route to Dext. If the upload succeeds, your API route returns an item ID which the UI can use to poll for the result. Open Vercel Dashboard → your project → Functions → View Logs in real time as you upload. You should see the POST request to /api/dext/upload appear in the logs. If the upload fails, the log will show the HTTP status code and error message returned from Dext's API. Common issues at this stage are authentication errors (wrong API key) or file format issues (Dext rejects certain HEIC or TIFF formats from iPhone cameras — convert to JPEG first). After a successful upload, wait 30-90 seconds and refresh the expense table. The item should appear with status 'processing' initially, then transition to 'completed' with extracted fields like supplier name, date, and total amount populated. If the status remains 'processing' for more than 5 minutes, check Dext's own dashboard to see if there is a processing error on their end. For complex receipt management workflows with approval chains, budget tracking, or multi-currency handling, RapidDev's team can help architect the integration and connect extracted Dext data to your accounting software or database schema.

V0 Prompt

Add a status indicator to the expense table that shows a yellow 'Processing' badge for items with status 'processing' and a green 'Ready' badge for items with status 'completed'. Auto-refresh the table every 15 seconds. Show a toast notification when a processing item transitions to completed.

Paste this in V0 chat

Pro tip: Dext's sandbox or test environment (if available on your plan) lets you submit test documents without consuming your production processing quota. Check your Dext account settings for a test API key.

Expected result: Uploading a receipt JPEG successfully processes through the API route to Dext, appears in the expense table with a Processing status, and updates to show extracted supplier name, date, and amount within 1-2 minutes.

Common use cases

Employee Expense Submission Portal

A startup builds an internal expense portal where employees photograph receipts on mobile and upload them directly from the browser. The V0-generated interface shows a drag-and-drop upload area and a running list of submitted expenses with processing status. Once Dext extracts the data, the portal displays line items for manager review and approval.

V0 Prompt

Create an expense submission page with a file upload area that accepts images and PDFs, a form field for the employee name and expense category, and a table below showing submitted expenses with columns for Date, Supplier, Amount, and Status. The upload button should POST to /api/dext/upload.

Copy this prompt to try it in V0

Client Accounting Dashboard

An accounting firm builds a client-facing portal where clients upload invoices and receipts. The dashboard fetches all processed items from Dext via the API and displays them in a categorized list with extraction confidence scores. Clients can flag items that need correction before they are synced to their accounting software.

V0 Prompt

Build a receipt processing dashboard that shows a grid of expense cards. Each card displays a receipt thumbnail, extracted supplier name, date, total amount with currency, and a category badge. Add a filter bar at the top for date range and category. Fetch data from /api/dext/items.

Copy this prompt to try it in V0

Automated Invoice Processing for SaaS

A SaaS product integrates Dext to let users forward supplier invoices into their account by uploading PDFs. The V0-generated upload flow accepts PDF files, submits them to Dext via the API route, and shows a processing animation while Dext extracts the invoice details. Once complete, the extracted data populates a review form that the user can confirm before saving to their database.

V0 Prompt

Create an invoice upload flow with three steps: Step 1 shows a PDF drop zone with a file browser button. Step 2 shows a loading animation with the message 'Extracting invoice data...'. Step 3 shows a pre-filled form with fields for Supplier, Invoice Number, Date, Amount, and Tax. Include Back and Confirm buttons. Call /api/dext/upload on Step 1 and /api/dext/items/{id} to populate Step 3.

Copy this prompt to try it in V0

Troubleshooting

API route returns 401 Unauthorized when uploading to Dext

Cause: The DEXT_API_KEY environment variable is missing, incorrectly named, or the key has been revoked in the Dext Dashboard.

Solution: Go to Vercel Dashboard → Settings → Environment Variables and verify DEXT_API_KEY exists and matches your key from Dext Dashboard → Settings → Integrations → API. After updating the variable, trigger a redeployment. For local testing, check your .env.local file.

File upload fails with 'Failed to parse multipart form data' or the file arrives at the API route as null

Cause: The browser-side fetch call is setting a manual Content-Type header like 'multipart/form-data' without the required boundary parameter, which prevents the server from parsing the body. Or the frontend is sending JSON instead of FormData.

Solution: Remove any manual Content-Type header from the fetch call when sending FormData. The browser automatically sets 'multipart/form-data; boundary=...' when you pass a FormData object as the body — manually setting it strips the boundary and breaks parsing.

typescript
1// WRONG — manually setting Content-Type breaks multipart parsing
2fetch('/api/dext/upload', {
3 method: 'POST',
4 headers: { 'Content-Type': 'multipart/form-data' },
5 body: formData,
6});
7
8// CORRECT — omit Content-Type, let browser set it with boundary
9fetch('/api/dext/upload', {
10 method: 'POST',
11 body: formData,
12});

Uploaded items appear in Dext Dashboard but extracted fields (supplier_name, total) are always empty

Cause: You are reading item data immediately after upload before Dext has finished OCR processing. The item exists in Dext's system with status 'processing' but the extraction pipeline has not completed yet.

Solution: Check the status field on each item before displaying extracted data. Only render supplier_name, total, and other extracted fields when status is 'completed'. Implement polling in your frontend — fetch /api/dext/items every 15-30 seconds until the target item's status changes from 'processing' to 'completed'.

File upload works locally but returns 413 Payload Too Large on Vercel

Cause: Vercel's serverless functions have a 4.5MB request body limit. Large receipt photos from modern smartphone cameras can exceed this limit.

Solution: Add client-side image compression before upload. Use the browser-image-compression npm package to resize images to a maximum of 1024px and compress to under 1MB before appending to FormData. PDF files may need to be processed differently — consider limiting PDF size to 4MB on the client with a file size check.

typescript
1// Install: npm install browser-image-compression
2import imageCompression from 'browser-image-compression';
3
4const options = { maxSizeMB: 1, maxWidthOrHeight: 1024 };
5const compressedFile = await imageCompression(originalFile, options);
6formData.append('file', compressedFile);

Best practices

  • Always validate file type and size on the client before upload to give users immediate feedback and prevent unnecessary API calls.
  • Store DEXT_API_KEY without any NEXT_PUBLIC_ prefix — this is a server-only secret that must never appear in browser JavaScript.
  • Implement polling with exponential backoff rather than fixed intervals — start at 10 seconds, double to 20, 40, then cap at 60 seconds to reduce API load.
  • Handle the Dext 'processing' status gracefully in the UI — show a loading skeleton or spinner for items that are not yet complete rather than blank fields.
  • Log the Dext item ID immediately after upload so you can debug specific documents that fail to extract correctly using the Dext Dashboard's item view.
  • Compress and resize receipt images on the client before upload to stay within Vercel's 4.5MB serverless body limit and speed up Dext processing.
  • Never trust client-reported file types — always validate MIME type server-side in the API route before forwarding to Dext.
  • For multi-user apps, associate each Dext item ID with the uploading user in your own database so you can scope the items list to the current user.

Alternatives

Frequently asked questions

Does Dext have a public API I can use with V0?

Yes, Dext provides a REST API for submitting documents and retrieving extracted data. API access is available on paid Dext plans. You can find your API key in the Dext Dashboard under Settings → Integrations → API. The API supports document upload, item retrieval, and webhook notifications for processing completion.

How long does Dext take to extract data from a receipt?

Dext typically processes receipts and invoices within 30 seconds to 2 minutes for clear images. Complex multi-page PDFs or low-quality photographs may take longer. Dext does not guarantee a specific processing time — always build your integration around polling the item status rather than assuming data will be available immediately after upload.

Can V0 generate code to connect Dext with QuickBooks automatically?

V0 can generate the Dext API integration code and the QuickBooks API integration code as separate API routes. However, you would need to connect them in your own Next.js server action or API route — fetch the extracted data from Dext and then POST it to QuickBooks as an expense or bill. V0 can scaffold this pipeline if you describe the complete flow in your prompt.

What file formats does Dext accept?

Dext accepts JPEG, PNG, GIF, TIFF, BMP, and PDF files. The most reliable formats for OCR accuracy are JPEG and PDF. Note that HEIC files from iPhone cameras are not always supported — prompt users to enable JPEG capture in their iPhone Camera settings, or convert HEIC to JPEG client-side before upload.

How do I handle multiple users submitting receipts without mixing up their documents?

Dext organizes documents by inboxes or client accounts depending on your plan type. For a multi-user app, create a separate Dext client or inbox for each user and pass the appropriate inbox identifier in your API request. Alternatively, store the Dext item ID alongside the user ID in your own database immediately after upload, then filter the items list by user when displaying data.

Why does my Vercel deployment fail to upload files even though local development works?

The most common cause is that your .env.local file works locally but the DEXT_API_KEY variable was not added to Vercel's Environment Variables. Vercel does not read .env.local files during deployment — all production environment variables must be added through Vercel Dashboard → Settings → Environment Variables. After adding the variable, trigger a redeployment.

Can I use webhooks instead of polling for Dext processing completion?

Dext supports webhook notifications that fire when an item's processing status changes to completed. Configure the webhook URL in Dext Dashboard → Settings → Integrations → Webhooks, pointing to a new API route in your V0 app at something like /api/dext/webhook. This is more efficient than polling for high-volume applications, though polling is simpler to implement for getting started.

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.