Security

Information tool, not tax advice. Last updated 2026-09-06.

At a glance

No broker credentials

We do not connect to brokers, do not ask for trading-account passwords, and hold no credential beyond the email address you sign in with.

Passwordless sign-in

Sign-in is a one-time code emailed to you. There is no password to phish or reuse. An idle session signs out after 8 hours, with a warning 15 minutes before.

Stripe-handled payments

Card numbers and bank details never reach our servers. Stripe collects and stores them, and we hold only the resulting customer and subscription identifiers.

Per-user isolation

Data is separated per account in two different ways depending on where the request comes from. The section below sets out exactly which guarantee applies where.

How your data is kept separate

There are two access paths into the database, and they are not protected by the same mechanism. Both matter, and the difference is worth stating plainly.

From your browser. The browser holds only a public key that is constrained by PostgreSQL row-level security. Every table holding your data carries policies that filter rows on the signed-in user id. A query from a browser asking for "all rows in claims" returns only your own rows, and that is enforced by the database rather than by our code.

From our backend. Our API does not use that key. It connects with a service key, which by design bypasses row-level security, and it scopes each query in application code to the user id taken from your verified session token. So on the server side the database is not the last line of defence: our code is. We say so rather than claiming that application code cannot leak across users, because it could, and a claim to the contrary would be untrue.

What we test. Continuous integration runs cross-user isolation tests on every push. They create two users and assert that neither can read, insert or update the other's subscriptions, holdings, dividend inputs, usage counters or webhook events; the build fails if any probe returns a row. The claims table is not yet covered by these probes, and we are not going to describe coverage we do not have.

Application and transport security

  • HTTPS throughout, with HTTP Strict Transport Security on the API.
  • A Content Security Policy on both the site and the API, plus nosniff, framing denied to third parties, Referrer-Policy: strict-origin-when-cross-origin, and a permissions policy that denies camera, microphone and geolocation.
  • Session tokens are verified against the identity provider's signing keys on every request.
  • Rate limits on every sensitive route, backed by Redis so they hold across instances: 10 per minute on form generation, 5 per minute on the contact form, 20 per minute on statement import, 30 to 60 per minute on lookup endpoints.
  • Request logs record method, path, status, duration and a request id. They do not record request bodies, query strings or headers.
  • No privileged secret is included in the browser bundle. The keys that ship to your browser are publishable by design and are constrained server-side.

Sign-in and sessions

Sign-in is a one-time code sent to your email address; there is no password. Codes can be re-requested once a minute. Some emails, such as an email-address change or an invitation, still contain a sign-in link rather than a code.

Your session is stored in your browser's local storage, not in a cookie. After 8 hours without activity the app signs you out and clears it, warning you 15 minutes beforehand. That timer runs in your browser, so signing out on a shared machine is the reliable way to end a session.

Broker credentials

Tax Reclaim stores no broker credentials and connects to no broker APIs. You enter dividend and interest amounts manually, or upload a statement file you exported yourself. No screen-scraping, no API tokens, no OAuth connections to brokerage accounts.

An uploaded statement is parsed on our server, held in memory for the request only, never written to disk, and kept out of our logs by name and by content. See Privacy for the full boundary between what is processed and what is stored.

Error monitoring

We use Sentry to be told when something breaks. Before a report leaves our backend, the request body is dropped on form-generation requests and a fixed list of payment and identity fields is redacted from the request data and extra context.

That is the whole of it. The redaction does not extend to variables captured from the stack of the failing function, to breadcrumbs or to query strings, and the email address is not on the redaction list. We do not claim that personal data is scrubbed at the SDK boundary, because it is not.

Provider attestations

The certifications below are held and published by the providers we use. They are not certifications of Tax Reclaim. Tax Reclaim itself holds no security certification and has not been independently audited. Where a row says "see the provider", we have not verified a certification ourselves and will not restate one second-hand.

LayerProviderProvider's published posture
Frontend hostingRenderSee the provider
Backend hostingRailwaySee the provider
Network layerCloudflareSee the provider
Database & authSupabase (PostgreSQL)SOC 2 Type II
PaymentsStripePCI DSS Level 1, SOC 1 / SOC 2
Transactional emailResendSee the provider
Error monitoringSentrySee the provider
AnalyticsPostHog (EU), Amplitude (EU), Google Analytics, Cloudflare Web AnalyticsSee the provider

The full processor list, including what each one receives, is on the Privacy page. Processing regions are covered there too: we publish a verified region only where we have one.

Reporting a vulnerability

Report security issues to security@taxreclaim.eu. The machine-readable version of this policy is at /.well-known/security.txt.

In scope: taxreclaim.eu, its subdomains, and the API that serves them. Out of scope: our providers' own infrastructure, which should be reported to them directly.

Please do not: run denial-of-service or volumetric tests; send spam or phishing; socially engineer our staff, our providers or our users; attempt physical access; run automated scanning heavy enough to degrade the service for others; or test against an account that is not yours.

If you reach another user's data, stop. Do not read further, copy, store, share or exfiltrate it. Tell us what you saw and how, and delete anything you retrieved. Proving the flaw exists is enough; enumerating it is not.

We will acknowledge your report and tell you what we find. Please give us reasonable time to fix an issue before publishing it. We do not run a bug bounty and do not promise payment. For research carried out in good faith within this policy, we will not pursue legal action over it.

If something goes wrong

If we suffer a personal data breach we assess the risk it poses to the people affected. Where that breach is likely to result in a risk to their rights and freedoms, we notify the competent supervisory authority without undue delay and, where feasible, within 72 hours of becoming aware of it, as Article 33 GDPR requires. Where it is likely to result in a high risk to you, we tell you directly without undue delay, under Article 34.

Not every incident meets those thresholds, so we do not promise a notification for every event. We do commit to applying the test honestly rather than defining our way out of it.

Your data: export and deletion

You can export your data and delete your account from inside the app. The export has known gaps, and the two delete buttons behave differently from one another. Both are documented, without varnish, on the Privacy page, along with the records that survive deletion and how to have them erased.