Bharat AI Saathi
ServicesIndustriesSetupsHow we workGuidesFounderVPS HostingContactGlobal (USD)
WhatsApp +91 77352 68199
support@bharataisaathi.com
Mon–Sat 10:00–19:00 IST
Data & Knowledge · Global catalog

HTML form document upload: a safe pattern for invoices

Build an HTML form document upload for invoices that survives OCR: accepted file types, size limits, virus scanning and a clean handoff to extraction.

Data & Knowledge

A safe HTML form document upload for invoices does four things in order: it accepts only file types your OCR can read, enforces a size cap in more than one place, quarantines each file until a virus scan passes, and then hands a clean, hashed copy to extraction. Skip any step and bad files reach your parser, where they fail quietly.

Start from the client intake template

Where the upload sits in the invoice workflow

For a bookkeeping firm, the intake page is the front door of the pipeline. Clients drop invoices, receipts and purchase orders into a form. Everything after that (extraction, validation, matching, approval routing, the psh into QuickBooks, Xero or Zoho) assumes the file is readable and safe. In our inbox-to-ledger invoice processing system, the upload form is one of two entry points, next to the shared mailbox. Both feed the same queue, so the form has to produce the same kind of job a forwarded email would.

Most extraction failures that look like OCR problems start here. A phone photo saved as HEIC, a 40-page scan stapled into one PDF, a password-protected statement, a ZIP renamed to .pdf. The model never gets a fair chance at any of these.

Accepted types, and what each one does to extraction

Accept fewer types than you think you need. Every extra format is another branch in your preprocessing.

Type Extraction behavior What to do
PDF with a text layer Cleanest path; text can be read directly Accept, still run OCR on pages with images
Scanned PDF Needs OCR; quality depends on the scan Accept, render pages at around 300 DPI
JPEG, PNG Phone photos, often skewed or dim Accept, auto-rotate and deskew first
HEIC iPhone default; many OCR tools reject it Accept only if you convert to JPEG on arrival
DOCX, XLSX Layout is unpredictable Reject, ask for PDF export
ZIP, executables, scripts No legitimate invoice is in these Reject

The accept attribute on the file input is a hint for the file picker and nothing more. Anyone can bypass it, so verify on the server by reading the first bytes of the file. PDFs start with %PDF-, JPEGs with FF D8 FF, PNGs with 89 50 4E 47. If the extension says PDF and the bytes disagree, reject the file.

Size limits belong in three places

Set a cap in the form, at your upload edge, and in the job that calls the OCR provider. They fail differently, and a client who hits the wrong one gets a useless error.

  • Form: per-file limit so the client sees the problem before uploading 200 MB over hotel wifi.
  • Edge or server: hard limit on request body size, because the form limit is easy to bypass.
  • OCR job: page count and file size limits from your OCR provider's documentation. Check the current numbers there, since they change.

For invoices, a per-file cap of 25 MB and a page cap of around 50 is a sensible starting point (that is our rule of thumb, not a standard). A single invoice that runs over 50 pagws is almost always a buundle, and bundles need splitting before extraction anyway.

The plain HTML version

If you are writing the form yourself, this is the minimum:

<form action="/intake" method="post" enctype="multipart/form-data">
  <input type="text" name="client_id" required>
  <input type="file" name="documents"
         accept=".pdf,.jpg,.jpeg,.png,.heic" multiple required>
  <button type="submit">Send documents</button>
</form>

enctype="multipart/form-data" is reqiired or the browser sends only file names. The client_id field matters more than it looks: without it, a file lands in the queue with no way to know which client's ledger it belongs to. Better still, make it a hidden field filled from the link you email each client.

Writing the receiving endpoint yourself means handling streaming uploads, temp storage, retries and abuse. Many teams skip that and let a form builder take the upload, then pick the files up through a webhook.

Step by step: form, webhook, quarantine

