Privacy policy

Written against what the system actually stores, which you can verify in the repository's schema.

Last updated 27 August 2026

Pending legal review. This document was drafted to describe how the service actually works and is accurate to the implemented architecture. It has not been reviewed by a qualified lawyer in the operating jurisdiction, and must be before launch — see OWNER_ACTION_REQUIRED.md in the repository.

What we collect

When you make a static QR code

Nothing about its contents. Static codes are generated entirely in your browser — the network name, password, contact details or URL you enter never leave your device. We record an anonymous count that a code was generated, and nothing about what was in it.

When someone scans a dynamic code

The scan is recorded, in deliberately coarse terms:

  • Which code, and when
  • A device class: mobile, tablet, desktop or unknown
  • An operating system family, with no version
  • A two-letter country code, never finer
  • The referring site’s host, never the full URL
  • Any UTM parameters on the short link

We do not record the IP address, the user agent string, a cookie, or any fingerprint. There is no identifier that would let anyone — us, or the code’s owner — recognise the same person scanning twice.

When you have an account

  • Your email address
  • Your codes, their destinations, and the history of destination changes
  • Campaign names and any optional context you add
  • Subscription status and a Stripe customer identifier. Card details are handled entirely by Stripe and never reach our servers

What we do not collect

  • The contents of any static code you generate
  • The IP address of anyone who scans a code
  • Browser fingerprints or cross-site tracking identifiers
  • Location finer than a country
  • Anything that would let a repeat scanner be recognised

Why we collect it

To run the checks you ask for, send the alerts you subscribe to, enforce plan limits, prevent abuse, bill you, and understand which parts of the product are useful. The lawful basis is performance of the contract for account data, and legitimate interests for abuse prevention and product analytics.

How long we keep it

  • Individual scan events: the retention window on your plan, 30 to 730 days
  • Daily scan totals: retained longer, so historical charts survive
  • Rate limiting hashes: 2 days
  • Risk and safety events: 365 days
  • Usage and billing records: up to 400 days, as required for accounting
  • Account data: until you delete your account

These are enforced by a scheduled job, not by policy alone.

Who we share it with

We use a small number of processors, and no others:

  • Supabase — database and authentication
  • Vercel — application hosting
  • Stripe — payments
  • Resend — transactional email

We do not sell personal data and do not share it for advertising. Any aggregate statistics we publish are derived only where the sample is large enough that no individual domain or customer is identifiable.

Your rights

You may access, correct, export or delete your data. Deletion is self-service in your account settings and removes your profile, campaigns and scan analytics. Your dynamic codes are not deleted — they are printed in the world, and disabling them abruptly would strand the people who scan them, so they display a neutral notice instead. For anything else, email privacy@scanlight.org.

Cookies

We set a session cookie when you sign in, and a first-party analytics identifier that distinguishes visits without identifying you. There are no third-party advertising or tracking cookies, which is why there is no consent banner to click through.

Security

Data is encrypted in transit and at rest. Tenant isolation is enforced by row-level security in the database rather than by application code alone, so a bug in a route handler cannot expose another account’s data. See SECURITY.md in the repository.

Contact

privacy@scanlight.org