Security and data handling

your invoices are never stored. This is everything that is.

Invoice data sent to Attestwire is checked in memory and is not stored as an invoice. It is not sent to an AI model or used for training. The sections below explain account data, payments, optional records and the safeguards we provide.

QuestionAnswer
Are invoice payloads stored? No. An optional validation record keeps the findings, not the document.
Are they used for model training? No. There is no model here to train.
Are responses cached? No. Responses carry Cache-Control: no-store.
How are API keys stored? As SHA-256 hashes. The plaintext key is shown once.
What is the infrastructure? Cloudflare Workers, Workers KV and Durable Objects.
Where is data processed? US-operated. Cloudflare routes to the nearest point of presence, for latency. EU-only processing is not contractually guaranteed today.
If there is a breach, when do I hear? Within 48 hours of us becoming aware. It is a term of the DPA.
What does the payment processor see? Paddle handles billing. No invoice payload is sent to Paddle.
Certifications? No SOC 2 and no ISO 27001 today.
Penetration test? No third-party penetration test today.

Each answer links to the section below that supports it. For what the product does and does not do, see product scope and limits; for a GDPR Article 28 processor agreement, the data processing addendum.

What happens to an invoice you send us

Invoice processing is separate from live registration lookups. A VAT check sends the VAT number to EU VIES. A Peppol lookup sends the participant identifier to OpenPeppol’s public service. These checks do not send the invoice itself to those services.

It is processed in memory and never stored. A request to POST /v1/validate or POST /v1/generate is parsed, run through the validation engine, and answered. When the response is written, the payload goes out of scope with the request. There is no database of invoices, no object storage bucket, no queue and no cache holding one.

There is nothing for us to go and look at later, which is also why we cannot help you re-run yesterday's document.

It is never used to train anything. There is no model here to train: the engine is deterministic TypeScript, a rule set compiled from EN 16931. We do not send payloads to any third-party service, AI or otherwise.

Responses carry Cache-Control: no-store. So neither Cloudflare's cache nor an intermediary proxy retains the result of a validation.

What we log. We do not log invoice payloads in normal operation. What we keep is what we need to run the service: a request count per key, and the fact that a call happened. The validation and generation paths log the error class name and nothing else: never the document, never a field value.

The catch-all handler behind every route does the same: it records the error class name and the route, and no stack trace. There is no trace to scrub, because none is written.

One real exception. If our Paddle webhook handler fails, we log the stack trace of that failure. That path carries billing events, never an invoice payload. If that is not good enough for your data, run @attestwire/en16931 in your own process. It makes no network calls at all.

API keys

We do not have your key. The plaintext existed once, in the body of the one HTTP response that created it. What we keep is a SHA-256 hash of it, which is enough to recognise the real key and useless for producing it.

A breach of our key store yields a list of hashes rather than a table of live credentials. Nobody here can read your key back to you, whether in a support thread, by accident, to somebody impersonating you, or under a subpoena, which would yield the same hashes we hold. The same property applies if you lose the key: we can no more help you than we could help an attacker.

The common alternative is to encrypt the keys instead of hashing them and put a reveal button in a dashboard. That is friendlier on one day of a customer's life and worse on every other, because it rebuilds the store that can hand out working credentials to whoever reaches it. We chose to be unable to reveal a key.

This is the constraint the code was written under, not marketing written afterwards. Here is the comment at the top of apps/api/src/keys.js, verbatim:

SECURITY INVARIANT: the plaintext key exists in exactly one place — the HTTP
response to the request that created it. We store SHA-256(key) and nothing
else derived from it, we never log it, and we cannot recover it. A lost key
is re-issued, not recovered.

What to quote instead of your key. Every key has a fingerprint: the first eight hex characters of its hash, which is what our logs record. It identifies which key a conversation is about and cannot authenticate as it, so it is safe in an email or a ticket. Never send the key itself: an email is a poor place to keep a secret, and we have no use for it.

That leaves four cases. The last two differ in what we hold, and therefore in what happens next.

The lost key itself is never recoverable: we store its SHA-256 hash and nothing else, so there is nothing on our side to show you again and every route above ends in a replacement rather than the old key returned. That is the same property that means a breach of our key store hands over a list of hashes, and that no support conversation can leak your credential, because the support side of it has never held one.

Metering is per key, held in a Cloudflare Durable Object, so the count is consistent without a shared database.

The complete list of what we hold about an account, in Cloudflare Workers KV:

IP addresses

POST /v1/keys is open to anyone, so it is rate limited by IP address: five key requests per address per UTC day. To do that we keep a counter named after your IP address, for that day. The key form on our homepage posts to that same endpoint, so requesting a key from this website writes one.

An IP address is personal data under the GDPR, so it is listed here and in DPA section 2 and section 7.

No other route records an IP address. We do not attach one to a validation, to a key record, or to any log line we keep.

Hostile XML

The package ships with zero runtime dependencies, so it carries its own XML reader rather than a general-purpose parser. That is a security decision as much as a size one: a general parser accepts DTDs, entities, notations and mixed content, and an invoice needs none of them. The classic XML attacks are absent rather than mitigated: the features they need were never implemented.

