S2SStmt2Sheet

Data handling, read from the code

What happens to your statement PDF

The PDF leaves your browser, is parsed on a Cloudflare Worker in memory, and is not written to storage. No OCR or AI service sees it unless the deployment has one configured.

The short answer

The PDF leaves your browser. It is sent to /api/convert on this site's Cloudflare Worker, parsed in memory, and returned as rows or an export file. No copy is written to storage. No OCR or AI service is called unless the operator of the deployment has configured one, and then only for a PDF with no text layer.

The rest of this page separates what the code guarantees from what the deployment decides.

The path of one PDF

StepWhat happensWhere the data is
1. You choose the fileapp.js checks the name or MIME type and the 15 MB limit, then sends the file as multipart/form-data to /api/convert. The PDF is not read or parsed in the browser.Browser memory
2. The Worker receives itThe Worker reads the whole file into memory and enforces 15 MB per file and 30 MB per request.Worker memory
3. Text is extractedunpdf 1.9.1, which wraps pdf.js, returns positioned text items: text, x, y, width and height. The library is bundled into the Worker and makes no network call.Worker memory
4. Rows are parsedItems are grouped into lines by vertical position, matched against header labels, assigned to columns by x position, and emitted as rows. Exact duplicates are dropped.Worker memory
5. OCR is consideredIf the extracted text is under 40 non-whitespace characters, the PDF is image-only. With a provider configured, the whole PDF is base64-encoded and sent to it; without one, the request ends with ocr_not_configured.Worker memory, plus the OCR provider only when configured
6. The response is builtJSON rows or an export file, with Cache-Control: no-store. No storage write, queue or webhook receives the statement.Response body
7. You downloadClicking XLSX, CSV, QBO, Xero or Tally sends the parsed rows — not the PDF — to /api/export, which builds the file in memory.Browser to Worker memory
8. After the responseThe request's memory is released. The parsed rows stay in the page until you reload or close the tab.—

Does the file leave my browser?

Yes. The only conversion request is a fetch to /api/convert whose body is a FormData containing the PDF. There is no client-side parser, no service-worker cache and no local-first mode. If you need the statement to stay on your own machine, this converter is the wrong tool.

The preview does not change that: the table is built from the JSON the Worker returns. A second request happens only when you click a download button, and it carries the parsed rows rather than the PDF.

Third parties

WhoReceives the PDF?WhenWhat governs it
CloudflareYes, in transit and in Worker memoryEvery conversionCloudflare hosts the site and runs the Worker. Its privacy policy covers request metadata such as IP address, URL and user agent. Checked 2026-10-30.
OCR providerYes — the full PDF as base64 inside a JSON bodyOnly when OCR_PROVIDER_URL and OCR_API_KEY are both set, and only for an image-only PDFThe provider is chosen by the site operator and is not named in the code. Its own terms and retention apply. The code sends one request per conversion and has no delete call.
DodoNoOnly when you open a checkout linkThe payment page. It never receives statement data, and this site never receives card details.
AI or analytics vendorsNoNeverThere is no AI model or third-party analytics script in the code. app.js is the only script and it is first-party.
unpdf/pdf.js and the XLSX writer are code libraries bundled into the Worker, not services. They do not transmit data.

What is kept, where, and for how long

DataWhere it existsHow longContains the statement?
PDF bytesBrowser memory (the selected file) and Worker memoryUntil the page is reloaded on your side; for the duration of the request on the serverYes
Extracted rowsWorker memory during the request; afterwards in a JavaScript variable and the preview table in your browserUntil you reload or close the tabYes
Exported fileYour download folderUntil you delete itYes
Trial cookie s2s_trialYour browser, for this domain30 days, renewed each time a new count is writtenNo — page count and a signature only
Request metadataCloudflare's edge and logsGoverned by Cloudflare's policy, not by this codeNo request body is logged by the Worker
Account, statement database, object storeDoes not exist——

Deletion timing

