Build a Review-Ready Invoice-to-JSON API with DEVUP AI, Next.js, and Zod A developer built a review-ready invoice-to-JSON extraction API using Next.js, DEVUP AI's Vision API, JSON Schema, and Zod, with deterministic arithmetic checks that route extractions into either an auto_checked or needs_review state. The pipeline validates image uploads by size and file signature, keeps the API key server-side, and treats both the image and the model's JSON output as untrusted input until application validation passes. The guide explicitly declines to claim any accuracy percentage without a labeled evaluation set. Disclosure: I build DEVUP AI. This guide uses its public API. The example documents are third-party, freely licensed illustrations. No accuracy benchmark or production security certification is implied. An invoice image is not an accounting record. It is a collection of pixels that may contain a seller, dates, line items, taxes, handwritten corrections, and numbers in more than one format. A vision model can propose a structured interpretation. It cannot decide, by itself, that a financial record is safe to book. In this tutorial we will build a review-ready invoice and receipt extraction API with Next.js, DEVUP AI Vision, JSON Schema, Zod, and deterministic arithmetic checks. Its output will be one of two application states: auto checked : the object passed structural and the checks we were able to perform. needs review : data is missing, inconsistent, or otherwise outside the narrow assumptions of this example. Invalid uploads and invalid model responses are rejected with an HTTP error. Neither state means the source document is authentic. We will not claim an accuracy percentage without a labeled evaluation set. The example below is a synthetic receipt. Even a clean document makes the extraction problem more than “read the total”: the app must distinguish item prices, subtotals, taxes, and the final amount. Example receipt by DoubleCritch on Wikimedia Commons https://commons.wikimedia.org/wiki/File:Example Reciept 001.png , CC0 1.0 https://creativecommons.org/publicdomain/zero/1.0/ . The image is an illustration, not a measured DEVUP AI output. A photographed invoice is a different problem: perspective, mixed handwriting and print, layout, and document-specific accounting conventions can all affect extraction. The following real-world sample has personal information removed. Sample invoice by Teemeah on Wikimedia Commons https://commons.wikimedia.org/wiki/File:Sz%C3%A1mla minta.jpg , CC0 1.0 https://creativecommons.org/publicdomain/zero/1.0/ . This is not an Algerian invoice or a claim about extraction quality. DEVUP AI exposes image understanding through the public Chat Completions Vision API https://docs.devupai.com/docs/vision . An image can be supplied as a publicly reachable URL or a Base64 data URL. For a private uploaded invoice we will use the latter, so the app does not need to publish the document to obtain a URL. Supported image capability and OCR quality vary by model. Structured Outputs https://docs.devupai.com/docs/structured-outputs can request a JSON Schema from a compatible model, but the application must still parse and validate the response . User's image ↓ validate size and file signature Application server ↓ send image and extraction schema DEVUP AI public Vision API ↓ proposed JSON Zod parser + arithmetic checks ↓ Review queue → human approval → your accounting system The browser never receives the DEVUP AI key. The image is untrusted input ; text inside it is data, not an instruction to the app. The model's JSON is also untrusted until it passes application validation. “Auto-checked” means only that the explicit checks in our code passed. Use a current Next.js App Router project running in the Node.js runtime. Install the two small validation dependencies: npx create-next-app@latest invoice-review --ts --app cd invoice-review npm install zod decimal.js npm install -D vitest Add .env.local : DEVUP API KEY=your server side key DEVUP VISION MODEL=an exact model id from the live catalog Choose a model from the live DEVUP AI catalog https://devupai.com/models that explicitly accepts image input and supports the requested structured-output mode. Do not paste a model name from a blog post without checking its current capabilities. Do not prefix the key with NEXT PUBLIC . This guide accepts one JPG, PNG, or WebP image , up to 5 MiB. It does not accept PDF. Multi-page PDFs require a separate, deliberately designed conversion and storage path; do not silently reinterpret a PDF as an image. Amounts are decimal strings , not JavaScript floating-point numbers. This example accepts non-negative amounts with up to four decimal places and one currency code for the entire document. Refunds, credits, discounts, shipping, and mixed-currency invoices must be modeled explicitly before automatic reconciliation can be meaningful. Create lib/invoice.ts : js import { z } from "zod"; const amount = z.string .regex /^ ?:0| 1-9 \d ?:\.\d{1,4} ?$/ ; export const invoiceSchema = z.object { kind: z.enum "invoice", "receipt", "other" , sellerName: z.string .trim .min 1 .nullable , invoiceNumber: z.string .trim .min 1 .nullable , issueDate: z.string .regex /^\d{4}-\d{2}-\d{2}$/ .nullable , currency: z.string .regex /^ A-Z {3}$/ .nullable , items: z.array z.object { description: z.string .trim .min 1 , quantity: amount.nullable , unitPrice: amount.nullable , lineTotal: amount.nullable , } .strict .max 100 , subtotal: amount.nullable , tax: amount.nullable , total: amount.nullable , } .strict ; export type Invoice = z.infer