These are properties of the MIT package, so they hold whether you call the hosted API, run the library in your own process, or paste a file into the browser checker. Same reader, same refusals, same error codes.

Infrastructure

Everything runs on Cloudflare Workers: the marketing site, the API, the free tools. We administer no servers: there are no long-lived hosts to patch and no remote access to leave open. Cloudflare terminates TLS; the API is HTTPS-only. State is limited to the KV and Durable Object stores described above.

Administrative access to the Cloudflare and Paddle accounts belongs to one person and is protected by multi-factor authentication.

Payments

Card data never touches us. Checkout and the billing portal are hosted by Paddle; you enter payment details on Paddle's pages, not ours. We hold a Paddle customer identifier and your tier. Nobody here can see a card number, because it never arrives.

Paddle is our merchant of record, not a card gateway we point at. It sells the subscription to you, so it also holds the billing address and any VAT or tax identifier you enter, it works out the sales tax or VAT due, and the invoice for your subscription comes from Paddle. None of that reaches us either.

Paddle never receives an invoice payload. It knows you are a customer; it does not know what you validate.

Where processing happens

Cloudflare Workers executes your request at the point of presence nearest the caller. In practice, a request from Germany is normally handled inside the EU and never leaves it. We do not contractually guarantee that today. Cloudflare chooses the location by proximity to the caller, so we will not print a residency promise we cannot enforce.

Cloudflare offers a product for this, Data Localization Suite, with regional processing controls. Adopting it is on the roadmap, and it is a roadmap item, not a claim. If contractual EU-only processing is a requirement for you, say so at hello@attestwire.com before you integrate; in the meantime the library runs entirely inside your own infrastructure, wherever that is, and sends nothing anywhere.

Attestwire itself is operated from the United States. Where personal data is involved, the transfer basis is covered in the data processing addendum.

Subprocessors

There are two, with different jobs.

WhoWhat forWhat they touchWhere
Cloudflare, Inc. Infrastructure Everything the API receives, for the moments it is being processed. The key record (signup email address, tier, timestamps, past-due flag, subscription status and Paddle identifiers), the email index, Paddle event receipts and rotation pointers, all stored in Workers KV. Per-key request counts and a daily key-request count per IP address, stored in Durable Objects. Global edge network; the PoP nearest the caller.
Paddle.com Market Limited (and its affiliates, including Paddle.com Inc.) Billing only (merchant of record) Your billing email, billing address, any VAT or tax identifier you enter, and payment details — all given to Paddle directly at its checkout. Paddle never receives invoice payloads, and no card number ever reaches our code. United Kingdom and United States (entity depends on buyer location), per Paddle's Buyer Terms.

Nothing else in the stack sends data to a third party: there is no error-tracking service, CRM, marketing automation or advertising network. Two things measure anything, and both are Cloudflare's own. The marketing pages load Cloudflare Web Analytics (static.cloudflareinsights.com/beacon.min.js), which is cookieless, does no cross-site tracking and builds no visitor profiles.

Three of those pages also count a few named button presses in Cloudflare Workers Analytics Engine, first-party, in the account that already serves the page: a name from a closed list and nothing else, with no identifier, address, URL or payload attached, and nothing sent when the browser signals Do Not Track or Global Privacy Control.

Neither runs on the API, neither writes to your browser, and neither sees an invoice payload: privacy describes the beacon and the counters in full. The beacon is the only cross-origin subresource on almost every page. The two exceptions are the pages you pay us on: /pay, the noindex checkout page, and /fix, which sells the one-time Invoice Fix Report.

Both load cdn.paddle.com/paddle/v2/paddle.js so Paddle can open its payment window, and that is described in full too. The site sets no cookies. If we add a subprocessor, DPA customers get 30 days' notice before it starts processing.

Cloudflare and Paddle process data under their own terms. We choose them and describe what they do for us; we do not warrant their conduct, and no statement here is a representation on their behalf.

If something goes wrong, when you hear

Within 48 hours. Under the data processing addendum we notify you without undue delay, and in any case within 48 hours of becoming aware of a personal data breach affecting your data, with what we know at the time. That is a contract term: DPA section 4(f). What there is to breach is on this page: no invoice store, keys as hashes, no card data.

What we do not have

Vendor questionnaires ask for these. The answers are all "no" today, and we state them as such so that you can plan around them.

What stands in for a certificate is the architecture described above: there is no invoice store to breach, keys exist only as hashes, and cards never arrive.

Reporting a vulnerability

Email hello@attestwire.com. The same address is published in /.well-known/security.txt per RFC 9116.

There is no bug bounty and no formal disclosure process: one address that reaches a person. Tell us what you found and how to reproduce it, and we will answer. Two business days is the target. We will not threaten you, and we will credit you if you want to be credited. Please do not run automated scans against the paid endpoints or test with anyone else's invoice data.

Next: what the engine covers. Every boundary is on one page: the syntaxes, the rule sets, the operational ceilings and what we never touch.

Read the exact product limits Security questions

Last updated . If this page changes materially we will say so here rather than silently swapping it.