Privacy
This page describes exactly what happens to a file you send to the FactureCheck validation API, in plain language. It is not a substitute for legal advice, but it is an accurate description of the code that runs this service.
What we never store
- The uploaded invoice file itself (XML or PDF).
- Any field or value from inside the invoice — no seller/buyer names, addresses, amounts, VAT numbers, dates, or line items.
- Any derived copy, cache, or backup of the invoice content.
The uploaded bytes are held in memory only for the duration of the request, sent to the validation engine, and discarded once the response is returned. Nothing about the file's content survives the request.
What we do log
For every /api/v1/validate request, a single log line records:
- A SHA-256 hash of the uploaded bytes — enough to tell whether two uploads were identical, not enough to reconstruct them.
- The rule codes that fired (e.g.
BR-02,PEPPOL-EN16931-R008) — which validation checks passed or failed, never the values that caused them to fire. - A timestamp.
- The detected format (UBL, CII, or hybrid PDF).
That is the complete list. No invoice content is ever written to a log.
Why this matters
Many free online invoice validators are anonymous services with no stated retention policy — you have no way to know whether your invoice (often containing customer names, prices, and VAT numbers) is kept, resold, or used to train something. FactureCheck is built so that it structurally cannot do that: the code path from upload to response never writes invoice content to disk or to logs.
Hosting and retention
The service is operated and hosted within the European Union. The only persistent data store is a small SQLite database holding API keys, waitlist signups, and rate-limit counters — never uploaded files. Rate-limit counters are pruned automatically once their one-hour window has passed.
Issued reports
Every PDF conformance report we issue is registered by the SHA-256 hash of its own bytes, so anyone can later confirm it is genuine at facturecheck.eu/verify — never by the invoice's content, a filename, or your IP address.
Three data contracts — read this carefully
Almost everything above describes one-off validation
(/api/v1/validate, /api/v1/repair,
/api/v1/validate/batch, and the MCP tools): nothing is ever
stored, full stop. Regression monitoring
(/api/v1/watch) is a separate contract with two modes of its
own, and both work differently from one-off validation on purpose:
- One-off validation — the promise above holds completely unchanged. Your file is processed in memory and discarded.
- Watch, skeleton (the default) — you opt in, per
sample, by sending
store_document: truetoPOST /api/v1/watch. What is actually kept is not your document: before storing anything, the service pseudonymizes it — company names, addresses, IBANs, VAT ids, emails and phone numbers are replaced with synthetic, format-valid stand-ins (a fixed test IBAN with a correct check digit, "Sample Company 1", and so on). Amounts, currencies, tax rates, dates and every other rule-relevant field are left untouched, and this is proven, not assumed: both the original and the pseudonymized skeleton are validated, and if the two ever fire a different set of rules the request is refused (HTTP 409) rather than silently stored. What lands in storage therefore holds no personal data belonging to your supplier or customer. Withoutstore_document: true, nothing is stored at all: sendstore_document: falseinstead and only a fingerprint (hash, verdict, fired rule codes) is kept — a ruleset change then marks that entryneeds_resubmitinstead of auto-rechecking it, since there is no document left to revalidate. - Watch, full (opt-in, one flag further) — passing
store_full: trueas well stores the real, original document instead of a skeleton. This is never the default and never implied bystore_document: truealone. The bytes are encrypted at rest (XChaCha20-Poly1305); the encryption key lives only in server configuration, never in this codebase. Each full-mode watch has aretention_daysperiod (90 days by default, 365 at most): once it elapses, the stored document is hard-deleted automatically, and the watch entry stays on as metadata only, markeddocument_purged.
Either way, deleting a watch entry (DELETE /api/v1/watch/{id})
hard-deletes its stored document (skeleton or full) immediately — nothing
about it survives in any table. GET /api/v1/watch/{id}/export
returns everything the service holds about one watch, for answering a
data-subject access request. Nothing about any of this weakens the one-off
promise above — the three are entirely separate code paths, and the vast
majority of usage (a single validate or repair call) never touches storage
at all.
Trois contrats de données différents — à lire attentivement
Tout ce qui précède décrit la validation ponctuelle
(/api/v1/validate, /api/v1/repair,
/api/v1/validate/batch, et les outils MCP) : rien n'est jamais
stocké, un point c'est tout. Le suivi de régression
(/api/v1/watch) est un contrat distinct, avec deux modes, et
tous deux fonctionnent volontairement autrement :
- Validation ponctuelle — la promesse ci-dessus reste entièrement inchangée. Votre fichier est traité en mémoire puis supprimé.
- Suivi, squelette (par défaut) — vous consentez
explicitement, par échantillon, en envoyant
store_document: trueàPOST /api/v1/watch. Ce qui est réellement conservé n'est pas votre document : avant tout stockage, le service le pseudonymise — noms de sociétés, adresses, IBAN, numéros de TVA, emails et téléphones sont remplacés par des valeurs synthétiques, formellement valides. Les montants, devises, taux de taxe, dates et tout autre champ dont dépendent les règles restent intacts, et ceci est prouvé, pas supposé : l'original et le squelette pseudonymisé sont tous deux validés, et si l'ensemble des règles déclenchées diffère, la demande est refusée (HTTP 409) plutôt que stockée silencieusement. Sans ce consentement explicite, rien n'est stocké : envoyezstore_document: falseà la place, et seule une empreinte (hash, verdict, codes de règles déclenchés) est conservée. - Suivi, complet (opt-in, un cran plus loin) — ajouter
store_full: truestocke le document original réel au lieu d'un squelette. Ce n'est jamais le comportement par défaut. Les octets sont chiffrés au repos (XChaCha20-Poly1305) ; la clé de chiffrement ne vit que dans la configuration du serveur. Chaque suivi en mode complet a une périoderetention_days(90 jours par défaut, 365 au maximum) : une fois écoulée, le document stocké est supprimé automatiquement, et l'entrée de suivi reste en tant que métadonnées, marquéedocument_purged.
Dans tous les cas, supprimer une entrée de suivi (DELETE
/api/v1/watch/{id}) supprime immédiatement son document stocké —
rien n'en subsiste dans aucune table. GET
/api/v1/watch/{id}/export renvoie tout ce que le service conserve sur
un suivi donné, pour répondre à une demande d'accès aux données
personnelles. Rien de tout cela n'affaiblit la promesse de la validation
ponctuelle ci-dessus — ce sont trois chemins de code entièrement séparés, et
l'immense majorité de l'usage (un simple appel de validation ou de
réparation) ne touche jamais au stockage.
Repair requests
The same rules apply to /api/v1/repair: the uploaded invoice
is processed in memory and discarded. Repairs are limited to a fixed
whitelist of safe, mechanical fixes; monetary amounts, VAT rates, dates, and
party identities are never altered, and arithmetic rule failures are always
reported for you to fix, never silently changed.
Contact
Questions about this policy can be directed to the service operator.