How can we help?
Answers to the questions we hear most, and a direct line to the team for bugs, ideas and enterprise enquiries.
Technology & privacy
Where are my documents processed?
On the device of the person using your page, inside their browser. PDF and DOCX files are read by a JavaScript engine in the SDK. Photos of ID cards, passports and licences are read by an OCR engine (Tesseract compiled to WebAssembly) that runs in a Web Worker on the same device. The files are never sent to a Qovox server, or to any other server.
What does travel over the network, then?
Three things only: (1) a small licence check that sends your licence key, the domain of the page and the SDK version, and receives a signed token; (2) the engine files (the SDK, the OCR engine and the English and Arabic language models, about 12 MB), downloaded once from the address you host them on and then cached by the browser; (3) whatever your own page does. No document bytes, no extracted text and no field values are included in any request.
Is it really private? What about GDPR and HIPAA?
Because document bytes never leave the device, there is no transfer to a processor and nothing for us to store or leak. That removes most of the data-protection work that cloud OCR creates. It does not make your product compliant by itself: you remain responsible for how your page uses the extracted data once it has it. We say “built for GDPR and HIPAA workflows”, not “certified”, and this is not legal advice.
What technology does the OCR use?
Tesseract 5 (LSTM models, English and Arabic) compiled to WebAssembly, plus our own layers on top: page detection and flattening, shadow removal and binarisation, a digit reader for numbers, field-by-field re-reads, and validation (the 14-digit Egyptian national number structure, ICAO check digits for passports). We host the engine files ourselves, so nothing is pulled from a public CDN.
Does it work offline?
Yes, after the first load. Parsing and OCR need no connection. The licence is checked online when your page starts; if the connection is down, the SDK keeps working from a saved, signed token for the offline grace period of your plan (24, 72 or 168 hours) and then stops until it can check again. A page opened for the very first time with no connection cannot start, because there is no saved licence yet.
Which browsers are supported?
Current Chrome, Edge, Safari and Firefox (anything with WebAssembly, Web Workers and DecompressionStream). Verifying a saved licence while offline needs Ed25519 in WebCrypto (Chrome 137+, Safari 17+, Firefox 129+); on older browsers the SDK works online only.
How accurate is it?
Honestly: it depends on the field and the photo. Passport MRZ fields are checked against the ICAO check digits and are the most reliable; the Egyptian national number is validated structurally (century, real date, governorate, gender digit) and read with a dedicated digit reader. Arabic names and especially addresses are harder and are marked needsReview when the engine is not sure. We publish measured numbers only from tests on real documents; figures from synthetic test data are not accuracy claims. If you need a number for your use case, run our field-test page on your own documents.
Licensing & plans
Which plans are there?
- Starter, €890 / year: 1 domain, up to 50,000 scans a year, the document parser (PDF and DOCX), 24-hour offline grace.
- Professional, €1,500 / year (recommended): 2 domains (for example staging and production), unlimited scans, document parser and ID / passport / licence OCR, 72-hour offline grace, priority support.
- Enterprise, from €3,000 / year: multi-domain deployment, custom document templates, custom SLA, 168-hour offline grace, white-label / custom EULA and air-gapped options by agreement.
All prices are per year, excluding VAT, invoiced upfront.
What is domain locking?
A licence key is valid only for the domains listed on it (plus localhost for development). The licence server compares the browser-supplied Origin of every check with that list and refuses anything else, so a key copied from your page source does not work on someone else’s site.
How does the licence check work?
The SDK sends the key; the server answers with a short-lived token signed with an Ed25519 key. The token carries your plan, the modules you may use (pdf_parser, id_ocr), the domain and the offline grace period. The SDK verifies the signature locally with the server’s public key, checks the domain, and refuses a token that has been edited or copied to another site. Modules are enforced from the token, so a plan without id_ocr cannot call parser.id.
What is the offline grace period?
How long the SDK keeps working from its last verified token when it cannot reach the licence server: 24 hours on Starter, 72 hours on Professional, 168 hours (7 days) on Enterprise. The number is inside the signed token, so it cannot be extended on the device. Moving the device clock backwards is detected and requires a fresh online check.
What happens when a subscription ends?
The next licence check is refused (LICENSE_EXPIRED) and init() fails, so parsing stops. Between the last successful check and the end of the grace period the SDK may still run from its saved token; after that it stops. Renewing restores access at the next check, with no change on your side.
Is the scan limit enforced?
The 50,000 scans a year on Starter is a contractual allowance. Because documents are processed on the device, we do not receive scan counts and do not meter them on our server. Professional and Enterprise are unlimited.
Can I try it before buying?
Yes. Create an account and use the free developer plan (1 key, 2 domains, for building and testing). Move to an annual licence when you go live.
How do I pay?
Annual licences are invoiced upfront and paid by bank transfer. Choose a plan on the pricing page and send the request; we reply with the invoice and activate the plan when it is paid. Card checkout will be added later.
Integration & usage
How do I add Qovox to my site?
Download the files from your dashboard, put them on your own domain, and load the script. The identity module and the OCR engine are loaded from the same folder only when you first use them.
<script src="/qovox-parser.min.js"></script>
<script>
const parser = await QovoxParser.init({ licenseKey: 'QVX-XXXX-XXXX-XXXX-XXXX' });
const doc = await parser.parse(file); // File from <input type="file">
console.log(doc.markdown, doc.blocks, doc.text);
</script>Keep this layout: qovox-parser.min.js, qovox-parser.id.min.js and a tesseract/ folder together. With a bundler: import { QovoxParser } from 'qovox-parser'. Initialise once per page load, not per file.
Which PDFs can it read?
PDFs that contain a text layer (most documents created by software) and DOCX files; the output is Markdown, plain text and structured blocks. Scanned, image-only PDFs have no text layer; render the pages as images and use the OCR functions instead. Encrypted PDFs are not supported, and the stream filters FlateDecode, ASCIIHexDecode and ASCII85Decode are handled (LZWDecode and predictors are not yet).
How do I read a passport?
parser.id.passport(photo) crops the machine-readable zone, reads it with a restricted alphabet (A–Z, 0–9, <), and verifies every ICAO 9303 check digit (document number, birth date, expiry, personal number, composite) for TD1, TD2 and TD3 documents. Characters the OCR confused (O/0, I/1, S/5, B/8, Z/2) are repaired only when exactly one explanation satisfies the check digits; anything that cannot be verified comes back with needsReview and valid: false. You can also pass text you recognised yourself to parser.id.parseMrz().
How do I read an Egyptian national ID?
parser.id.scan(file, { side: 'front' | 'back' }) returns the name, address, national number and the fields decoded from it (birth date, governorate, gender); the back gives job, marital status, religion and dates. parser.id.scanSides(front, back) merges both sides into one record and cross-checks the number printed on each. The 14-digit number is validated structurally; there is no published check digit for it, so a number that fails validation is flagged rather than guessed.
const front = await parser.id.scan(file, { country: 'EG', docType: 'national_id', side: 'front' });
const record = await parser.id.scanSides(frontFile, backFile); // merged and cross-checked
const pass = await parser.id.passport(photo); // MRZ, ICAO check digits
const mrz = await parser.id.parseMrz(textYouOcrdYourself);What about driving licences?
Driving licences are supported through parser.id.scan(file, { docType: 'drivers_license' }), which reads the licence number, name and the dates it can find. This is the least mature document type: Egyptian licences exist in several layouts and we are tuning field templates against real samples. If you depend on licences, use the field-test page on your own samples first, and show the extracted values for confirmation.
Which errors should I handle?
All errors are QovoxError with a stable code: LICENSE_INVALID, LICENSE_EXPIRED, LICENSE_REVOKED, DOMAIN_NOT_ALLOWED, LICENSE_OFFLINE_EXPIRED, LICENSE_TAMPERED, CLOCK_TAMPERED, MODULE_NOT_LICENSED, NETWORK_ERROR; parsing errors are ParseError (UNSUPPORTED, CORRUPT, TOO_LARGE, ...). Show a friendly message for the licence codes and let the user retry.
Does it work with React, Vue or Angular?
Yes, it is plain JavaScript. Create the parser once (for example in a module or a context), reuse it for every file, and call parser.dispose() when the app is torn down.
Security & CSP
Which Content-Security-Policy do I need?
The OCR engine runs WebAssembly in a Web Worker created from a blob: URL, and its WASM core is embedded as a data: URI. A policy that works:
default-src 'self';
script-src 'self' 'wasm-unsafe-eval';
worker-src 'self' blob:;
connect-src 'self' data: blob: https://qovox-license-api.qovox222.workers.dev;
img-src 'self' data: blob:;
style-src 'self' 'unsafe-inline'Nothing needs unsafe-eval and no third-party host is required. data: in connect-src only allows reading the embedded WASM; it does not open a connection anywhere.
Can I host everything myself?
Yes, that is the intended setup. The SDK, the identity module, the OCR engine (tesseract.min.js, worker.min.js, the WASM core) and the English and Arabic language models are plain static files. Set engine: { assetsUrl: '/vendor/tesseract/', requireLocal: true } and the SDK refuses to start the engine unless it can load it from your folder, so it can never fall back to a public CDN.
Can it run in an air-gapped environment?
The document and OCR functions need no network at all. The licence check needs one, so for fully disconnected sites we issue an offline signed licence (an ECDSA P-256 token bound to your domain and modules, with the validity you agree on) as part of an Enterprise agreement. Contact us to arrange it.
How do I verify the files I deploy?
Each build ships a BUILD.json with the version and an SRI hash for the script tag, and the OCR folder has a MANIFEST.json with a SHA-384 for every file. Use integrity="sha384-..." on the script and compare the manifest in your pipeline.
Where do I report a security issue?
Write to contact@qovox.ai with “Security” in the subject. Please do not include real personal documents; a description or a synthetic sample is enough.
No question matches. Send us yours with the form.