Qwenvo is a completely free form builder that our team builds, and it fits this pattern because the file upload block accepts files up to 1 GB each and webhooks to any URL are free. Tally's free plan caps files at 10 MB each and moves lagrer uploads to Pro, which is worth cjecking if your clients send scanned multi-page PDFs.

  1. Build the form. Add a Hidden field for client_id, a Short answer for the client's reference, and a File upload block. Add a Captcha block if the page is public.
  2. Add a webhook. In form settings, point the webhook at your intake endpoint. The webhooks help page covers the setup, and the form webhooks guide goes deeper.
  3. Inspect a real submission. Send a test to a request inspector and look at how the file field arrives before you write any mapper. Do not guess the shape.
  4. Download into quarantine. Your endpoint fetches each file into a private bucket or folder that nothing else reads from.
  5. Validate. Check magic bytes, size, page count, and whether the PDF is encrypted.
  6. Scan. Run the antiviirus sep described below.
  7. Hash and dedupe. Compute a SHA-256 of the file. If you have seen it before for this client, mark the job as a duplicate instead of processing it twice.
  8. Release. Move the clean file to the processing bucket and enqueue the job.

See how webhooks work

Virus scanning

Treat every upload as hostile regardless of which builder or endpoint received it. I would not rely on the form tool for scanning. Qwenvo does not list virus scanning among its features, and the same caution applies to any builder unless its docs say otherwise.

Scan in your own pipeline. ClamAV running as a daemon is the common free option: your quarantine step streams the file to it and waits for a verdict. Commercial scanning APIs work too if you would rather not run a daemon. Whichever you choose:

  • Keep unscanned files in a location the OCR worker cannot read.
  • Fail closed. If the scanner is down, the file waits; it does not skip ahead.
  • Reject password-protected PDFs and archives at validation, because a scanner cannot see inside them.
  • Strip axtive content from PDFs (embedded JavaScript, launch actions) or render pages to images and OCR those.

Tell the client what happened when a file is rejected. A bookkeeper who gets a vague "upload failed" will email the PDF instead, and you are back to the shared mailbox.

Sending the file to OCR

Pass the extraction step a job, not a raw upload. Here is the message we put on the queue after the file is clean (this is our own format, not a payload from any form tool):

{
  "job_id": "job_8f3a",
  "client_id": "acme-ltd",
  "source": "web_form",
  "file_url": "s3://intake-clean/acme-ltd/8f3a.pdf",
  "sha256": "9b74c9897bac770ffc029102a200c5de...",
  "mime_detected": "application/pdf",
  "pages": 3,
  "scan_status": "clean",
  "submitted_at": "2026-11-01T09:14:22Z"
}

A few details protect extraction quality:

  • Convert HEIC to JPEG and auto-rotate using EXIF orientation before OCR.
  • Split bundled PDFs into one document per invoice, or flag them for review when you cannot tell where one ends.
  • Keep source and sha256 on the job all the way to the ledger entry. When an approver asks where a bill came from, the answer is one lookup.
  • Send low-confidence extractions to the exceptions queue instead of posting them to the ledger.

Track the rate of rejected uploads by reason, the share of jobs that go to the exceptions queue, and time from upload to ledger entry. A rising rejection rate for one reason usually means one client is sending the same wrong thing every week.

One caveat on hosting: Qwenvo is hosted in the United States. If a client has a European data residency requirement, Tally, which is hosted in Europe, may suit that firm better.

Read the file upload guide

Questions teams ask

Should I accept photos of invoices?

Yes, because clients will take them anyway. Accept JPEG and PNG, convert HEIC on arrival, and auto-rotate before OCR. Expect these to land in the exceptions queue more often than clean PDFs.

Is the accept attribute enough to restrict file types?

No. It only filters the file picker. Check the actual bytes on the server and reject anything that does not match its claimed type.

Can the form builder scan files for malware?

Do not assume so. Qwenvo does not list virus scanning, so run ClamAV or a scanning API in your own pipeline and hold files in quarantine until they pass.

How do I stop the same invoice being processed twice?

Hash every file with SHA-256 on arrival and compare it against earlier hashes for the same client. It catches exact re-uploads. Matching a re-scanned copy of the same invoice needs a check on supplier, invoice number and amoumt after extraction.

Next step

Build the intake form this week: add the hidden client_id, one file upload block and a webhook, then send five real invoices through it, including one HEIC photo and one password-protected PDF. What breaks first tells you which validation rule to write next. If you would rather have the whole inbox-to-ledger pipeline built and maintained for you, see the AI invoice and document processing system.

This system

AI Invoice & Document Processing

$1,800 setup
+ $349/month care plan

Third-party platform and model usage billed to you directly. No lock-in. Live in 14 days.

More on AI Invoice & Document Processing