There is no deletion schedule because there is no stored copy. When the Worker returns the response, the runtime releases that request's memory. Two things outlive the request: the page-count cookie and whatever Cloudflare records about the request. Neither contains the statement. If an OCR provider is configured, the provider holds its own copy under its own retention policy; this code sends the PDF once and never asks the provider to delete it.

The rows in your browser are gone when you reload or close the tab. A downloaded file is yours and is deleted by you.

What the trial cookie stores

PropertyValue in the code
Names2s_trial
ValueAn integer page count, a dot, and a 64-character HMAC-SHA256 hex signature made with the TRIAL_SECRET Worker secret
FlagsHttpOnly; SameSite=Lax; Path=/; Max-Age=2592000, plus Secure on HTTPS. HttpOnly keeps the value out of page JavaScript.
WrittenAfter a conversion that returns at least one row; the count is the total pages of the uploaded PDFs. An OCR response that returns rows without a page count is counted as one page.
Not writtenWhen the request fails before the page-count check (no rows, parsing failure, OCR missing), on /api/export, or when TRIAL_SECRET is unset
ContainsThe page count only: no statement, transactions, file name, timestamp, user ID or IP address
Lifetime30 days, renewed when a new count is written; cleared when you clear site data for this domain
Server copyNone. The count lives only in your browser

There is no account and no server-side usage record to look up. Clearing site data or using a different browser starts the count again; the page count is the cookie. When API_KEY is set, an API request carrying the exact Authorization: Bearer key is not counted and receives no cookie.

Deployment-dependent behavior

This repository cannot tell you which secrets the production Worker has. These are the switches the code reads:

SettingWhen setWhen unset
TRIAL_SECRETA signed cookie is written and the 5-page limit is enforced; responses include remaining.No cookie is written, no page limit is applied, and remaining is null.
OCR_PROVIDER_URL and OCR_API_KEYImage-only PDFs are sent to that provider, and both values must be present for OCR to run.Image-only PDFs end with ocr_not_configured (422).
API_KEYA request with the exact Authorization: Bearer header bypasses the trial and gets no cookie.Every browser conversion is subject to the trial.
DODO_STARTER_URL, DODO_PRO_URL, DODO_BUSINESS_URL/api/checkout?plan=… redirects to the payment link.The checkout endpoint returns checkout_not_configured (503).

The Worker reports its own state at /api/health. When checked on 2026-10-30, the production deployment returned "ocr": false and "trial": true: image-only PDFs on that deployment stop at ocr_not_configured, and the page-count cookie is enforced. Treat the endpoint as the current source for those two flags. With ocr: false, no OCR provider was receiving statements from that deployment at the checked date.

What the code does not do

  • No account, email capture or dashboard.
  • No statement written to a database, object store, cache or queue in the conversion path.
  • No AI or LLM step. Extraction is pdf.js text plus position and regex rules.
  • No bank connection, ledger posting or transaction categorization.
  • No currency conversion: the QBO export declares USD regardless of the statement's currency.
  • No statement content in the URL, the cookie or a response header.

If you process client statements

Ask the operator which OCR provider is configured before sending scans, because the code is provider-agnostic and will not tell you. Confirm that the provider is acceptable for the client's data, and keep the original statement and the export in your own files, since nothing is retained here to retrieve later. The accountants page covers the review workflow; privacy is the policy.

Questions

Does the PDF leave my device?

Yes. It is uploaded to the conversion endpoint on this site's Cloudflare Worker and parsed there. There is no in-browser parsing mode.

Is my statement used to train a model?

There is no model or training step in the code. The only optional outbound call is to an OCR provider, and only when the deployment has one configured and the PDF has no text layer.

When is my data deleted?

There is no stored copy to delete. The request's memory is released when the response is built, the parsed rows stay in your tab until you reload, and the page-count cookie expires after 30 days or when you clear it.

What exactly is in the trial cookie?

An integer page count and an HMAC signature. No statement, transactions, file name, timestamp or user identifier.

Is there a history of my conversions?

No. There is no account and no server-side usage record; the page count in your browser is the only state kept.