Security & Data Protection

What happens to a payments file you upload, and who is responsible for it

What happens to a file you upload

  1. Scanned — every upload is scanned by ClamAV before it is processed. If the scanner is unavailable, the upload is refused rather than accepted unscanned.
  2. Stored while it is read — in AWS S3 in the EU region we configure (eu-north-1), encrypted at rest (SSE-S3, AES-256).
  3. Read, then deleted — once you confirm which column is which and the rows have been read, the file is deleted from storage. A file whose columns are never confirmed stays until you delete that upload.
  4. Reviewed — a review runs deterministic checks over the rows: the same files always give the same findings. Amounts are never converted between currencies.
  5. Kept in your workspace — the rows, the reviews, their findings and your decisions stay in your workspace until you delete them or ask us to. Nobody outside your workspace can reach them through the API.
  6. Shared only when you choose — a client question link shows the one question, the supplier and the payments it is about, and nothing else about your workspace. It expires after 14 days and can be withdrawn.

GDPR: your role and ours

  • For the personal data in the files you upload — supplier and payee names, with their payments — you are the controller and CodeSaaS AI is your processor. We process it only to provide the service as you use it.
  • The Data Processing Agreement (GDPR Article 28) is part of our Terms and is accepted with them at sign-up: read the Data Processing Agreement. It lists the sub-processors, which are the services named on this page.
  • Files and rows are stored in the EU. How we handle personal data about you as an account holder is in the Privacy Policy.

How findings are produced

The checks are deterministic: plain comparisons over your own rows, not AI. AI writes sentences about findings and never produces or adjusts a figure.

Duplicate Detection

Grouped on vendor + amount + direction. Dates are used only to exclude monthly recurring payments, never to match.

Amounts

Shown in the currency of the file they came from. Files in different currencies are reported as such and never converted.

Responsible AI

AI in EasyFlowData is used exclusively for generating human-readable explanations of calculated financial metrics.

AI receives totals and category names, never individual transactions — enforced in code and held by a test

AI does NOT create, modify, or estimate financial numbers

We do not send your data for training, and have agreed to no such use — see the privacy page for what the providers' own terms cover

Answers from the AI CFO carry a confidence level (high/medium/low)

AI features are available during the trial and after a purchase, and each user has an hourly limit on AI requests

Data Encryption

In Transit

All data transmitted over HTTPS (TLS 1.2+), through Cloudflare. The connection from Cloudflare to our server is encrypted too, and the server accepts connections from Cloudflare only.

At Rest

While an uploaded file is held, it is in AWS S3 in the EU region we configure (eu-north-1), with server-side encryption (SSE-S3, AES-256).

API Keys

Stripe and third-party API keys are stored per workspace and encrypted by the application before they reach the database, never in shared config files.

Authentication

JWT tokens with HMAC-SHA256 signing. CSRF tokens for state-changing operations. HttpOnly secure cookies.

Data Isolation & Access Control

  • Workspace isolation — every user's data is scoped to their workspace ID. Users cannot access other workspaces' data.
  • File access — a stored upload is reached only through its import, which is looked up within your workspace
  • API-level enforcement — all API endpoints validate workspace ownership before returning data
  • Admin separation — admin functions require explicit ADMIN_EMAILS whitelist
  • Stripe keys per workspace — each customer's Stripe API key is stored separately, never shared

Infrastructure Security

  • Hosting — we run the service on a single Hetzner server in Germany, behind Cloudflare. The server’s firewall admits only Cloudflare, and it refuses any TLS connection that Cloudflare did not make
  • Database — MongoDB on the same server, reachable only by the application, never from the internet. Nightly backups go to a separate Cloudflare R2 bucket
  • File Storage — AWS S3, in the EU region we configure (eu-north-1), encrypted at rest
  • Malware scanning — every upload is scanned by ClamAV before it is processed. If the scanner is unavailable, the upload is refused rather than accepted unscanned
  • Rate Limiting — a Redis-backed limit per client address on every API request, with tighter limits on sign-in, registration, password reset, AI requests, and uploads after the trial
  • Webhook Security — Stripe webhooks verified via signature validation (HMAC)
  • Error Monitoring — Sentry, with passwords, tokens, cookies and card numbers scrubbed from error reports and no personal data sent by default

Threat Protection

Cross-Site Request Forgery (CSRF)

Double-submit cookie pattern with per-request CSRF tokens on state-changing endpoints, including every upload path. A small exempt set — sign-in, registration, the OAuth callback, token refresh, the Stripe webhook and health checks — is pinned by name with a reason per entry and guarded by a test.

Cross-Site Scripting (XSS)

Content Security Policy headers, X-XSS-Protection, React's built-in XSS protection

Clickjacking

X-Frame-Options: DENY header on all responses

Brute Force Attacks

Rate limiting on sign-in (10 attempts per minute), registration (5 per 5 minutes), password reset (3 per 5 minutes) and two-factor codes (5 per minute)

Stripe Webhook Bypass

All webhook events verified against Stripe signature before processing

Data Leakage

MongoDB ObjectIds excluded from API responses, workspace scoping on all queries

Data Retention & Deletion

  • Uploaded files (Data Sources) — kept only until their rows have been read: once an import finishes, the file is deleted from storage. If an import is never finished (the columns are never confirmed, or reading fails), the file stays until you delete that upload from Data Sources
  • Large-file and quick-analysis uploads — these are not deleted automatically after processing. To have them removed, email us at the address below
  • Payment rows — the rows read from an upload, and a 10-row preview of the file, stay in your workspace with no automatic expiry. Deleting the upload from Data Sources deletes its rows and the preview
  • Findings — the duplicate-payment list is worked out from your rows each time you open it, so deleting an upload removes its findings from that list. The findings of a review, with their decisions and activity history, are stored with the review; they have no automatic expiry and are not removed when you delete an upload. Alerts, issue history and snapshots are kept the same way. All of these are deleted on request
  • Account deletion — there is no self-service account deletion yet. To have a whole account deleted, email us at the address below
  • Backups — nightly database backups are kept in a separate bucket for disaster recovery, and expire under a retention rule on that bucket
  • To request data deletion: [email protected]

Monitoring & Incident Response

  • Error tracking via Sentry, with credentials scrubbed from reports
  • Health check endpoints for uptime monitoring
  • Users will be notified of any data breach affecting their information

Security Contact

To report a security vulnerability or for security inquiries: [email protected]