# Security practices

How ProductLense protects your data: hosting in Germany, encrypted provider credentials, hashed API keys, verified webhooks and read-only billing access.

This page describes the controls that protect your analytics and billing data. To report a vulnerability, see the [security page](/security).

## Hosting

- The API and its databases (ClickHouse for events, Postgres for accounts and billing data) run on servers at Hetzner in Germany.
- Cloudflare sits in front for TLS, DDoS protection, rate limiting at the edge, and caching of `a.js`.
- Backups run nightly to Cloudflare R2.

## Billing provider credentials

- Keys and webhook secrets you paste into a billing connection are encrypted at rest with AES-256-GCM. Each ciphertext is bound to its connection, so it cannot be moved to another row and decrypted there.
- Credentials never appear in logs or error messages; outgoing provider calls redact them.
- ProductLense asks for read access only and never writes to your billing account: it does not create, refund or cancel anything, and never moves money.
- Outgoing provider requests refuse private and internal network addresses, refuse redirects to other hosts or from https to http, and time out after 15 seconds.

## Webhooks

Every provider webhook is verified before its body is decoded: an HMAC signature (Stripe, Paddle, Lemon Squeezy, Razorpay, Shopify, Creem), Standard Webhooks signatures (Polar, Dodo Payments), basic authentication compared in constant time (Chargebee), or re-fetching the sale from the API (Gumroad, whose pings are unsigned). Signed deliveries with a timestamp are rejected after 5 minutes where the provider supports it. Each delivery is processed once, keyed by an event id taken from the signed content.

## API keys

- API keys start with `mk_live_` and are shown once when created. ProductLense stores only a SHA-256 hash.
- Each key belongs to one site and carries scopes (`events`, `payments`); a key without the scope gets `403`.
- Delete a key in the dashboard to revoke it immediately. See [Authentication and API keys](/docs/api/authentication).

## Dashboard sign-in

- Sign-in uses magic links that expire after 15 minutes and work once. Sign-in and verification requests are rate limited per IP address and per email.
- The session cookie is `HttpOnly`, `Secure` and `SameSite=Lax`, and lasts 30 days.
- Roles limit who can manage members, API keys and billing; see [Team and roles](/docs/dashboard/team-and-roles).

## Data minimisation

- No IP addresses are stored; visitor ids are salted daily hashes. See [Cookieless visitor counting](/docs/privacy/cookieless-counting).
- The events database holds no emails; the only person-level value in it is the user id you choose to send with `identify`.
- Raw events are deleted after 90 days.
- Ask and the daily insight run on our own servers. No outside AI model ever sees your data: questions are matched by a small open model (EmbeddingGemma 2) running beside the database, and answers are written from tested templates.
